diff --git a/changelog.txt b/changelog.txt index 3abcbf9bf..f16222025 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,69 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 11/18/2014 == +Trevius: Mercenaries can now zone once again. + +== 11/17/2014 == +demonstar55: Correct OP_AugmentInfo reply. This fixes RoF display issue with Adventurer's Stone. Still issues with UF/SoF/SoD though. + +== 11/16/2014 == +demonstar55: fix size issue with ControlBoat_Struct and exploit fix in OP_BoardBoat + +Akkadius: Implemented Automatic Database update and versioning system +Akkadius: Created database revision define, this is located in version.h in common #define CURRENT_BINARY_DATABASE_VERSION 9057 + - This revision define will need to be incremented each time a database update is made + - Along with a revision define increment, you will need to update the db_update manifest located in: + - https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + - An entry needs to be made at the bottom of the manifest, the entry is quite simple + - Example: 9057|2014_11_13_spells_new_updates.sql|SHOW COLUMNS FROM `spells_new` LIKE 'disallow_sit'|empty| + - This latest example is checking to see if the spells_new table contains the column 'disallow_sit', if its empty, the update needs to be ran + - More examples of match types below: + # Example: Version|Filename.sql|Query_to_Check_Condition_For_Needed_Update|match type|text to match + # 0 = Database Version + # 1 = Filename.sql + # 2 = Query_to_Check_Condition_For_Needed_Update + # 3 = Match Type - If condition from match type to Value 4 is true, update will flag for needing to be ran + # contains = If query results contains text from 4th value + # match = If query results matches text from 4th value + # missing = If query result is missing text from 4th value + # empty = If the query results in no results + # not_empty = If the query is not empty + # 4 = Text to match + - The manifest contains all database updates 'Required' to be made to the schema, and it will contain a working backport all the way back to SVN - + currently it is tested and backported through the beginning of our Github repo + - On world bootup or standalone run of db_update.pl, users will be prompted with a simple menu that we will expand upon later: + + ============================================================ + EQEmu: Automatic Database Upgrade Check + ============================================================ + Operating System is: MSWin32 + (Windows) MySQL is in system path + Path = C:\Program Files\MariaDB 10.0\bin/mysql + ============================================================ + Binary Database Version: (9057) + Local Database Version: (9057) + + Database up to Date: Continuing World Bootup... + ============================================================ + Retrieving latest database manifest... + URL: https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + Saved: db_update/db_update_manifest.txt + +Database Management Menu (Please Select): + 1) Backup Database - (Saves to Backups folder) + Ideal to perform before performing updates + 2) Backup Database Compressed - (Saves to Backups folder) + Ideal to perform before performing updates + 3) Check for pending Database updates + Stages updates for automatic upgrade... + 0) Exit + +Akkadius: Created db_update.pl, placed in utils/scripts folder, used for the automatic database update routine (Linux/Windows) + - db_update.pl script created db_version table if not created, if old one is present it will remove it +Akkadius: Created db_dumper.pl, placed in utils/scripts folder, used for the automatic database update routine backups and standalone backups (Linux/Windows) +Akkadius: World will now check the db_update.pl script on bootup, if the db_update.pl script is not present, it will fetch it remotely before running - + when db_update.pl is done running, world will continue with bootup + == 11/15/2014 == Uleat(Natedog): A better fix for OP_ShopPlayerBuy - doesn't cause the issues that I introduced Kayen: Implemented NPC Special Ability 41 'Allow To Tank', gives NPC opportunity to take aggro over a client in melee range. diff --git a/common/database.cpp b/common/database.cpp index 599d615a7..12ef93223 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2084,6 +2084,19 @@ bool Database::CheckDatabaseConversions() { #endif + /* Fetch Automatic Database Upgrade Script */ + if (!std::ifstream("db_update.pl")){ + std::cout << "Pulling down automatic database upgrade script...\n" << std::endl; +#ifdef _WIN32 + system("perl -MLWP::UserAgent -e \"require LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->timeout(10); $ua->env_proxy; my $response = $ua->get('https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/db_update.pl'); if ($response->is_success){ open(FILE, '> db_update.pl'); print FILE $response->decoded_content; close(FILE); }\""); +#else + system("wget -O db_update.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/db_update.pl"); +#endif + } + /* Run Automatic Database Upgrade Script */ + + system("perl db_update.pl ran_from_world"); + return true; } diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8e72ccaa8..7c663f18e 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4383,14 +4383,14 @@ typedef struct { struct ControlBoat_Struct { /*000*/ uint32 boatId; // entitylist id of the boat /*004*/ bool TakeControl; // 01 if taking control, 00 if releasing it -/*007*/ // no idea what these last three bytes represent +/*007*/ char unknown[3]; // no idea what these last three bytes represent }; struct AugmentInfo_Struct { /*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[67]; // total packet length 72, all the rest were always 00 +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // the reply has the text here /*072*/ }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 95e0047e8..092101d7d 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -235,6 +235,18 @@ namespace RoF FINISH_ENCODE(); } + ENCODE(OP_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + FINISH_ENCODE(); + } + ENCODE(OP_Barter) { EQApplicationPacket *in = *p; diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 57ff1f91d..878870a81 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -5,6 +5,7 @@ E(OP_AltCurrency) E(OP_AltCurrencySell) E(OP_Animation) E(OP_ApplyPoison) +E(OP_AugmentInfo) E(OP_Barter) E(OP_BazaarSearch) E(OP_BeginCast) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index f3a6209b8..0a62a90bf 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4692,9 +4692,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { /*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 -/*076*/ +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 1d93ad721..54dd33841 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -174,6 +174,18 @@ namespace SoD FINISH_ENCODE(); } + ENCODE(OP_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + FINISH_ENCODE(); + } + ENCODE(OP_Barter) { EQApplicationPacket *in = *p; diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 792c9e5d3..038b42e08 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -3,6 +3,7 @@ E(OP_Action) E(OP_AdventureMerchantSell) E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_AugmentInfo) E(OP_Barter) E(OP_BazaarSearch) E(OP_Buff) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 15e0d5307..47b228cb7 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4173,9 +4173,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { /*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 -/*076*/ +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index a9b8d6e26..629395cd1 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -174,6 +174,18 @@ namespace SoF FINISH_ENCODE(); } + ENCODE(OP_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + FINISH_ENCODE(); + } + ENCODE(OP_BazaarSearch) { if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index f039c2189..132e3dc77 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -3,6 +3,7 @@ E(OP_Action) E(OP_AdventureMerchantSell) E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_AugmentInfo) E(OP_BazaarSearch) E(OP_BecomeTrader) E(OP_Buff) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index a49dc6f25..9c3238f73 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -4027,9 +4027,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { /*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 -/*076*/ +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index a0cd76114..adc4ed6e3 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -226,6 +226,18 @@ namespace Underfoot FINISH_ENCODE(); } + ENCODE(OP_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + FINISH_ENCODE(); + } + ENCODE(OP_Barter) { EQApplicationPacket *in = *p; diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index 38cfb79e3..cfb4d4fe9 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -4,6 +4,7 @@ E(OP_AdventureMerchantSell) E(OP_AltCurrency) E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_AugmentInfo) E(OP_Barter) E(OP_BazaarSearch) E(OP_Buff) diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 86f712d24..45c47875d 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -4272,9 +4272,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { /*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 -/*076*/ +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/version.h b/common/version.h index 432d8157f..caff584d8 100644 --- a/common/version.h +++ b/common/version.h @@ -23,6 +23,14 @@ #define EQEMU_PROTOCOL_VERSION "0.3.10" #define CURRENT_VERSION "1.0.0" + +/* + Everytime a Database SQL is added to Github, + increment CURRENT_BINARY_DATABASE_VERSION number and make sure you update the manifest + Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt +*/ + +#define CURRENT_BINARY_DATABASE_VERSION 9057 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/scripts/db_dumper.pl b/utils/scripts/db_dumper.pl new file mode 100644 index 000000000..cce98fdff --- /dev/null +++ b/utils/scripts/db_dumper.pl @@ -0,0 +1,214 @@ +#!/usr/bin/perl + +############################################################ +#::: Script: DB_Dumper.pl +#::: Purpose: Utility to easily manage database backups and compress. +#::: Export Individual DB Tables... +#::: Export specific databases... +#::: Built for both Windows and Linux +#::: Windows uses WinRar or 7-Zip for compression +#::: Linux uses tar for compression +#::: Author: Akkadius +############################################################ + +$localdrive = "C:"; #::: Where Windows and all Install Programs are... +$linesep = "---------------------------------------"; + +use POSIX qw(strftime); +my $date = strftime "%m-%d-%Y", localtime; +print "\nTodays Date: " . $date . "\n"; + +use Config; +print "Operating System is: $Config{osname}\n"; +if($Config{osname}=~/linux/i){ $OS = "Linux"; } +if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; } + +if(!$ARGV[0]){ + print "\nERROR! Need arguments\n\n"; + print "#::: Help :::#\n"; + print "######################################################\n\n"; + print "Arguments\n"; + print " loc=\"C:\\File Location\" - File path location to backup...\n"; + print " database=\"dbname\" - Manually specify databasename, default is database in eqemu_config.xml\n"; + print " tables=\"table1,table2,table3\" - Manually specify tables, default is to dump all tables from database\n"; + print " compress - Compress Database with 7-ZIP, will fallback to WinRAR depending on what is installed (Must be installed to default program dir)...\n"; + print ' Example: perl DB_Dumper.pl Loc="E:\Backups"' . "\n\n"; + print "######################################################\n"; + exit; +} + +#::: CONFIG VARIABLES - Parsed from eqemu_config.xml + +my $confile = "eqemu_config.xml"; #default +open(F, "<$confile") or die "Unable to open config: $confile - This must be in your EQEmu Server Folder with your XML config\n"; +my $indb = 0; + +while() { + s/\r//g; + if(//i) { $indb = 1; } + next unless($indb == 1); + if(/<\/database>/i) { $indb = 0; last; } + if(/(.*)<\/host>/i) { $host = $1; } + elsif(/(.*)<\/username>/i) { $user = $1; } + elsif(/(.*)<\/password>/i) { $pass = $1; } + elsif(/(.*)<\/db>/i) { $db = $1; } +} + +$Debug = 0; +print "Arguments\n" if $Debug; +$n = 0; +while($ARGV[$n]){ + print $n . ': ' . $ARGV[$n] . "\n" if $Debug; + if($ARGV[$n]=~/compress/i){ + print "Compression SET\n"; + $Compress = 1; + } + if($ARGV[$n]=~/database=/i){ + @DB_NAME = split('=', $ARGV[$n]); + print "Database is " . $DB_NAME[1] . "\n"; + $db = $DB_NAME[1]; + } + if($ARGV[$n]=~/loc=/i){ + @B_LOC = split('=', $ARGV[$n]); + print "Backup Directory: " . $B_LOC[1] . "\n"; + } + if($ARGV[$n]=~/tables=/i){ + @Tables = split('=', $ARGV[$n]); @TList = split(',', $Tables[1]); + foreach my $tables (@TList){ + $t_tables .= $tables . " "; + $t_tables_l .= $tables . "_"; + $t_tables_p .= $tables . "\n"; + } + print "Backing up tables: \n\n############################\n" . $t_tables_p . "############################\n\n"; + } + $n++; +} + +#::: Check for Backup Directory existence, if doesn't exist then create... +if (-d $B_LOC[1]) { + print "Directory currently exists... Adding files to it...\n\n"; +} +elsif($B_LOC[1] ne ""){ + print "Directory does NOT exist! Creating...\n\n"; + mkdir($B_LOC[1]) or die 'Failed to create folder, maybe created the folder manually at "' . $B_LOC[1]. '" ?'; +} +else{ + print "No save location specified... Saving to folder script is running in...\n"; +} +if($B_LOC[1] ne ""){ + if($OS eq "Windows"){ $file_app = "\\"; } + if($OS eq "Linux"){ $file_app = "/"; } +} +else { + $file_app = ""; +} + +if($t_tables ne ""){ + $tables_f_l = substr($t_tables_l, 0, 20) . '...'; + $target_file = '' . $tables_f_l . ' ' . $date . ''; + print "Performing table based backup...\n"; + #::: Backup Database... + print "Backing up Database " . $db . "... \n\n"; + $cmd = 'mysqldump -u' . $user . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' ' . $t_tables . ' > "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql"'; + printcmd($cmd); + system($cmd); +} +else{ #::: Entire DB Backup + $target_file = '' . $db . ' ' . $date . ''; + #::: Backup Database... + print "Backing up Database " . $db . "... \n\n"; + $cmd = 'mysqldump -u' . $user . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' > "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql"'; + printcmd($cmd); + system($cmd); +} + +#::: Get File Size +$fileloc = '' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql'; +$filesize = -s $fileloc; +if($filesize < 1000){ print "\n" . 'Error occurred... exiting...' . "\n\n"; exit; } +print "Backup DONE... DB Backup File Size '" . $filesize . "' (" . get_filesize_str($fileloc) . ")\n\n"; + +#::: WinRar Get, check compression flag +if($Compress == 1){ + if($OS eq "Windows"){ + if(-d $localdrive . "\\Program Files\\7-Zip"){ + print " ::: You have 7-Zip installed as 64 Bit...\n\n"; + $S_ZIP = $localdrive . "\\Program Files\\7-Zip"; + } + elsif(-d $localdrive . "\\Program Files (x86)\\7-Zip"){ + print " ::: You have 7-Zip installed as 32 Bit...\n\n"; + $S_ZIP = $localdrive . "\\Program Files (x86)\\7-Zip"; + } + elsif(-d $localdrive . "\\Program Files (x86)\\WinRAR"){ + print " ::: You have WinRAR installed as 32 Bit...\n\n"; + $WinRar = $localdrive . "\\Program Files (x86)\\WinRAR"; + } + elsif(-d $localdrive . "\\Program Files\\WinRAR"){ + print " ::: You have WinRAR installed as 64 Bit...\n\n"; + $WinRar = $localdrive . "\\Program Files\\WinRAR"; + } + else{ + print "No WinRAR installed... Will not compress...\n"; + } + if($S_ZIP ne ""){ + print "Compressing Database with 7-ZIP... \n\n"; + $cmd = '"' . $S_ZIP . '\\7z" a -t7z -m0=lzma -mx=9 "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.7z" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'del "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".7z"; + } + elsif($WinRar ne ""){ + print "Compressing Database with WinRAR... \n"; + $cmd = '"' . $WinRar . '\\rar" a "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.rar" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'del "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".rar"; + } + } + if($OS eq "Linux"){ + print "Compressing Database with Tarball... \n"; + $cmd = 'tar -zcvf "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.tar.gz" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'rm "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".tar.gz"; + } +} + +#::: Get Final File Location for display +if($B_LOC[1] ne ""){ $final_loc = $B_LOC[1] . '' . $file_app . ""; } +else{ + if($OS eq "Windows"){ + $final_loc = `echo %cd%`; + } + elsif($OS eq "Linux"){ + $final_loc = `pwd`; + } +} + +print "Final file located: " . $final_loc . "" . $final_file . "\n\n"; + +sub printcmd{ + print "--- CMD --- \n" . $_[0] . "\n" . $linesep . "\n\n"; +} + +sub get_filesize_str{ + my $file = shift(); + my $size = (stat($file))[7] || die "stat($file): $!\n"; + if ($size > 1099511627776) { return sprintf("%.2f TiB", $size / 1099511627776); } + elsif ($size > 1073741824) { return sprintf("%.2f GiB", $size / 1073741824); } + elsif ($size > 1048576) { return sprintf("%.2f MiB", $size / 1048576); } + elsif ($size > 1024) { return sprintf("%.2f KiB", $size / 1024); } + else { return "$size byte" . ($size == 1 ? "" : "s"); } +} diff --git a/utils/scripts/db_update.pl b/utils/scripts/db_update.pl new file mode 100644 index 000000000..12e5aeab2 --- /dev/null +++ b/utils/scripts/db_update.pl @@ -0,0 +1,383 @@ +#!/usr/bin/perl + +########################################################### +#::: Automatic Database Upgrade Script +#::: Author: Akkadius +#::: Purpose: To upgrade databases with ease and maintain versioning +########################################################### + +$perl_version = $^V; +$perl_version =~s/v//g; +print "Perl Version is " . $perl_version . "\n"; +if($perl_version > 5.12){ no warnings 'uninitialized'; } +no warnings; + +my $confile = "eqemu_config.xml"; #default +open(F, "<$confile") or die "Unable to open config: $confile\n"; +my $indb = 0; +while() { + s/\r//g; + if(//i) { $indb = 1; } + next unless($indb == 1); + if(/<\/database>/i) { $indb = 0; last; } + if(/(.*)<\/host>/i) { $host = $1; } + elsif(/(.*)<\/username>/i) { $user = $1; } + elsif(/(.*)<\/password>/i) { $pass = $1; } + elsif(/(.*)<\/db>/i) { $db = $1; } +} + +print +"============================================================ + EQEmu: Automatic Database Upgrade Check +============================================================ +"; + +use Config; +print " Operating System is: $Config{osname}\n"; +if($Config{osname}=~/linux/i){ $OS = "Linux"; } +if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; } + +if($OS eq "Windows"){ + $has_mysql_path = `echo %PATH%`; + if($has_mysql_path=~/MySQL|MariaDB/i){ + @mysql = split(';', $has_mysql_path); + foreach my $v (@mysql){ + if($v=~/MySQL|MariaDB/i){ + $path = $v . "/mysql"; + last; + } + } + print " (Windows) MySQL is in system path \n"; + print " Path = " . $path . "\n"; + print "============================================================\n"; + } +} + +#::: Linux Check +if($OS eq "Linux"){ + $path = `which mysql`; + if ($path eq "") { + $path = `which mariadb`; + } + $path =~s/\n//g; + + print " (Linux) MySQL is in system path \n"; + print " Path = " . $path . "\n"; + print "============================================================\n"; +} + +#::: Path not found, error and exit +if($path eq ""){ + print "MySQL path not found, please add the path for automatic database upgrading to continue... \n\n"; + print "Exiting...\n"; + exit; +} + +#::: Create db_update working directory if not created +mkdir('db_update'); + +#::: Check if db_version table exists... +if(trim(GetMySQLResult("SHOW COLUMNS FROM db_version LIKE 'Revision'")) ne ""){ + print GetMySQLResult("DROP TABLE db_version"); + print "Old db_version table present, dropping...\n\n"; +} + +if(GetMySQLResult("SHOW TABLES LIKE 'db_version'") eq ""){ + print GetMySQLResult(" + CREATE TABLE db_version ( + version int(11) DEFAULT '0' + ) ENGINE=InnoDB DEFAULT CHARSET=latin1; + INSERT INTO db_version (version) VALUES ('1000');"); + print "Table 'db_version' does not exists.... Creating...\n\n"; +} + +if($OS eq "Windows"){ @db_version = split(': ', `world db_version`); } +if($OS eq "Linux"){ @db_version = split(': ', `./world db_version`); } + +$bin_db_ver = trim($db_version[1]); +$local_db_ver = trim(GetMySQLResult("SELECT version FROM db_version LIMIT 1")); +print " Binary Database Version: (" . $bin_db_ver . ")\n"; +print " Local Database Version: (" . $local_db_ver . ")\n\n"; + +#::: If World ran this script, and our version is up to date, continue... +if($bin_db_ver == $local_db_ver && $ARGV[0] eq "ran_from_world"){ + print " Database up to Date: Continuing World Bootup...\n"; + print "============================================================\n"; + exit; +} + +if(!$bin_db_ver){ $bin_db_ver = 9100; } + +print "Retrieving latest database manifest...\n"; +GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt"); +# GetRemoteFile("https://dl.dropboxusercontent.com/u/50023467/dl/db_update_manifest.txt", "db_update/db_update_manifest.txt"); + +if($local_db_ver < $bin_db_ver && $ARGV[0] eq "ran_from_world"){ + print "You have missing database updates, type 1 or 2 to backup your database before running them as recommended...\n\n"; + #::: Display Menu + ShowMenuPrompt(); +} +else{ + #::: Most likely ran standalone + print "\n"; + ShowMenuPrompt(); +} + + +sub ShowMenuPrompt { + my %dispatch = ( + 1 => \&database_dump, + 2 => \&database_dump_compress, + 3 => \&Run_Database_Check, + 0 => \&Exit, + ); + + while (1) { + { + local $| = 1; + if(!$menu_show && $ARGV[0] eq "ran_from_world"){ + $menu_show++; + next; + } + print MenuOptions(), '> '; + } + + my $choice = <>; + + $choice =~ s/\A\s+//; + $choice =~ s/\s+\z//; + + if (defined(my $handler = $dispatch{$choice})) { + my $result = $handler->(); + unless (defined $result) { + exit 0; + } + } + else { + if($ARGV[0] ne "ran_from_world"){ + # warn "\n\nInvalid selection\n\n"; + } + } + } +} + +sub MenuOptions { + if(@total_updates){ + $option[3] = "Run pending updates... (" . scalar (@total_updates) . ")"; + } + else{ + $option[3] = "Check for pending Database updates + Stages updates for automatic upgrade..."; + } + +return <new; + $ua->timeout(10); + $ua->env_proxy; + my $response = $ua->get($URL); + + if ($response->is_success){ + open (FILE, '> ' . $Dest_File . ''); + print FILE $response->decoded_content; + close (FILE); + print " URL: " . $URL . "\n"; + print " Saved: " . $Dest_File . " \n"; + } + else { + print "Error, no connection to the internet...\n\n"; + die $response->status_line; + } + } + if($OS eq "Linux"){ + #::: wget -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + $wget = `wget --quiet -O $Dest_File $URL`; + print " URL: " . $URL . "\n"; + print " Saved: " . $Dest_File . " \n"; + if($wget=~/unable to resolve/i){ + print "Error, no connection to the internet...\n\n"; + die; + } + } +} + +#::: Trim Whitespaces +sub trim { + my $string = $_[0]; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} + +#::: Responsible for Database Upgrade Routines +sub Run_Database_Check{ + #::: Run 2 - Running pending updates... + if(defined(@total_updates)){ + @total_updates = sort @total_updates; + foreach my $val (@total_updates){ + $file_name = trim($m_d{$val}[1]); + print "Running Update: " . $val . " - " . $file_name . "\n"; + print GetMySQLResultFromFile("db_update/$file_name"); + print GetMySQLResult("UPDATE db_version SET version = $val WHERE version < $val"); + } + } + + #::: Run 1 - Initial checking of needed updates... + print "Reading manifest...\n\n"; + use Data::Dumper; + open (FILE, "db_update/db_update_manifest.txt"); + while () { + chomp; + $o = $_; + if($o=~/#/i){ next; } + @manifest = split('\|', $o); + $m_d{$manifest[0]} = [@manifest]; + } + + @total_updates = (); + + #::: Iterate through Manifest backwards from binary version down to local version... + for($i = $bin_db_ver; $i > 1000; $i--){ + if(!defined($m_d{$i}[0])){ next; } + + $file_name = trim($m_d{$i}[1]); + $query_check = trim($m_d{$i}[2]); + $match_type = trim($m_d{$i}[3]); + $match_text = trim($m_d{$i}[4]); + + #::: Match type update + if($match_type eq "contains"){ + if(trim(GetMySQLResult($query_check))=~/$match_text/i){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if($match_type eq "missing"){ + if(GetMySQLResult($query_check)=~/$match_text/i){ + print "DB up to date with: " . $i . " '" . $file_name . "' \n"; + next; + } + else{ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + print_match_debug(); + print_break(); + } + if($match_type eq "empty"){ + if(GetMySQLResult($query_check) eq ""){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if($match_type eq "not_empty"){ + if(GetMySQLResult($query_check) ne ""){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + } + print "\n\n"; +} + +sub FetchMissingUpdate{ + $db_update = $_[0]; + $update_file = $_[1]; + if($db_update >= 9000){ + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/git/required/" . $update_file, "db_update/" . $update_file . ""); + } + elsif($db_update >= 5000 && $db_update <= 9000){ + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/svn/" . $update_file, "db_update/" . $update_file . ""); + } +} + +sub print_match_debug{ + if(!$debug){ return; } + print " Match Type: '" . $match_type . "'\n"; + print " Match Text: '" . $match_text . "'\n"; + print " Query Check: '" . $query_check . "'\n"; + print " Result: '" . trim(GetMySQLResult($query_check)) . "'\n"; +} +sub print_break{ + if(!$debug){ return; } + print "\n==============================================\n"; +} diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 3eb185476..4f1de43df 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -1,316 +1,332 @@ -5001, 1_task_system.sql -5002, 2_optional_maxclients.sql -5003, 14_optional_merchantlist.sql -5004, 35_task_stepped.sql -5005, 42_task_min_maxlevel.sql -5006, 55_zone_shutdowndeleay.sql -5007, 68_optional_character_maxexplevel.sql -5008, 103_optional_chat_rules.sql -5009, 104_traps.sql -5010, 106_optional_proc_rules.sql -5011, 120_damageshieldtypes.sql -5012, 125_aggrozone.sql -5013, 127_optional_spell_rules.sql -5014, 129_optional_shared_plat_rule.sql -5015, 131_optional_combat_rules.sql -5016, 133_task_repeatable.sql -5017, 142_deathpeace_and_lifetap_aas.sql -5018, 158_optional_death_exp_loss.sql -5019, 176_melody.sql -5020, 189_character_.sql -5021, 196_trader.sql -5022, 210_undyeme.sql -5023, 222_buyer.sql -5024, 226_account_limiting.sql -5025, 230_spells_table.sql -5026, 235_horses_table.sql -5027, 243_spawn_timers.sql -5028, 247_mail.sql -5029, 249_chatchannels.sql -5030, 250_bot_spell_update.sql -5031, 250_optional_bot_spell_update.sql -5032, 285_optional_bot_spell_update.sql -5033, 292_augslots.sql -5034, 294_merchant_logging.sql -5035, 304_faction_list.sql -5036, 326_aas.sql -5037, 328_bot_management.sql -5038, 328_optional_bot_management.sql -5039, 340_gm_ips.sql -5040, 356_combat.sql -5041, 360_peqzone.sql -5042, 364_ranged_dist_rule.sql -5043, 386_bot_save_raid.sql -5044, 434_optional_rest_state_rules.sql -5045, 447_sof_startzone_rule.sql -5046, 463_altadv_vars.sql -5047, 475_aa_actions.sql -5048, 500_spawn2_optimization.sql -5049, 503_bugs.sql -5050, 518_drakkin_npc_type_features.sql -5051, 524_rule_values_notes.sql -5052, 527_npc_armor_tint.sql -5053, 553_saylink_table.sql -5054, 564_nokeyring.sql -5055, 600_group_leadership.sql -5056, 612_instance_changes.sql -5057, 615_adventure_assassination.sql -5058, 619_Adventure_Recruiter_Flavor.sql -5059, 621_LDoNTraps.sql -5060, 633_ucs.sql -5061, 634_TrapTemplateDefaultValue.sql -5062, 643_BotsTable.sql -5063, 646_archery_penalty_rule.sql -5064, 665_heroic_resists.sql -5065, 667_titles.sql -5066, 687_aa_table_changes.sql -5067, 699_peqzone_rule.sql -5068, 702_aashieldblock_tint_table.sql -5069, 703_peqzone_rule.sql -5070, 704_rules.sql -5071, 710_tint_set_naming.sql -5072, 721_pathing_rules.sql -5073, 730_smart_delay_moving.sql -5074, 731_rule_assist_notarget_self.sql -5075, 732_sacrifice_rules.sql -5076, 745_slow_mitigation.sql -5077, 754_archery_base_damage_rule.sql -5078, 755_sof_altadv_vars_updates.sql -5079, 773_monk_rules.sql -5080, 853_optional_rule_aaexp.sql -5081, 858_optional_rule_ip_limit_by_status.sql -5082, 892_optional_bots_table_mod.sql -5083, 893_optional_bots_table_mod.sql -5084, 898_npc_maxlevel_scalerate.sql -5085, 902_optional_rule_snareflee.sql -5086, 923_spawn2_enabled.sql -5087, 962_hot_zone.sql -5088, 964_reports.sql -5089, 971_veteran_rewards.sql -5090, 977_raid_npc_private_corpses.sql -5091, 979_unique_spawn_by_name.sql -5092, 980_account_ip.sql -5093, 1022_botadventuring.sql -5094, 1027_botactives.sql -5095, 1030_botzoningsupport.sql -5096, 1036_botbuffs.sql -5097, 1038_botpetstatepersists.sql -5098, 1038_grouptablesuniquecolumndefinitions.sql -5099, 1039_botguilds.sql -5100, 1040_DeprecatedBotRaidsSystems.sql -5101, 1057_titles.sql -5102, 1077_botgroups.sql -5103, 1136_spell_globals.sql -5104, 1144_optional_rule_return_nodrop.sql -5105, 1195_account_suspendeduntil.sql -5106, 1259_npc_skill_types.sql -5107, 1280_bot_augs.sql -5108, 1290_optional_exp_loss_rule.sql -5109, 1293_guild_bank.sql -5110, 1379_loginserver_trusted_server.sql -5111, 1392_recipe_learning.sql -5112, 1394_optional_rule_sod_hp_mana_end.sql -5113, 1404_faction_list.sql -5114, 1410_optional_sod_aas_ht_and_loh.sql -5115, 1436_login_server_table_fix.sql -5116, 1446_allowrest_optional.sql -5117, 1446_allowrest_required.sql -5118, 1450_cvs.sql -5119, 1451_guilds.sql -5120, 1498_instance_adventure.sql -5121, 1510_global_instances.sql -5122, 1511_map_path_loading.sql -5123, 1513_zone_points.sql -5124, 1519_zone_primary_key_id.sql -5125, 1542_items_table_cleanup.sql -5126, 1548_nimbuseffect_required.sql -5127, 1562_instanced_spawnconditions.sql -5128, 1586_waypoints_optional.sql -5129, 1610_tradeskill_required.sql -5130, 1618_zone.sql -5131, 1625_optional_rule_class_race_exp_bonus.sql -5132, 1672_optional_rules_respawn_window.sql -5133, 1679_optional_rules_blocked_buffs.sql -5134, 1696_modify_zone_and_object_tables.sql -5135, 1711_account_restricted_aa.sql -5136, 1717_optional_rule_bash_stun_chance.sql -5137, 1718_optional_rules_mod3s.sql -5138, 1719_optional_triggerOnCastAAs.sql -5139, 1720_optional_sql_AAs.sql -5140, 1720_required_sql_AA_effects_update.sql -5141, 1721_optional_sql_drakkin_breath_update.sql -5142, 1721_required_sql_altadv_vars_update.sql -5143, 1723_optional_sql_new_stats_window_rule.sql -5144, 1723_required_sql_corruption.sql -5145, 1736_optional_sql_feral_swipe.sql -5146, 1737_required_sql_rule_and_aa_update.sql -5147, 1746_optional_sql_bot_manaregen.sql -5148, 1747_optional_HoT_zone_and_zonepoints.sql -5149, 1750_optional_sql_reflect_rule.sql -5150, 1753_optional_haste_cap_rule.sql -5151, 1753_required_sql_healing_adept_aa.sql -5152, 1754_required_sql_healing_adept_aa_fix.sql -5153, 1755_required_sql_fear_resist_aas.sql -5154, 1784_optional_corpsedrag_rules.sql -5155, 1786_required_update_to_aas.sql -5156, 1790_required_aa_required_level_cost.sql -5157, 1793_resist_adjust.sql -5158, 1799_optional_rest_regen_endurance_rule.sql -5159, 1802_required_doppelganger.sql -5160, 1803_required_tasks_xpreward_signed.sql -5161, 1804_required_ae_melee_updates.sql -5162, 1809_optional_rules.sql -5163, 1813_required_doppelganger_npcid_change.sql -5164, 1817_optional_npc_archery_bonus_rule.sql -5165, 1823_optional_delay_death.sql -5166, 1847_required_doors_dest_zone_size_32.sql -5167, 1859_optional_item_casts_use_focus_rule.sql -5168, 1884_optional_bot_spells_update.sql -5169, 1885_optional_rules_fv_pvp_expansions.sql -5170, 1889_optional_skill_cap_rule.sql -5171, 1908_required_npc_types_definitions.sql -5172, 1926_optional_stat_cap.sql -5173, 1944_spawn2.sql -5174, 1946_doors.sql -5175, 1960_optional_console_timeout_rule.sql -5176, 1962_optional_guild_creation_window_rules.sql -5177, 1963_optional_rule_live_like_focuses.sql -5178, 1968_optional_enrage_rules.sql -5179, 1972_optional_extradmg_item_cap.sql -5180, 1974_required_bot_spells_update.sql -5181, 1977_underwater.sql -5182, 1998_optional_intoxication_and_looting_rules.sql -5183, 2004_charges_alt_currency.sql -5184, 2015_optional_specialization_training_rule.sql -5185, 2016_optional_rule_bot_aa_expansion.sql -5186, 2023_optional_mysqlcli.sql -5187, 2024_optional_update_crystals.sql -5188, 2024_required_update.sql -5189, 2057_required_discovered_items.sql -5190, 2058_optional_rule_discovered_items.sql -5191, 2062_required_version_changes.sql -5192, 2069_required_pets.sql -5193, 2079_player_speech.sql -5194, 2087_required_bots_hp_and_mana_and_spell_updates.sql -5195, 2098_required_zonepoint_version_changes.sql -5196, 2099_required_discovered_items_account_status.sql -5197, 2104_required_group_roles.sql -5198, 2107_required_bot_stances.sql -5199, 2129_required_lfguild.sql -5200, 2133_required_faction_loot_despawn.sql -5201, 2136_extended_targets.sql -5202, 2142_emotes.sql -5203, 2154_optional_rule_spell_procs_resists_falloff.sql -5204, 2156_optional_charm_break_rule.sql -5205, 2159_optional_defensiveproc_rules.sql -5206, 2164_require_bots_bottimers.sql -5207, 2171_optional_SpecialAttackACBonus_rule.sql -5208, 2176_optional_aa_expansion_SOF_fix.sql -5209, 2176_optional_FrenzyBonus_rule.sql -5210, 2176_required_aa_updates.sql -5211, 2178_required_aa_updates.sql -5212, 2183_optional_bot_xp_rule.sql -5213, 2185_optional_NPCFlurryChacne_rule -5214, 2185_optional_NPCFlurryChacne_rule.sql -5215, 2185_optional_NPCFlurryChance_rule.sql -5216, 2185_required_aa_updates -5217, 2185_required_aa_updates.sql -5218, 2188_optional_miscspelleffect_rules -5219, 2188_optional_miscspelleffect_rules.sql -5220, 2188_required_aa_updates -5221, 2188_required_aa_updates.sql -5222, 2189_optional_taunt_rules -5223, 2189_optional_taunt_rules.sql -5224, 2195_required_sharedplatupdates.sql -5225, 2208_optional_aa_stacking_rule.sql -5226, 2208_optional_EnableSoulAbrasionAA.sql -5227, 2208_required_aa_updates.sql -5228, 2209_optional_additive_bonus_rule.sql -5229, 2213_loot_changes.sql -5230, 2214_faction_list_mod.sql -5231, 2215_required_aa_updates.sql -5232, 2243_optional_char_max_level_rule.sql -5233, 2260_probability.sql -5234, 2262_required_pet_discipline_update.sql -5235, 2264_required_aa_updates.sql -5236, 2268_QueryServ.sql -5237, 2268_required_updates.sql -5238, 2274_optional_rule_iplimitdisconnectall.sql -5239, 2278_optional_rule_targetableswarmpet.sql -5240, 2280_optional_rule_targetableswarmpet-rename.sql -5241, 2283_required_npc_changes.sql -5242, 2299_required_inspectmessage_fields.sql -5243, 2300_optional_loot_changes.sql -5244, 2304_QueryServ.sql -5245, 2340_required_maxbuffslotspet.sql -5246, 2361_QueryServ.sql -5247, 2361_required_qs_rule_values.sql -5248, 2370_required_aa_updates.sql -5249, 2376_required_aa_updates.sql -5250, 2380_optional_merc_data.sql -5251, 2380_optional_merc_merchant_npctypes_update.sql -5252, 2380_optional_merc_rules.sql -5253, 2383_required_group_ismerc.sql -5254, 2428_optional_levelbasedexpmods.sql -5255, 2448_optional_stun_proc_aggro_rule.sql -5256, 2471_required_aa_updates.sql -5257, 2482_required_start_zones.sql -5258, 2504_required_aa_updates.sql -9000, 2013_02_18_Merc_Rules_and_Tables.sql -9001, 2013_02_25_Impr_HT_LT.sql -9002, 2013_03_1_Merc_Rules_and_Equipment.sql -9003, 2013_03_23_Escape_FadingMemories.sql -9004, 2013_04_04_NaturesBounty.sql -9005, 2013_04_08_Salvage.sql -9006, 2013_05_05_Account_Flags.sql -9007, 2013_05_05_Item_Tick.sql -9008, 2013_07_11_NPC_Special_Abilities.sql -9009, 2013_10_12_Merc_Special_Abilities.sql -9010, 2013_10_12_Merc_vwMercNpcTypes.sql -9011, 2013_10_31_Recipe_disabling.sql -9012, 2013_11_07_BaseData.sql -9013, 2013_11_13_Instrument_Singing_Mastery.sql -9014, 2013_11_18_AssistRadius.sql -9015, 2013_12_26_MerchantList_Class_Required.sql -9016, 2014_01_04_SongModCapAAs.sql -9017, 2014_01_08_SpellsNewAdditions.sql -9018, 2014_01_09_PreservePetSize.sql -9019, 2014_01_20_MezMastery.sql -9020, 2014_01_20_Not_Extendable.sql -9021, 2014_01_20_SpellCastingReinforcement.sql -9022, 2014_01_20_Weather.sql -9023, 2014_01_27_CritcalMendAA.sql -9024, 2014_02_02_SpellCriticalsAA.sql -9025, 2014_02_13_Rename_instance_lockout_tables.sql -9026, 2014_02_13_spells_new_update.sql -9027, 2014_02_20_buff_update.sql -9028, 2014_02_26_roambox_update.sql -9029, 2014_02_26_virulentvenomAA.sql -9030, 2014_04_04_PhysicalResist.sql -9031, 2014_04_10_No_Target_With_Hotkey.sql -9032, 2014_04_12_SlowMitigation.sql -9033, 2014_04_18_Suppress_Command_Error.sql -9034, 2014_04_25_spawn_events.sql -9035, 2014_04_27_AISpellEffects.sql -9036, 2014_05_04_SlowMitigationFix.sql -9037, 2014_06_25_AA_Updates..sql -9038, 2014_06_25_AA_Updates.sql -9039, 2014_07_04_AA_Updates.sql -9040, 2014_07_10_npc_spells.sql -9041, 2014_08_02_spells_new.sql -9042, 2014_08_12_NPC_raid_targets.sql -9043, 2014_08_18_spells_new_update.sql -9044, 2014_08_20_merchantlist_probability.sql -9045, 2014_08_23_Complete_QueryServ_Table_Structures.sql -9046, 2014_08_23_player_events_and_player_aa_rate_hourly.sql -9047, 2014_08_24_character_lookup.sql -9048, 2014_09_09_attack_delay.sql -9049, 2014_09_18_tellqueuesclean.sql -9050, 2014_09_20_ban_messages.sql -9051, 2014_10_11_RaidMOTD.sql -9052, 2014_10_13_RaidLeadership.sql -9053, 2014_10_18_group_mentor.sql -9054, 2014_10_19_raid_group_mentor.sql -9055, 2014_10_30_special_abilities_null.sql -9056, 2014_11_08_RaidMembers.sql -9057, 2014_11_13_spells_new_updates.sql \ No newline at end of file +5001|1_task_system.sql +# 5002|2_optional_maxclients.sql +# 5003|14_optional_merchantlist.sql +5004|35_task_stepped.sql +5005|42_task_min_maxlevel.sql +5006|55_zone_shutdowndeleay.sql +# 5007|68_optional_character_maxexplevel.sql +# 5008|103_optional_chat_rules.sql +5009|104_traps.sql +# 5010|106_optional_proc_rules.sql +5011|120_damageshieldtypes.sql +5012|125_aggrozone.sql +# 5013|127_optional_spell_rules.sql +# 5014|129_optional_shared_plat_rule.sql +# 5015|131_optional_combat_rules.sql +5016|133_task_repeatable.sql +5017|142_deathpeace_and_lifetap_aas.sql +# 5018|158_optional_death_exp_loss.sql +5019|176_melody.sql +5020|189_character_.sql +5021|196_trader.sql +5022|210_undyeme.sql +5023|222_buyer.sql +5024|226_account_limiting.sql +5025|230_spells_table.sql +5026|235_horses_table.sql +5027|243_spawn_timers.sql +5028|247_mail.sql +5029|249_chatchannels.sql +5030|250_bot_spell_update.sql +# 5031|250_optional_bot_spell_update.sql +# 5032|285_optional_bot_spell_update.sql +5033|292_augslots.sql +5034|294_merchant_logging.sql +5035|304_faction_list.sql +5036|326_aas.sql +5037|328_bot_management.sql +# 5038|328_optional_bot_management.sql +5039|340_gm_ips.sql +5040|356_combat.sql +5041|360_peqzone.sql +5042|364_ranged_dist_rule.sql +5043|386_bot_save_raid.sql +# 5044|434_optional_rest_state_rules.sql +5045|447_sof_startzone_rule.sql +5046|463_altadv_vars.sql +5047|475_aa_actions.sql +5048|500_spawn2_optimization.sql +5049|503_bugs.sql +5050|518_drakkin_npc_type_features.sql +5051|524_rule_values_notes.sql +5052|527_npc_armor_tint.sql +5053|553_saylink_table.sql +5054|564_nokeyring.sql +5055|600_group_leadership.sql +5056|612_instance_changes.sql +5057|615_adventure_assassination.sql +5058|619_Adventure_Recruiter_Flavor.sql +5059|621_LDoNTraps.sql +5060|633_ucs.sql +5061|634_TrapTemplateDefaultValue.sql +5062|643_BotsTable.sql +5063|646_archery_penalty_rule.sql +5064|665_heroic_resists.sql +5065|667_titles.sql +5066|687_aa_table_changes.sql +5067|699_peqzone_rule.sql +5068|702_aashieldblock_tint_table.sql +5069|703_peqzone_rule.sql +5070|704_rules.sql +5071|710_tint_set_naming.sql +5072|721_pathing_rules.sql +5073|730_smart_delay_moving.sql +5074|731_rule_assist_notarget_self.sql +5075|732_sacrifice_rules.sql +5076|745_slow_mitigation.sql +5077|754_archery_base_damage_rule.sql +5078|755_sof_altadv_vars_updates.sql +5079|773_monk_rules.sql +# 5080|853_optional_rule_aaexp.sql +# 5081|858_optional_rule_ip_limit_by_status.sql +# 5082|892_optional_bots_table_mod.sql +# 5083|893_optional_bots_table_mod.sql +5084|898_npc_maxlevel_scalerate.sql +# 5085|902_optional_rule_snareflee.sql +5086|923_spawn2_enabled.sql +5087|962_hot_zone.sql +5088|964_reports.sql +5089|971_veteran_rewards.sql +5090|977_raid_npc_private_corpses.sql +5091|979_unique_spawn_by_name.sql +5092|980_account_ip.sql +5093|1022_botadventuring.sql +5094|1027_botactives.sql +5095|1030_botzoningsupport.sql +5096|1036_botbuffs.sql +5097|1038_botpetstatepersists.sql +5098|1038_grouptablesuniquecolumndefinitions.sql +5099|1039_botguilds.sql +5100|1040_DeprecatedBotRaidsSystems.sql +5101|1057_titles.sql +5102|1077_botgroups.sql +5103|1136_spell_globals.sql +# 5104|1144_optional_rule_return_nodrop.sql +5105|1195_account_suspendeduntil.sql +5106|1259_npc_skill_types.sql +5107|1280_bot_augs.sql +# 5108|1290_optional_exp_loss_rule.sql +5109|1293_guild_bank.sql +5110|1379_loginserver_trusted_server.sql +5111|1392_recipe_learning.sql +# 5112|1394_optional_rule_sod_hp_mana_end.sql +5113|1404_faction_list.sql +# 5114|1410_optional_sod_aas_ht_and_loh.sql +5115|1436_login_server_table_fix.sql +5116|1446_allowrest_optional.sql +5117|1446_allowrest_required.sql +5118|1450_cvs.sql +5119|1451_guilds.sql +5120|1498_instance_adventure.sql +5121|1510_global_instances.sql +5122|1511_map_path_loading.sql +5123|1513_zone_points.sql +5124|1519_zone_primary_key_id.sql +5125|1542_items_table_cleanup.sql +5126|1548_nimbuseffect_required.sql +5127|1562_instanced_spawnconditions.sql +5128|1586_waypoints_optional.sql +5129|1610_tradeskill_required.sql +5130|1618_zone.sql +# 5131|1625_optional_rule_class_race_exp_bonus.sql +# 5132|1672_optional_rules_respawn_window.sql +# 5133|1679_optional_rules_blocked_buffs.sql +5134|1696_modify_zone_and_object_tables.sql +5135|1711_account_restricted_aa.sql +# 5136|1717_optional_rule_bash_stun_chance.sql +# 5137|1718_optional_rules_mod3s.sql +# 5138|1719_optional_triggerOnCastAAs.sql +# 5139|1720_optional_sql_AAs.sql +5140|1720_required_sql_AA_effects_update.sql +# 5141|1721_optional_sql_drakkin_breath_update.sql +5142|1721_required_sql_altadv_vars_update.sql +# 5143|1723_optional_sql_new_stats_window_rule.sql +5144|1723_required_sql_corruption.sql +# 5145|1736_optional_sql_feral_swipe.sql +5146|1737_required_sql_rule_and_aa_update.sql +# 5147|1746_optional_sql_bot_manaregen.sql +# 5148|1747_optional_HoT_zone_and_zonepoints.sql +# 5149|1750_optional_sql_reflect_rule.sql +# 5150|1753_optional_haste_cap_rule.sql +5151|1753_required_sql_healing_adept_aa.sql +5152|1754_required_sql_healing_adept_aa_fix.sql +5153|1755_required_sql_fear_resist_aas.sql +# 5154|1784_optional_corpsedrag_rules.sql +5155|1786_required_update_to_aas.sql +5156|1790_required_aa_required_level_cost.sql +5157|1793_resist_adjust.sql +# 5158|1799_optional_rest_regen_endurance_rule.sql +5159|1802_required_doppelganger.sql +5160|1803_required_tasks_xpreward_signed.sql +5161|1804_required_ae_melee_updates.sql +# 5162|1809_optional_rules.sql +5163|1813_required_doppelganger_npcid_change.sql +# 5164|1817_optional_npc_archery_bonus_rule.sql +# 5165|1823_optional_delay_death.sql +5166|1847_required_doors_dest_zone_size_32.sql +# 5167|1859_optional_item_casts_use_focus_rule.sql +# 5168|1884_optional_bot_spells_update.sql +# 5169|1885_optional_rules_fv_pvp_expansions.sql +# 5170|1889_optional_skill_cap_rule.sql +5171|1908_required_npc_types_definitions.sql +# 5172|1926_optional_stat_cap.sql +5173|1944_spawn2.sql +5174|1946_doors.sql +# 5175|1960_optional_console_timeout_rule.sql +# 5176|1962_optional_guild_creation_window_rules.sql +# 5177|1963_optional_rule_live_like_focuses.sql +# 5178|1968_optional_enrage_rules.sql +# 5179|1972_optional_extradmg_item_cap.sql +5180|1974_required_bot_spells_update.sql +5181|1977_underwater.sql +# 5182|1998_optional_intoxication_and_looting_rules.sql +5183|2004_charges_alt_currency.sql +# 5184|2015_optional_specialization_training_rule.sql +# 5185|2016_optional_rule_bot_aa_expansion.sql +# 5186|2023_optional_mysqlcli.sql +# 5187|2024_optional_update_crystals.sql +5188|2024_required_update.sql +5189|2057_required_discovered_items.sql +# 5190|2058_optional_rule_discovered_items.sql +5191|2062_required_version_changes.sql +5192|2069_required_pets.sql +5193|2079_player_speech.sql +5194|2087_required_bots_hp_and_mana_and_spell_updates.sql +5195|2098_required_zonepoint_version_changes.sql +5196|2099_required_discovered_items_account_status.sql +5197|2104_required_group_roles.sql +5198|2107_required_bot_stances.sql +5199|2129_required_lfguild.sql +5200|2133_required_faction_loot_despawn.sql +5201|2136_extended_targets.sql +5202|2142_emotes.sql +# 5203|2154_optional_rule_spell_procs_resists_falloff.sql +# 5204|2156_optional_charm_break_rule.sql +# 5205|2159_optional_defensiveproc_rules.sql +5206|2164_require_bots_bottimers.sql +# 5207|2171_optional_SpecialAttackACBonus_rule.sql +# 5208|2176_optional_aa_expansion_SOF_fix.sql +# 5209|2176_optional_FrenzyBonus_rule.sql +5210|2176_required_aa_updates.sql +5211|2178_required_aa_updates.sql +# 5212|2183_optional_bot_xp_rule.sql +# 5213|2185_optional_NPCFlurryChacne_rule +# 5214|2185_optional_NPCFlurryChacne_rule.sql +# 5215|2185_optional_NPCFlurryChance_rule.sql +5216|2185_required_aa_updates +5217|2185_required_aa_updates.sql +# 5218|2188_optional_miscspelleffect_rules +# 5219|2188_optional_miscspelleffect_rules.sql +5220|2188_required_aa_updates +5221|2188_required_aa_updates.sql +# 5222|2189_optional_taunt_rules +# 5223|2189_optional_taunt_rules.sql +5224|2195_required_sharedplatupdates.sql +# 5225|2208_optional_aa_stacking_rule.sql +# 5226|2208_optional_EnableSoulAbrasionAA.sql +5227|2208_required_aa_updates.sql +# 5228|2209_optional_additive_bonus_rule.sql +5229|2213_loot_changes.sql +5230|2214_faction_list_mod.sql +5231|2215_required_aa_updates.sql +# 5232|2243_optional_char_max_level_rule.sql +5233|2260_probability.sql +5234|2262_required_pet_discipline_update.sql +5235|2264_required_aa_updates.sql +5236|2268_QueryServ.sql +5237|2268_required_updates.sql +# 5238|2274_optional_rule_iplimitdisconnectall.sql +# 5239|2278_optional_rule_targetableswarmpet.sql +# 5240|2280_optional_rule_targetableswarmpet-rename.sql +5241|2283_required_npc_changes.sql +5242|2299_required_inspectmessage_fields.sql +# 5243|2300_optional_loot_changes.sql +5244|2304_QueryServ.sql +5245|2340_required_maxbuffslotspet.sql +5246|2361_QueryServ.sql +5247|2361_required_qs_rule_values.sql +5248|2370_required_aa_updates.sql +5249|2376_required_aa_updates.sql +# 5250|2380_optional_merc_data.sql +# 5251|2380_optional_merc_merchant_npctypes_update.sql +# 5252|2380_optional_merc_rules.sql +5253|2383_required_group_ismerc.sql +# 5254|2428_optional_levelbasedexpmods.sql +# 5255|2448_optional_stun_proc_aggro_rule.sql +5256|2471_required_aa_updates.sql +5257|2482_required_start_zones.sql +5258|2504_required_aa_updates.sql +8000|mercs.sql|SHOW TABLES LIKE 'merc_stats'|empty| +9000|2013_02_18_Merc_Rules_and_Tables.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Mercs:ResurrectRadius%'|empty| +9001|2013_02_25_Impr_HT_LT.sql|SHOW TABLES LIKE 'merc_inventory'|empty| +9002|2013_03_1_Merc_Rules_and_Equipment.sql|SHOW TABLES LIKE 'merc_inventory'|empty| +# 9003|2013_03_23_Escape_FadingMemories.sql +9004|2013_04_04_NaturesBounty.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '1230' AND `slot` = '1' AND `effectid` = '313' AND `base1` = '15' AND `base2` = '0'|empty| +9005|2013_04_08_Salvage.sql|SHOW COLUMNS FROM `tradeskill_recipe_entries` LIKE 'salvagecount'|empty| +9006|2013_05_05_Account_Flags.sql|SHOW TABLES LIKE 'account_flags'|empty| +9007|2013_05_05_Item_Tick.sql|SHOW TABLES LIKE 'item_tick'|empty| +9008|2013_07_11_NPC_Special_Abilities.sql|SHOW COLUMNS FROM `npc_types` LIKE 'special_abilities'|empty| +9009|2013_10_12_Merc_Special_Abilities.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'special_abilities'|empty| +# 9010|2013_10_12_Merc_vwMercNpcTypes.sql +9011|2013_10_31_Recipe_disabling.sql|SHOW COLUMNS FROM `tradeskill_recipe` LIKE 'enabled'|empty| +9012|2013_11_07_BaseData.sql|SHOW TABLES LIKE 'base_data'|empty| +9013|2013_11_13_Instrument_Singing_Mastery.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '213' AND `slot` = '1' AND `effectid` = '260' AND `base1` = '2' AND `base2` = '23'|empty| +9014|2013_11_18_AssistRadius.sql|SHOW COLUMNS FROM `npc_types` LIKE 'assistradius'|empty| +9015|2013_12_26_MerchantList_Class_Required.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'classes_required'|empty| +9016|2014_01_04_SongModCapAAs.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '571' AND `slot` = '1' AND `effectid` = '261'|empty| +9017|2014_01_08_SpellsNewAdditions.sql|SHOW COLUMNS FROM `spells_new` LIKE 'persistdeath'|empty| +9018|2014_01_09_PreservePetSize.sql|SHOW COLUMNS FROM `character_pet_info` LIKE 'size'|empty| +9019|2014_01_20_MezMastery.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '781' AND `slot` = '1' AND `effectid` = '287'|empty| +9020|2014_01_20_Not_Extendable.sql|SHOW COLUMNS FROM `spells_new` LIKE 'not_extendable'|empty| +9021|2014_01_20_SpellCastingReinforcement.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '86' AND `slot` = '1' AND `effectid` = '128'|empty| +9022|2014_01_20_Weather.sql|SHOW COLUMNS FROM `zone` LIKE 'rain_chance1'|empty| +9023|2014_01_27_CritcalMendAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '230' AND `slot` = '1' AND `effectid` = '275'|empty +9024|2014_02_02_SpellCriticalsAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '4755' AND `slot` = '1' AND `effectid` = '294'|empty +9025|2014_02_13_Rename_instance_lockout_tables.sql|SHOW TABLES LIKE 'instance_list'|empty| +9026|2014_02_13_spells_new_update.sql|SHOW COLUMNS FROM `spells_new` LIKE 'ConeStartAngle'|empty| +9027|2014_02_20_buff_update.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'caston_y'|empty| +9028|2014_02_26_roambox_update.sql|SHOW COLUMNS FROM `spawngroup` LIKE 'mindelay'|empty| +9029|2014_02_26_virulentvenomAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '888' AND `slot` = '1' AND `effectid` = '250'|empty| +9030|2014_04_04_PhysicalResist.sql|SHOW COLUMNS FROM `npc_types` LIKE 'PhR'|empty| +9031|2014_04_10_No_Target_With_Hotkey.sql|SHOW COLUMNS FROM `npc_types` LIKE 'no_target_hotkey'|empty| +9032|2014_04_12_SlowMitigation.sql|SHOW COLUMNS FROM `npc_types` LIKE 'slow_mitigation'|contains|float +9034|2014_04_25_spawn_events.sql|SHOW COLUMNS FROM `spawn_events` LIKE 'strict'|empty| +9035|2014_04_27_AISpellEffects.sql|SHOW COLUMNS FROM `npc_types` LIKE 'npc_spells_effects_id'|empty| +9036|2014_05_04_SlowMitigationFix.sql|SHOW COLUMNS FROM `npc_types` LIKE 'slow_mitigation'|contains|float +9038|2014_06_25_AA_Updates.sql|SELECT * FROM `altadv_vars` WHERE `skill_id` = '1604'|empty +9039|2014_07_04_AA_Updates.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '158' AND `slot` = '1' AND `effectid` = '238'|empty +9040|2014_07_10_npc_spells.sql|SHOW COLUMNS FROM `npc_spells` LIKE 'engaged_no_sp_recast_min'|empty| +9041|2014_08_02_spells_new.sql|SHOW COLUMNS FROM `spells_new` LIKE 'viral_range'|empty| +9042|2014_08_12_NPC_raid_targets.sql|SHOW COLUMNS FROM `npc_types` LIKE 'raid_target'|empty| +9043|2014_08_18_spells_new_update.sql|SHOW COLUMNS FROM `spells_new` LIKE 'viral_targets'|empty| +9044|2014_08_20_merchantlist_probability.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'probability'|empty| +9045|2014_08_23_Complete_QueryServ_Table_Structures.sql|SHOW TABLES LIKE 'qs_player_aa_rate_hourly'|empty| +9046|2014_08_23_player_events_and_player_aa_rate_hourly.sql|SHOW TABLES LIKE 'qs_player_events'|empty| +9048|2014_09_09_attack_delay.sql|SHOW COLUMNS FROM `npc_types` LIKE 'attack_delay'|empty| +9050|2014_09_20_ban_messages.sql|SHOW COLUMNS FROM `account` LIKE 'ban_reason'|empty| +9051|2014_10_11_RaidMOTD.sql|SHOW COLUMNS FROM `raid_details` LIKE 'motd'|empty| +9052|2014_10_13_RaidLeadership.sql|SHOW TABLES LIKE 'raid_leaders'|empty| +9053|2014_10_18_group_mentor.sql|SHOW COLUMNS FROM `group_leaders` LIKE 'mentoree'|empty| +9054|2014_10_19_raid_group_mentor.sql|SHOW COLUMNS FROM `raid_leaders` LIKE 'mentoree'|empty| +9055|2014_10_30_special_abilities_null.sql|SHOW COLUMNS FROM `npc_types` LIKE 'special_abilities'|contains|NO +9056|2014_11_08_RaidMembers.sql|SHOW COLUMNS FROM `raid_members` LIKE 'groupid'|missing|unsigned +9057|2014_11_13_spells_new_updates.sql|SHOW COLUMNS FROM `spells_new` LIKE 'disallow_sit'|empty| + +# Upgrade conditions: +# This won't be needed after this system is implemented, but it is used database that are not +# yet using the versioning system to figure out where the database is schema wise to determine +# which updates are necessary to run +# +# Example: Version|Filename.sql|Query_to_Check_Condition_For_Needed_Update|match type|text to match +# 0 = Database Version +# 1 = Filename.sql +# 2 = Query_to_Check_Condition_For_Needed_Update +# 3 = Match Type - If condition from match type to Value 4 is true, update will flag for needing to be ran +# contains = If query results contains text from 4th value +# match = If query results matches text from 4th value +# missing = If query result is missing text from 4th value +# empty = If the query results in no results +# not_empty = If the query is not empty +# 4 = Text to match +# +# \ No newline at end of file diff --git a/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql b/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql index dfcdfd552..e93989777 100644 --- a/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql +++ b/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql @@ -1,4 +1,5 @@ -ALTER TABLE `npc_types` ADD COLUMN `special_abilities` TEXT NOT NULL DEFAULT '' AFTER `npcspecialattks`; +ALTER TABLE `npc_types` ADD COLUMN `special_abilities` TEXT NULL AFTER `npcspecialattks`; +ALTER TABLE `npc_types` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; UPDATE npc_types SET special_abilities = CONCAT(special_abilities, "1,1^") WHERE npcspecialattks LIKE BINARY '%S%'; UPDATE npc_types SET special_abilities = CONCAT(special_abilities, "2,1^") WHERE npcspecialattks LIKE BINARY '%E%'; diff --git a/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql b/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql index 740bf7517..cf63625da 100644 --- a/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql +++ b/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql @@ -1,5 +1,6 @@ -ALTER TABLE `merc_stats` ADD COLUMN `special_abilities` TEXT NOT NULL DEFAULT '' AFTER `specialattks`; - +ALTER TABLE `merc_stats` ADD COLUMN `special_abilities` TEXT NULL AFTER `specialattks`; +ALTER TABLE `merc_stats` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; + UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "1,1^") WHERE specialattks LIKE BINARY '%S%'; UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "2,1^") WHERE specialattks LIKE BINARY '%E%'; UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "3,1^") WHERE specialattks LIKE BINARY '%R%'; diff --git a/utils/sql/git/required/2014_06_25_AA_Updates..sql b/utils/sql/git/required/2014_06_25_AA_Updates..sql deleted file mode 100644 index 3d7a048b6..000000000 --- a/utils/sql/git/required/2014_06_25_AA_Updates..sql +++ /dev/null @@ -1,34 +0,0 @@ --- AA MGB update -UPDATE altadv_vars SET spellid = 5228 WHERE skill_id = 128; -UPDATE aa_actions SET spell_id = 5228, nonspell_action = 0 WHERE aaid = 128; - --- AA Project Illusion update -UPDATE altadv_vars SET spellid = 5227 WHERE skill_id = 643; -UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643; - --- AA Improved Reclaim Energy -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0'); - --- AA Headshot -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0'); - --- AA Anatomy (Rogue Assassinate) -INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0'); - --- AA Finishing Blow Fix -DELETE FROM aa_effects WHERE aaid = 199 AND slot = 2; -DELETE FROM aa_effects WHERE aaid = 200 AND slot = 2; -DELETE FROM aa_effects WHERE aaid = 201 AND slot = 2; -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '2', '440', '50', '200'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '2', '440', '52', '200'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '2', '440', '54', '200'); \ No newline at end of file diff --git a/utils/sql/git/required/2014_09_20_ban_messages.sql b/utils/sql/git/required/2014_09_20_ban_messages.sql index 4612f9962..229f08776 100644 --- a/utils/sql/git/required/2014_09_20_ban_messages.sql +++ b/utils/sql/git/required/2014_09_20_ban_messages.sql @@ -1 +1 @@ -ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL AFTER `expansion`, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`; +ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`; diff --git a/world/net.cpp b/world/net.cpp index 7973b5872..eb2721c92 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -111,6 +111,15 @@ int main(int argc, char** argv) { RegisterExecutablePlatform(ExePlatformWorld); set_exception_handler(); + /* Database Version Check */ + uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION; + if (argc >= 2) { + if (strcasecmp(argv[1], "db_version") == 0) { + std::cout << "Binary Database Version: " << Database_Version << std::endl; + return 0; + } + } + // Load server configuration _log(WORLD__INIT, "Loading server configuration.."); if (!WorldConfig::LoadConfig()) { diff --git a/zone/client.cpp b/zone/client.cpp index cfc686707..25dbb2840 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7296,26 +7296,24 @@ void Client::SendMercPersonalInfo() { uint32 mercTypeCount = 1; uint32 mercCount = 1; //TODO: Un-hardcode this and support multiple mercs like in later clients than SoD. - //uint32 packetSize = 0; - uint32 i=0; + uint32 i = 0; uint32 altCurrentType = 19; //TODO: Implement alternate currency purchases involving mercs! - if (GetClientVersion() >= EQClientRoF) + MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; + + int stancecount = 0; + stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); + if(stancecount > MAX_MERC_STANCES || mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) { - MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; + SendMercMerchantResponsePacket(0); + return; + } - if (mercData) + if(mercData) + { + if (GetClientVersion() >= EQClientRoF) { - int i = 0; - int stancecount = 0; - stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); - - if(stancecount > MAX_MERC_STANCES || mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) - { - SendMercMerchantResponsePacket(0); - return; - } - if (mercCount > 0 && mercCount) + if (mercCount > 0) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, sizeof(MercenaryDataUpdate_Struct)); MercenaryDataUpdate_Struct* mdus = (MercenaryDataUpdate_Struct*)outapp->pBuffer; @@ -7353,40 +7351,19 @@ void Client::SendMercPersonalInfo() mdus->MercData[i].MercUnk05 = 1; FastQueuePacket(&outapp); + safe_delete(outapp); return; } } - } - else - { - int stancecount = 0; - stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); - - if(mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) + else { - if (GetClientVersion() == EQClientSoD) + if(mercTypeCount > 0 && mercCount > 0) { - SendMercMerchantResponsePacket(0); - } - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); - MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; - MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; - - - if(mercData) - { - if(mercTypeCount > 0) - { - mml->MercTypeCount = mercTypeCount; //We only should have one merc entry. + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); + MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; + mml->MercTypeCount = mercTypeCount; //We should only have one merc entry. mml->MercGrades[i] = 1; - } - mml->MercCount = mercCount; - if(mercCount > 0) - { - + mml->MercCount = mercCount; mml->Mercs[i].MercID = mercData->MercTemplateID; mml->Mercs[i].MercType = mercData->MercType; mml->Mercs[i].MercSubType = mercData->MercSubType; @@ -7403,7 +7380,7 @@ void Client::SendMercPersonalInfo() mml->Mercs[i].StanceCount = zone->merc_stance_list[mercData->MercTemplateID].size(); mml->Mercs[i].MercUnk03 = 0; mml->Mercs[i].MercUnk04 = 1; - //mml->Mercs[i].MercName; + strn0cpy(mml->Mercs[i].MercName, GetMercInfo().merc_name , sizeof(mml->Mercs[i].MercName)); int stanceindex = 0; if(mml->Mercs[i].StanceCount != 0) { @@ -7417,31 +7394,12 @@ void Client::SendMercPersonalInfo() } } FastQueuePacket(&outapp); - } - else - { safe_delete(outapp); - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } return; } - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } - } - else - { - safe_delete(outapp); - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } - return; } } + SendMercMerchantResponsePacket(0); } void Client::SendClearMercInfo() diff --git a/zone/client.h b/zone/client.h index 16e7b82dd..8407ff221 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1,4 +1,4 @@ -/* EQEMu: Everquest Server Emulator +/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.org) This program is free software; you can redistribute it and/or modify @@ -61,10 +61,10 @@ class Client; #include -#define CLIENT_TIMEOUT 90000 -#define CLIENT_LD_TIMEOUT 30000 // length of time client stays in zone after LDing -#define TARGETING_RANGE 200 // range for /assist and /target -#define XTARGET_HARDCAP 20 +#define CLIENT_TIMEOUT 90000 +#define CLIENT_LD_TIMEOUT 30000 // length of time client stays in zone after LDing +#define TARGETING_RANGE 200 // range for /assist and /target +#define XTARGET_HARDCAP 20 extern Zone* zone; extern TaskManager *taskmanager; @@ -78,25 +78,25 @@ public: bool ack_req; }; -enum { //Type arguments to the Message* routines. +enum { //Type arguments to the Message* routines. //all not explicitly listed are the same grey color clientMessageWhite0 = 0, - clientMessageLoot = 2, //dark green - clientMessageTradeskill = 4, //light blue - clientMessageTell = 5, //magenta + clientMessageLoot = 2, //dark green + clientMessageTradeskill = 4, //light blue + clientMessageTell = 5, //magenta clientMessageWhite = 7, clientMessageWhite2 = 10, clientMessageLightGrey = 12, - clientMessageError = 13, //red + clientMessageError = 13, //red clientMessageGreen = 14, clientMessageYellow = 15, clientMessageBlue = 16, - clientMessageGroup = 18, //cyan + clientMessageGroup = 18, //cyan clientMessageWhite3 = 20, }; #define SPELLBAR_UNLOCK 0x2bc -enum { //scribing argument to MemorizeSpell +enum { //scribing argument to MemorizeSpell memSpellScribing = 0, memSpellMemorize = 1, memSpellForget = 2, @@ -105,14 +105,14 @@ enum { //scribing argument to MemorizeSpell //Modes for the zoning state of the client. typedef enum { - ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac - GMSummon, // Always send ZonePlayerToBind_Struct to client: Only a GM Summon - ZoneToBindPoint, // Always send ZonePlayerToBind_Struct to client: Death Only - ZoneSolicited, // Always send ZonePlayerToBind_Struct to client: Portal, Translocate, Evac spells that have a x y z coord in the spell data + ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac + GMSummon, // Always send ZonePlayerToBind_Struct to client: Only a GM Summon + ZoneToBindPoint, // Always send ZonePlayerToBind_Struct to client: Death Only + ZoneSolicited, // Always send ZonePlayerToBind_Struct to client: Portal, Translocate, Evac spells that have a x y z coord in the spell data ZoneUnsolicited, - GateToBindPoint, // Always send RequestClientZoneChange_Struct to client: Gate spell or Translocate To Bind Point spell - SummonPC, // In-zone GMMove() always: Call of the Hero spell or some other type of in zone only summons - Rewind, // Summon to /rewind location. + GateToBindPoint, // Always send RequestClientZoneChange_Struct to client: Gate spell or Translocate To Bind Point spell + SummonPC, // In-zone GMMove() always: Call of the Hero spell or some other type of in zone only summons + Rewind, // Summon to /rewind location. EvacToSafeCoords } ZoneMode; @@ -211,7 +211,7 @@ public: virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill); virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, - ExtraAttackOptions *opts = nullptr); + ExtraAttackOptions *opts = nullptr); virtual bool HasRaid() { return (GetRaid() ? true : false); } virtual bool HasGroup() { return (GetGroup() ? true : false); } virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); } @@ -221,176 +221,176 @@ public: virtual void SetAttackTimer(); float GetQuiverHaste(); - void AI_Init(); - void AI_Start(uint32 iMoveDelay = 0); - void AI_Stop(); - void AI_Process(); - void AI_SpellCast(); - void Trader_ShowItems(); - void Trader_CustomerBrowsing(Client *Customer); - void Trader_EndTrader(); - void Trader_StartTrader(); - uint8 WithCustomer(uint16 NewCustomer); - void KeyRingLoad(); - void KeyRingAdd(uint32 item_id); - bool KeyRingCheck(uint32 item_id); - void KeyRingList(); + void AI_Init(); + void AI_Start(uint32 iMoveDelay = 0); + void AI_Stop(); + void AI_Process(); + void AI_SpellCast(); + void Trader_ShowItems(); + void Trader_CustomerBrowsing(Client *Customer); + void Trader_EndTrader(); + void Trader_StartTrader(); + uint8 WithCustomer(uint16 NewCustomer); + void KeyRingLoad(); + void KeyRingAdd(uint32 item_id); + bool KeyRingCheck(uint32 item_id); + void KeyRingList(); virtual bool IsClient() const { return true; } - void CompleteConnect(); - bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); - void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); - void SendBuyerPacket(Client* Buyer); + void CompleteConnect(); + bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); + void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); + void SendBuyerPacket(Client* Buyer); GetItems_Struct* GetTraderItems(); - void SendBazaarWelcome(); - void DyeArmor(DyeStruct* dye); - uint8 SlotConvert(uint8 slot,bool bracer=false); - void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0); - void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); - bool FilteredMessageCheck(Mob *sender, eqFilterType filter); - void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); - void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, - uint32 string_id, const char *message1, const char *message2 = nullptr, - const char *message3 = nullptr, const char *message4 = nullptr, - const char *message5 = nullptr, const char *message6 = nullptr, - const char *message7 = nullptr, const char *message8 = nullptr, - const char *message9 = nullptr); - void Tell_StringID(uint32 string_id, const char *who, const char *message); - void SendColoredText(uint32 color, std::string message); - void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); - void SendTraderItem(uint32 item_id,uint16 quantity); - uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); + void SendBazaarWelcome(); + void DyeArmor(DyeStruct* dye); + uint8 SlotConvert(uint8 slot,bool bracer=false); + void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0); + void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); + bool FilteredMessageCheck(Mob *sender, eqFilterType filter); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, + uint32 string_id, const char *message1, const char *message2 = nullptr, + const char *message3 = nullptr, const char *message4 = nullptr, + const char *message5 = nullptr, const char *message6 = nullptr, + const char *message7 = nullptr, const char *message8 = nullptr, + const char *message9 = nullptr); + void Tell_StringID(uint32 string_id, const char *who, const char *message); + void SendColoredText(uint32 color, std::string message); + void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); + void SendTraderItem(uint32 item_id,uint16 quantity); + uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); ItemInst* FindTraderItemBySerialNumber(int32 SerialNumber); - void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot); - void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid); - void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges); - void TradeRequestFailed(const EQApplicationPacket* app); - void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app); - void TraderUpdate(uint16 slot_id,uint32 trader_id); - void FinishTrade(Mob* with, bool finalizer = false, void* event_entry = nullptr, std::list* event_details = nullptr); - void SendZonePoints(); + void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot); + void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid); + void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges); + void TradeRequestFailed(const EQApplicationPacket* app); + void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app); + void TraderUpdate(uint16 slot_id,uint32 trader_id); + void FinishTrade(Mob* with, bool finalizer = false, void* event_entry = nullptr, std::list* event_details = nullptr); + void SendZonePoints(); - void SendBuyerResults(char *SearchQuery, uint32 SearchID); - void ShowBuyLines(const EQApplicationPacket *app); - void SellToBuyer(const EQApplicationPacket *app); - void ToggleBuyerMode(bool TurnOn); - void UpdateBuyLine(const EQApplicationPacket *app); - void BuyerItemSearch(const EQApplicationPacket *app); - void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; } - const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); } + void SendBuyerResults(char *SearchQuery, uint32 SearchID); + void ShowBuyLines(const EQApplicationPacket *app); + void SellToBuyer(const EQApplicationPacket *app); + void ToggleBuyerMode(bool TurnOn); + void UpdateBuyLine(const EQApplicationPacket *app); + void BuyerItemSearch(const EQApplicationPacket *app); + void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; } + const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); } - void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); virtual bool Process(); - void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying); - void SendPacketQueue(bool Block = true); - void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone); - void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL); - void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname=nullptr); - void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); - void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); - void Message(uint32 type, const char* message, ...); - void QuestJournalledMessage(const char *npcname, const char* message); - void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber); - void SendSound(); - void LearnRecipe(uint32 recipeID); - bool CanIncreaseTradeskill(SkillUseTypes tradeskill); + void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying); + void SendPacketQueue(bool Block = true); + void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone); + void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL); + void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname=nullptr); + void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); + void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); + void Message(uint32 type, const char* message, ...); + void QuestJournalledMessage(const char *npcname, const char* message); + void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber); + void SendSound(); + void LearnRecipe(uint32 recipeID); + bool CanIncreaseTradeskill(SkillUseTypes tradeskill); - EQApplicationPacket* ReturnItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); + EQApplicationPacket* ReturnItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); - bool GetRevoked() const { return revoked; } - void SetRevoked(bool rev) { revoked = rev; } - inline uint32 GetIP() const { return ip; } - inline bool GetHideMe() const { return gmhideme; } - void SetHideMe(bool hm); - inline uint16 GetPort() const { return port; } - bool IsDead() const { return(dead); } - bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); } - inline bool IsLFP() { return LFP; } - void UpdateLFP(); + bool GetRevoked() const { return revoked; } + void SetRevoked(bool rev) { revoked = rev; } + inline uint32 GetIP() const { return ip; } + inline bool GetHideMe() const { return gmhideme; } + void SetHideMe(bool hm); + inline uint16 GetPort() const { return port; } + bool IsDead() const { return(dead); } + bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); } + inline bool IsLFP() { return LFP; } + void UpdateLFP(); - virtual bool Save() { return Save(0); } - bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now - void SaveBackup(); + virtual bool Save() { return Save(0); } + bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now + void SaveBackup(); /* New PP Save Functions */ bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); } bool SaveAA(); inline bool ClientDataLoaded() const { return client_data_loaded; } - inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } - inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } - inline void Kick() { client_state = CLIENT_KICKED; } - inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } - inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } - void WorldKick(); - inline uint8 GetAnon() const { return m_pp.anon; } - inline PlayerProfile_Struct& GetPP() { return m_pp; } - inline ExtendedProfile_Struct& GetEPP() { return m_epp; } - inline Inventory& GetInv() { return m_inv; } - inline const Inventory& GetInv() const { return m_inv; } - inline PetInfo* GetPetInfo(uint16 pet) { return (pet==1)?&m_suspendedminion:&m_petinfo; } + inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } + inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } + inline void Kick() { client_state = CLIENT_KICKED; } + inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } + inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } + void WorldKick(); + inline uint8 GetAnon() const { return m_pp.anon; } + inline PlayerProfile_Struct& GetPP() { return m_pp; } + inline ExtendedProfile_Struct& GetEPP() { return m_epp; } + inline Inventory& GetInv() { return m_inv; } + inline const Inventory& GetInv() const { return m_inv; } + inline PetInfo* GetPetInfo(uint16 pet) { return (pet==1)?&m_suspendedminion:&m_petinfo; } inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; } inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; } - bool CheckAccess(int16 iDBLevel, int16 iDefaultLevel); + bool CheckAccess(int16 iDBLevel, int16 iDefaultLevel); - void CheckQuests(const char* zonename, const char* message, uint32 npc_id, uint32 item_id, Mob* other); - void LogLoot(Client* player,Corpse* corpse,const Item_Struct* item); - bool AutoAttackEnabled() const { return auto_attack; } - bool AutoFireEnabled() const { return auto_fire; } - void MakeCorpse(uint32 exploss); + void CheckQuests(const char* zonename, const char* message, uint32 npc_id, uint32 item_id, Mob* other); + 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); + bool ChangeFirstName(const char* in_firstname,const char* gmname); - void Duck(); - void Stand(); + void Duck(); + void Stand(); - virtual void SetMaxHP(); - int32 LevelRegen(); - void HPTick(); - void SetGM(bool toggle); - void SetPVP(bool toggle); + virtual void SetMaxHP(); + int32 LevelRegen(); + void HPTick(); + void SetGM(bool toggle); + void SetPVP(bool toggle); - inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); } - inline bool GetGM() const { return m_pp.gm != 0; } + inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); } + inline bool GetGM() const { return m_pp.gm != 0; } - inline void SetBaseClass(uint32 i) { m_pp.class_=i; } - inline void SetBaseRace(uint32 i) { m_pp.race=i; } - inline void SetBaseGender(uint32 i) { m_pp.gender=i; } + inline void SetBaseClass(uint32 i) { m_pp.class_=i; } + inline void SetBaseRace(uint32 i) { m_pp.race=i; } + inline void SetBaseGender(uint32 i) { m_pp.gender=i; } inline void SetDeity(uint32 i) {m_pp.deity=i;deity=i;} - inline uint8 GetLevel2() const { return m_pp.level2; } - inline uint16 GetBaseRace() const { return m_pp.race; } - inline uint16 GetBaseClass() const { return m_pp.class_; } - inline uint8 GetBaseGender() const { return m_pp.gender; } - inline uint8 GetBaseFace() const { return m_pp.face; } - inline uint8 GetBaseHairColor() const { return m_pp.haircolor; } - inline uint8 GetBaseBeardColor() const { return m_pp.beardcolor; } - inline uint8 GetBaseEyeColor() const { return m_pp.eyecolor1; } - inline uint8 GetBaseHairStyle() const { return m_pp.hairstyle; } - inline uint8 GetBaseBeard() const { return m_pp.beard; } - inline uint8 GetBaseHeritage() const { return m_pp.drakkin_heritage; } - inline uint8 GetBaseTattoo() const { return m_pp.drakkin_tattoo; } - inline uint8 GetBaseDetails() const { return m_pp.drakkin_details; } - inline const float GetBindX(uint32 index = 0) const { return m_pp.binds[index].x; } - inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; } - inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; } - inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; } - inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; } - int32 CalcMaxMana(); - int32 CalcBaseMana(); - const int32& SetMana(int32 amount); - int32 CalcManaRegenCap(); + inline uint8 GetLevel2() const { return m_pp.level2; } + inline uint16 GetBaseRace() const { return m_pp.race; } + inline uint16 GetBaseClass() const { return m_pp.class_; } + inline uint8 GetBaseGender() const { return m_pp.gender; } + inline uint8 GetBaseFace() const { return m_pp.face; } + inline uint8 GetBaseHairColor() const { return m_pp.haircolor; } + inline uint8 GetBaseBeardColor() const { return m_pp.beardcolor; } + inline uint8 GetBaseEyeColor() const { return m_pp.eyecolor1; } + inline uint8 GetBaseHairStyle() const { return m_pp.hairstyle; } + inline uint8 GetBaseBeard() const { return m_pp.beard; } + inline uint8 GetBaseHeritage() const { return m_pp.drakkin_heritage; } + inline uint8 GetBaseTattoo() const { return m_pp.drakkin_tattoo; } + inline uint8 GetBaseDetails() const { return m_pp.drakkin_details; } + inline const float GetBindX(uint32 index = 0) const { return m_pp.binds[index].x; } + inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; } + inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; } + inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; } + inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; } + int32 CalcMaxMana(); + int32 CalcBaseMana(); + const int32& SetMana(int32 amount); + int32 CalcManaRegenCap(); - void ServerFilter(SetServerFilter_Struct* filter); - void BulkSendTraderInventory(uint32 char_id); - void SendSingleTraderItem(uint32 char_id, int uniqueid); - void BulkSendMerchantInventory(int merchant_id, int npcid); + void ServerFilter(SetServerFilter_Struct* filter); + void BulkSendTraderInventory(uint32 char_id); + void SendSingleTraderItem(uint32 char_id, int uniqueid); + void BulkSendMerchantInventory(int merchant_id, int npcid); - inline uint8 GetLanguageSkill(uint16 n) const { return m_pp.languages[n]; } + inline uint8 GetLanguageSkill(uint16 n) const { return m_pp.languages[n]; } - void SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item = nullptr); + void SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item = nullptr); - inline const char* GetLastName() const { return lastname; } + inline const char* GetLastName() const { return lastname; } inline float ProximityX() const { return(proximity_x); } inline float ProximityY() const { return(proximity_y); } @@ -398,91 +398,91 @@ public: inline void ClearAllProximities() { entity_list.ProcessMove(this, FLT_MAX, FLT_MAX, FLT_MAX); proximity_x = FLT_MAX; proximity_y = FLT_MAX; proximity_z = FLT_MAX; } /* - Begin client modifiers + Begin client modifiers */ virtual void CalcBonuses(); //these are all precalculated now - inline virtual int32 GetAC() const { return AC; } + inline virtual int32 GetAC() const { return AC; } inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(SkillOffense)) * 9 / 10); } inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } - inline virtual int GetHaste() const { return Haste; } + inline virtual int GetHaste() const { return Haste; } int GetRawACNoShield(int &shield_ac) const; - inline virtual int32 GetSTR() const { return STR; } - inline virtual int32 GetSTA() const { return STA; } - inline virtual int32 GetDEX() const { return DEX; } - inline virtual int32 GetAGI() const { return AGI; } - inline virtual int32 GetINT() const { return INT; } - inline virtual int32 GetWIS() const { return WIS; } - inline virtual int32 GetCHA() const { return CHA; } - inline virtual int32 GetMR() const { return MR; } - inline virtual int32 GetFR() const { return FR; } - inline virtual int32 GetDR() const { return DR; } - inline virtual int32 GetPR() const { return PR; } - inline virtual int32 GetCR() const { return CR; } - inline virtual int32 GetCorrup() const { return Corrup; } + inline virtual int32 GetSTR() const { return STR; } + inline virtual int32 GetSTA() const { return STA; } + inline virtual int32 GetDEX() const { return DEX; } + inline virtual int32 GetAGI() const { return AGI; } + inline virtual int32 GetINT() const { return INT; } + inline virtual int32 GetWIS() const { return WIS; } + inline virtual int32 GetCHA() const { return CHA; } + inline virtual int32 GetMR() const { return MR; } + inline virtual int32 GetFR() const { return FR; } + inline virtual int32 GetDR() const { return DR; } + inline virtual int32 GetPR() const { return PR; } + inline virtual int32 GetCR() const { return CR; } + inline virtual int32 GetCorrup() const { return Corrup; } - int32 GetMaxStat() const; - int32 GetMaxResist() const; - int32 GetMaxSTR() const; - int32 GetMaxSTA() const; - int32 GetMaxDEX() const; - int32 GetMaxAGI() const; - int32 GetMaxINT() const; - int32 GetMaxWIS() const; - int32 GetMaxCHA() const; - int32 GetMaxMR() const; - int32 GetMaxPR() const; - int32 GetMaxDR() const; - int32 GetMaxCR() const; - int32 GetMaxFR() const; - int32 GetMaxCorrup() const; - inline uint8 GetBaseSTR() const { return m_pp.STR; } - inline uint8 GetBaseSTA() const { return m_pp.STA; } - inline uint8 GetBaseCHA() const { return m_pp.CHA; } - inline uint8 GetBaseDEX() const { return m_pp.DEX; } - inline uint8 GetBaseINT() const { return m_pp.INT; } - inline uint8 GetBaseAGI() const { return m_pp.AGI; } - inline uint8 GetBaseWIS() const { return m_pp.WIS; } - inline uint8 GetBaseCorrup() const { return 15; } // Same for all + int32 GetMaxStat() const; + int32 GetMaxResist() const; + int32 GetMaxSTR() const; + int32 GetMaxSTA() const; + int32 GetMaxDEX() const; + int32 GetMaxAGI() const; + int32 GetMaxINT() const; + int32 GetMaxWIS() const; + int32 GetMaxCHA() const; + int32 GetMaxMR() const; + int32 GetMaxPR() const; + int32 GetMaxDR() const; + int32 GetMaxCR() const; + int32 GetMaxFR() const; + int32 GetMaxCorrup() const; + inline uint8 GetBaseSTR() const { return m_pp.STR; } + inline uint8 GetBaseSTA() const { return m_pp.STA; } + inline uint8 GetBaseCHA() const { return m_pp.CHA; } + inline uint8 GetBaseDEX() const { return m_pp.DEX; } + inline uint8 GetBaseINT() const { return m_pp.INT; } + inline uint8 GetBaseAGI() const { return m_pp.AGI; } + inline uint8 GetBaseWIS() const { return m_pp.WIS; } + inline uint8 GetBaseCorrup() const { return 15; } // Same for all - inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } - inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } - inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } - inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } - inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } - inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } - inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } - inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } - inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } - inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } - inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } - inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } - inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } + inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } + inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } + inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } + inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } + inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } + inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } + inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } + inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } + inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } + inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } + inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } + inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } // Mod2 - inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } - inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } - inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } - inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } - inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } - inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } - inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } - inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } - inline virtual int32 GetDS() const { return itembonuses.DamageShield; } + inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } + inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } + inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } + inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } + inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } + inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } + inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } + inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } + inline virtual int32 GetDS() const { return itembonuses.DamageShield; } // Mod3 - inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } - inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } - inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } - inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } + inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } + inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } + inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } + inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } - inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } - inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } - inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } - inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } - inline virtual int32 GetWindMod() const { return itembonuses.windMod; } + inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } + inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } + inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } + inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } + inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); @@ -504,29 +504,29 @@ public: virtual void InitializeBuffSlots(); virtual void UninitializeBuffSlots(); - inline const int32 GetBaseHP() const { return base_hp; } + inline const int32 GetBaseHP() const { return base_hp; } uint32 GetWeight() const { return(weight); } inline void RecalcWeight() { weight = CalcCurrentWeight(); } uint32 CalcCurrentWeight(); - inline uint32 GetCopper() const { return m_pp.copper; } - inline uint32 GetSilver() const { return m_pp.silver; } - inline uint32 GetGold() const { return m_pp.gold; } - inline uint32 GetPlatinum() const { return m_pp.platinum; } + inline uint32 GetCopper() const { return m_pp.copper; } + inline uint32 GetSilver() const { return m_pp.silver; } + inline uint32 GetGold() const { return m_pp.gold; } + inline uint32 GetPlatinum() const { return m_pp.platinum; } /*Endurance and such*/ - void CalcMaxEndurance(); //This calculates the maximum endurance we can have - int32 CalcBaseEndurance(); //Calculates Base End - int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() - int32 GetEndurance() const {return cur_end;} //This gets our current endurance - int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call - int32 CalcEnduranceRegenCap(); - int32 CalcHPRegenCap(); + void CalcMaxEndurance(); //This calculates the maximum endurance we can have + int32 CalcBaseEndurance(); //Calculates Base End + int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() + int32 GetEndurance() const {return cur_end;} //This gets our current endurance + int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call + int32 CalcEnduranceRegenCap(); + int32 CalcHPRegenCap(); inline uint8 GetEndurancePercent() { return (uint8)((float)cur_end / (float)max_end * 100.0f); } - void SetEndurance(int32 newEnd); //This sets the current endurance to the new value - void DoEnduranceRegen(); //This Regenerates endurance - void DoEnduranceUpkeep(); //does the endurance upkeep + void SetEndurance(int32 newEnd); //This sets the current endurance to the new value + void DoEnduranceRegen(); //This Regenerates endurance + void DoEnduranceUpkeep(); //does the endurance upkeep //This calculates total Attack Rating to match very close to what the client should show uint32 GetTotalATK(); @@ -538,153 +538,153 @@ public: bool Rampage(); void DurationRampage(uint32 duration); - inline uint32 GetEXP() const { return m_pp.exp; } + inline uint32 GetEXP() const { return m_pp.exp; } - bool UpdateLDoNPoints(int32 points, uint32 theme); - void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } - uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } - void AddPVPPoints(uint32 Points); - uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; } - void SetRadiantCrystals(uint32 Crystals) { m_pp.currentRadCrystals = Crystals; } - uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; } - void SetEbonCrystals(uint32 Crystals) { m_pp.currentEbonCrystals = Crystals; } - void AddCrystals(uint32 Radiant, uint32 Ebon); - void SendCrystalCounts(); + bool UpdateLDoNPoints(int32 points, uint32 theme); + void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } + uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } + void AddPVPPoints(uint32 Points); + uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; } + void SetRadiantCrystals(uint32 Crystals) { m_pp.currentRadCrystals = Crystals; } + uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; } + void SetEbonCrystals(uint32 Crystals) { m_pp.currentEbonCrystals = Crystals; } + void AddCrystals(uint32 Radiant, uint32 Ebon); + void SendCrystalCounts(); - void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); - void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); - void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); - void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); - void AddLeadershipEXP(uint32 group_exp, uint32 raid_exp); - void SendLeadershipEXPUpdate(); - bool IsLeadershipEXPOn(); - inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; } - inline LeadershipAA_Struct &GetLeadershipAA() { return m_pp.leader_abilities; } - inline GroupLeadershipAA_Struct &GetGroupLeadershipAA() { return m_pp.leader_abilities.group; } - inline RaidLeadershipAA_Struct &GetRaidLeadershipAA() { return m_pp.leader_abilities.raid; } - int GroupLeadershipAAHealthEnhancement(); - int GroupLeadershipAAManaEnhancement(); - int GroupLeadershipAAHealthRegeneration(); - int GroupLeadershipAAOffenseEnhancement(); - void InspectBuffs(Client* Inspector, int Rank); - uint32 GetRaidPoints() { return(m_pp.raid_leadership_points); } - uint32 GetGroupPoints() { return(m_pp.group_leadership_points); } - uint32 GetRaidEXP() { return(m_pp.raid_leadership_exp); } - uint32 GetGroupEXP() { return(m_pp.group_leadership_exp); } - uint32 GetTotalSecondsPlayed() { return(TotalSecondsPlayed); } + void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); + void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); + void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); + void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); + void AddLeadershipEXP(uint32 group_exp, uint32 raid_exp); + void SendLeadershipEXPUpdate(); + bool IsLeadershipEXPOn(); + inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; } + inline LeadershipAA_Struct &GetLeadershipAA() { return m_pp.leader_abilities; } + inline GroupLeadershipAA_Struct &GetGroupLeadershipAA() { return m_pp.leader_abilities.group; } + inline RaidLeadershipAA_Struct &GetRaidLeadershipAA() { return m_pp.leader_abilities.raid; } + int GroupLeadershipAAHealthEnhancement(); + int GroupLeadershipAAManaEnhancement(); + int GroupLeadershipAAHealthRegeneration(); + int GroupLeadershipAAOffenseEnhancement(); + void InspectBuffs(Client* Inspector, int Rank); + uint32 GetRaidPoints() { return(m_pp.raid_leadership_points); } + uint32 GetGroupPoints() { return(m_pp.group_leadership_points); } + uint32 GetRaidEXP() { return(m_pp.raid_leadership_exp); } + uint32 GetGroupEXP() { return(m_pp.group_leadership_exp); } + uint32 GetTotalSecondsPlayed() { return(TotalSecondsPlayed); } virtual void SetLevel(uint8 set_level, bool command = false); - void GoToBind(uint8 bindnum = 0); - void GoToSafeCoords(uint16 zone_id, uint16 instance_id); - void Gate(); - void SetBindPoint(int to_zone = -1, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f); - void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f); - uint32 GetStartZone(void); - void MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void AssignToInstance(uint16 instance_id); - void RemoveFromInstance(uint16 instance_id); - void WhoAll(); - bool CheckLoreConflict(const Item_Struct* item); - void ChangeLastName(const char* in_lastname); - void GetGroupAAs(GroupLeadershipAA_Struct *into) const; - void GetRaidAAs(RaidLeadershipAA_Struct *into) const; - void ClearGroupAAs(); - void UpdateGroupAAs(int32 points, uint32 type); - void SacrificeConfirm(Client* caster); - void Sacrifice(Client* caster); - void GoToDeath(); - inline const int32 GetInstanceID() const { return zone->GetInstanceID(); } + void 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 RemoveFromInstance(uint16 instance_id); + void WhoAll(); + bool CheckLoreConflict(const Item_Struct* item); + void ChangeLastName(const char* in_lastname); + void GetGroupAAs(GroupLeadershipAA_Struct *into) const; + void GetRaidAAs(RaidLeadershipAA_Struct *into) const; + void ClearGroupAAs(); + void UpdateGroupAAs(int32 points, uint32 type); + void SacrificeConfirm(Client* caster); + void Sacrifice(Client* caster); + void GoToDeath(); + inline const int32 GetInstanceID() const { return zone->GetInstanceID(); } - FACTION_VALUE GetReverseFactionCon(Mob* iOther); - FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc); - int32 GetCharacterFactionLevel(int32 faction_id); - int32 GetModCharacterFactionLevel(int32 faction_id); - void MerchantRejectMessage(Mob *merchant, int primaryfaction); - void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp); + FACTION_VALUE GetReverseFactionCon(Mob* iOther); + FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc); + int32 GetCharacterFactionLevel(int32 faction_id); + int32 GetModCharacterFactionLevel(int32 faction_id); + void MerchantRejectMessage(Mob *merchant, int primaryfaction); + void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp); - void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); - void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); - int32 GetRawItemAC(); - uint16 GetCombinedAC_TEST(); + void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); + void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); + int32 GetRawItemAC(); + uint16 GetCombinedAC_TEST(); - inline uint32 LSAccountID() const { return lsaccountid; } - inline uint32 GetWID() const { return WID; } - inline void SetWID(uint32 iWID) { WID = iWID; } - inline uint32 AccountID() const { return account_id; } + inline uint32 LSAccountID() const { return lsaccountid; } + inline uint32 GetWID() const { return WID; } + inline void SetWID(uint32 iWID) { WID = iWID; } + inline uint32 AccountID() const { return account_id; } inline const char* AccountName()const { return account_name; } - inline int16 Admin() const { return admin; } - inline uint32 CharacterID() const { return character_id; } - void UpdateAdmin(bool iFromDB = true); - void UpdateWho(uint8 remove = 0); - bool GMHideMe(Client* client = 0); + inline int16 Admin() const { return admin; } + inline uint32 CharacterID() const { return character_id; } + void UpdateAdmin(bool iFromDB = true); + void UpdateWho(uint8 remove = 0); + bool GMHideMe(Client* client = 0); inline bool IsInAGuild() const { return(guild_id != GUILD_NONE && guild_id != 0); } inline bool IsInGuild(uint32 in_gid) const { return(in_gid == guild_id && IsInAGuild()); } - inline uint32 GuildID() const { return guild_id; } - inline uint8 GuildRank() const { return guildrank; } - void SendGuildMOTD(bool GetGuildMOTDReply = false); - void SendGuildURL(); - void SendGuildChannel(); - void SendGuildSpawnAppearance(); - void SendGuildRanks(); - void SendGuildMembers(); - void SendGuildList(); - void SendGuildJoin(GuildJoin_Struct* gj); - void RefreshGuildInfo(); + inline uint32 GuildID() const { return guild_id; } + inline uint8 GuildRank() const { return guildrank; } + void SendGuildMOTD(bool GetGuildMOTDReply = false); + void SendGuildURL(); + void SendGuildChannel(); + void SendGuildSpawnAppearance(); + void SendGuildRanks(); + void SendGuildMembers(); + void SendGuildList(); + void SendGuildJoin(GuildJoin_Struct* gj); + void RefreshGuildInfo(); - void SendManaUpdatePacket(); - void SendManaUpdate(); - void SendEnduranceUpdate(); - uint8 GetFace() const { return m_pp.face; } - void WhoAll(Who_All_Struct* whom); - void FriendsWho(char *FriendsString); + void SendManaUpdatePacket(); + void SendManaUpdate(); + void SendEnduranceUpdate(); + uint8 GetFace() const { return m_pp.face; } + void WhoAll(Who_All_Struct* whom); + void FriendsWho(char *FriendsString); - void Stun(int duration); - void UnStun(); - void ReadBook(BookRequest_Struct *book); - void QuestReadBook(const char* text, uint8 type); - void SendClientMoneyUpdate(uint8 type,uint32 amount); - void SendMoneyUpdate(); - bool TakeMoneyFromPP(uint64 copper, bool updateclient=false); - void AddMoneyToPP(uint64 copper,bool updateclient); - void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold,uint32 platinum,bool updateclient); - bool HasMoney(uint64 copper); - uint64 GetCarriedMoney(); - uint64 GetAllMoney(); + void Stun(int duration); + void UnStun(); + void ReadBook(BookRequest_Struct *book); + void QuestReadBook(const char* text, uint8 type); + void SendClientMoneyUpdate(uint8 type,uint32 amount); + void SendMoneyUpdate(); + bool TakeMoneyFromPP(uint64 copper, bool updateclient=false); + void AddMoneyToPP(uint64 copper,bool updateclient); + void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold,uint32 platinum,bool updateclient); + bool HasMoney(uint64 copper); + uint64 GetCarriedMoney(); + uint64 GetAllMoney(); - bool IsDiscovered(uint32 itemid); - void DiscoverItem(uint32 itemid); + bool IsDiscovered(uint32 itemid); + void DiscoverItem(uint32 itemid); - bool TGB() const { return tgb; } + bool TGB() const { return tgb; } - void OnDisconnect(bool hard_disconnect); + void OnDisconnect(bool hard_disconnect); - uint16 GetSkillPoints() { return m_pp.points;} - void SetSkillPoints(int inp) { m_pp.points = inp;} + uint16 GetSkillPoints() { return m_pp.points;} + void SetSkillPoints(int inp) { m_pp.points = inp;} - void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } } - void IncreaseLanguageSkill(int skill_id, int value = 1); + void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } } + void IncreaseLanguageSkill(int skill_id, int value = 1); virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; } - uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } - bool HasSkill(SkillUseTypes skill_id) const; - bool CanHaveSkill(SkillUseTypes skill_id) const; - void SetSkill(SkillUseTypes skill_num, uint16 value); - void AddSkill(SkillUseTypes skillid, uint16 value); - void CheckSpecializeIncrease(uint16 spell_id); - void CheckSongSkillIncrease(uint16 spell_id); - bool CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi = 0); - void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill); - void SetLanguageSkill(int langid, int value); - void SetHoTT(uint32 mobid); - void ShowSkillsWindow(); - void SendStatsWindow(Client* client, bool use_window); + uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } + bool HasSkill(SkillUseTypes skill_id) const; + bool CanHaveSkill(SkillUseTypes skill_id) const; + void SetSkill(SkillUseTypes skill_num, uint16 value); + void AddSkill(SkillUseTypes skillid, uint16 value); + void CheckSpecializeIncrease(uint16 spell_id); + void CheckSongSkillIncrease(uint16 spell_id); + bool CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi = 0); + void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill); + void SetLanguageSkill(int langid, int value); + void SetHoTT(uint32 mobid); + void ShowSkillsWindow(); + void SendStatsWindow(Client* client, bool use_window); - uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; - inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } - uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); + uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); void TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid); void SendTradeskillDetails(uint32 recipe_id); @@ -692,11 +692,11 @@ public: void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill); void GMKill(); - inline bool IsMedding() const {return medding;} + inline bool IsMedding() const {return medding;} inline uint16 GetDuelTarget() const { return duel_target; } - inline bool IsDueling() const { return duelaccepted; } - inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; } - inline void SetDueling(bool duel) { duelaccepted = duel; } + inline bool IsDueling() const { return duelaccepted; } + inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; } + inline void SetDueling(bool duel) { duelaccepted = duel; } // use this one instead void MemSpell(uint16 spell_id, int slot, bool update_client = true); void UnmemSpell(int slot, bool update_client = true); @@ -709,11 +709,11 @@ public: 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; } + inline bool IsSitting() const {return (playeraction == 1);} + inline bool IsBecomeNPC() const { return npcflag; } + inline uint8 GetBecomeNPCLevel() const { return npclevel; } + inline void SetBecomeNPC(bool flag) { npcflag = flag; } + inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; } void SetFeigned(bool in_feigned); /// this cures timing issues cuz dead animation isn't done but server side feigning is? inline bool GetFeigned() const { return(feigned); } @@ -721,7 +721,7 @@ public: #ifdef PACKET_PROFILER void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); } #endif - uint32 GetEquipment(uint8 material_slot) const; // returns item id + uint32 GetEquipment(uint8 material_slot) const; // returns item id uint32 GetEquipmentColor(uint8 material_slot) const; inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } @@ -735,17 +735,17 @@ public: bool BindWound(Mob* bindmob, bool start, bool fail = false); void SetTradeskillObject(Object* object) { m_tradeskill_object = object; } Object* GetTradeskillObject() { return m_tradeskill_object; } - void SendTributes(); - void SendGuildTributes(); - void DoTributeUpdate(); - void SendTributeDetails(uint32 client_id, uint32 tribute_id); - int32 TributeItem(uint32 slot, uint32 quantity); - int32 TributeMoney(uint32 platinum); - void AddTributePoints(int32 ammount); - void ChangeTributeSettings(TributeInfo_Struct *t); - void SendTributeTimer(); - void ToggleTribute(bool enabled); - void SendPathPacket(std::vector &path); + void SendTributes(); + void SendGuildTributes(); + void DoTributeUpdate(); + void SendTributeDetails(uint32 client_id, uint32 tribute_id); + int32 TributeItem(uint32 slot, uint32 quantity); + int32 TributeMoney(uint32 platinum); + void AddTributePoints(int32 ammount); + void ChangeTributeSettings(TributeInfo_Struct *t); + void SendTributeTimer(); + void ToggleTribute(bool enabled); + void SendPathPacket(std::vector &path); inline PTimerList &GetPTimers() { return(p_timers); } @@ -757,9 +757,9 @@ public: void BuyAA(AA_Action* action); //this function is used by some AA stuff void MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing); - void SetAATitle(const char *Title); - void SetTitleSuffix(const char *txt); - inline uint32 GetMaxAAXP(void) const { return max_AAXP; } + void SetAATitle(const char *Title); + void SetTitleSuffix(const char *txt); + inline uint32 GetMaxAAXP(void) const { return max_AAXP; } inline uint32 GetAAXP() const { return m_pp.expAA; } void SendAAStats(); void SendAATable(); @@ -791,93 +791,93 @@ public: // Item methods void EVENT_ITEM_ScriptStopReturn(); - uint32 NukeItem(uint32 itemnum, uint8 where_to_check = - (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); - void SetTint(int16 slot_id, uint32 color); - void SetTint(int16 slot_id, Color_Struct& color); - void SetMaterial(int16 slot_id, uint32 item_id); - void Undye(); - int32 GetItemIDAt(int16 slot_id); - int32 GetAugmentIDAt(int16 slot_id, uint8 augslot); - bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false); - bool PushItemOnCursor(const ItemInst& inst, bool client_update = false); - void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); - bool SwapItem(MoveItem_Struct* move_in); - void SwapItemResync(MoveItem_Struct* move_slots); - void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); - void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); - bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); - bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, bool attuned = false, uint16 to_slot = MainCursor); - void SetStats(uint8 type,int16 set_val); - void IncStats(uint8 type,int16 increase_val); - void DropItem(int16 slot_id); - bool MakeItemLink(char* &ret_link, const ItemInst* inst); - int GetItemLinkHash(const ItemInst* inst); - void SendItemLink(const ItemInst* inst, bool sendtoall=false); - void SendLootItemInPacket(const ItemInst* inst, int16 slot_id); - void SendItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); - bool IsValidSlot(uint32 slot); - bool IsBankSlot(uint32 slot); + uint32 NukeItem(uint32 itemnum, uint8 where_to_check = + (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); + void SetTint(int16 slot_id, uint32 color); + void SetTint(int16 slot_id, Color_Struct& color); + void SetMaterial(int16 slot_id, uint32 item_id); + void Undye(); + int32 GetItemIDAt(int16 slot_id); + int32 GetAugmentIDAt(int16 slot_id, uint8 augslot); + bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false); + bool PushItemOnCursor(const ItemInst& inst, bool client_update = false); + void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); + bool SwapItem(MoveItem_Struct* move_in); + void SwapItemResync(MoveItem_Struct* move_slots); + void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); + void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); + bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); + bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, bool attuned = false, uint16 to_slot = MainCursor); + void SetStats(uint8 type,int16 set_val); + void IncStats(uint8 type,int16 increase_val); + void DropItem(int16 slot_id); + bool MakeItemLink(char* &ret_link, const ItemInst* inst); + int GetItemLinkHash(const ItemInst* inst); + void SendItemLink(const ItemInst* inst, bool sendtoall=false); + void SendLootItemInPacket(const ItemInst* inst, int16 slot_id); + void SendItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); + bool IsValidSlot(uint32 slot); + bool IsBankSlot(uint32 slot); - inline bool IsTrader() const { return(Trader); } - inline bool IsBuyer() const { return(Buyer); } - eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } - void SetFilter(eqFilterType filter_id, eqFilterMode value) { ClientFilters[filter_id]=value; } + inline bool IsTrader() const { return(Trader); } + inline bool IsBuyer() const { return(Buyer); } + eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } + void SetFilter(eqFilterType filter_id, eqFilterMode value) { ClientFilters[filter_id]=value; } - void BreakInvis(); - void LeaveGroup(); + void BreakInvis(); + void LeaveGroup(); - bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} - bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} - int32 GetHunger() const { return m_pp.hunger_level; } - int32 GetThirst() const { return m_pp.thirst_level; } - void SetHunger(int32 in_hunger); - void SetThirst(int32 in_thirst); - void SetConsumption(int32 in_hunger, int32 in_thirst); + bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} + bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} +int32 GetHunger() const { return m_pp.hunger_level; } +int32 GetThirst() const { return m_pp.thirst_level; } +void SetHunger(int32 in_hunger); +void SetThirst(int32 in_thirst); +void SetConsumption(int32 in_hunger, int32 in_thirst); - bool CheckTradeLoreConflict(Client* other); - void LinkDead(); - void Insight(uint32 t_id); - bool CheckDoubleAttack(bool tripleAttack = false); - bool CheckDoubleRangedAttack(); + bool CheckTradeLoreConflict(Client* other); + void LinkDead(); + void Insight(uint32 t_id); + bool CheckDoubleAttack(bool tripleAttack = false); + bool CheckDoubleRangedAttack(); //remove charges/multiple objects from inventory: - //bool DecreaseByType(uint32 type, uint8 amt); - bool DecreaseByID(uint32 type, uint8 amt); - uint8 SlotConvert2(uint8 slot); //Maybe not needed. - void Escape(); //AA Escape - void RemoveNoRent(bool client_update = true); - void RemoveDuplicateLore(bool client_update = true); - void MoveSlotNotAllowed(bool client_update = true); - virtual void RangedAttack(Mob* other, bool CanDoubleAttack = false); - virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false); - void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false); + //bool DecreaseByType(uint32 type, uint8 amt); + bool DecreaseByID(uint32 type, uint8 amt); + uint8 SlotConvert2(uint8 slot); //Maybe not needed. + void Escape(); //AA Escape + void RemoveNoRent(bool client_update = true); + void RemoveDuplicateLore(bool client_update = true); + void MoveSlotNotAllowed(bool client_update = true); + virtual void RangedAttack(Mob* other, bool CanDoubleAttack = false); + virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false); + void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false); - void SetZoneFlag(uint32 zone_id); - void ClearZoneFlag(uint32 zone_id); - bool HasZoneFlag(uint32 zone_id) const; - void SendZoneFlagInfo(Client *to) const; - void LoadZoneFlags(); + void SetZoneFlag(uint32 zone_id); + void ClearZoneFlag(uint32 zone_id); + bool HasZoneFlag(uint32 zone_id) const; + void SendZoneFlagInfo(Client *to) const; + void LoadZoneFlags(); - void ChangeSQLLog(const char *file); - void LogSQL(const char *fmt, ...); - bool CanFish(); - void GoFish(); - void ForageItem(bool guarantee = false); + void ChangeSQLLog(const char *file); + void LogSQL(const char *fmt, ...); + bool CanFish(); + void GoFish(); + void ForageItem(bool guarantee = false); //Calculate vendor price modifier based on CHA: (reverse==selling) - float CalcPriceMod(Mob* other = 0, bool reverse = false); - void ResetTrade(); - void DropInst(const ItemInst* inst); - bool TrainDiscipline(uint32 itemid); - void SendDisciplineUpdate(); - bool UseDiscipline(uint32 spell_id, uint32 target); + float CalcPriceMod(Mob* other = 0, bool reverse = false); + void ResetTrade(); + void DropInst(const ItemInst* inst); + bool TrainDiscipline(uint32 itemid); + void SendDisciplineUpdate(); + bool UseDiscipline(uint32 spell_id, uint32 target); - bool CheckTitle(int titleset); - void EnableTitle(int titleset); - void RemoveTitle(int titleset); + bool CheckTitle(int titleset); + void EnableTitle(int titleset); + void RemoveTitle(int titleset); - void EnteringMessages(Client* client); - void SendRules(Client* client); + void EnteringMessages(Client* client); + void SendRules(Client* client); std::list consent_list; //Anti-Cheat Stuff @@ -904,28 +904,28 @@ public: void SendBuffDurationPacket(Buffs_Struct &buff); void SendBuffNumHitPacket(Buffs_Struct &buff, int slot); - void ProcessInspectRequest(Client* requestee, Client* requester); - bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } - int FindSpellBookSlotBySpellID(uint16 spellid); - int GetNextAvailableSpellBookSlot(int starting_slot = 0); + void ProcessInspectRequest(Client* requestee, Client* requester); + bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } + int FindSpellBookSlotBySpellID(uint16 spellid); + int GetNextAvailableSpellBookSlot(int starting_slot = 0); inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; } inline bool HasSpellScribed(int spellid) { return (FindSpellBookSlotBySpellID(spellid) != -1 ? true : false); } - uint16 GetMaxSkillAfterSpecializationRules(SkillUseTypes skillid, uint16 maxSkill); - void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0); - void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...); - bool PendingTranslocate; - time_t TranslocateTime; - bool PendingSacrifice; - std::string SacrificeCaster; - struct Translocate_Struct PendingTranslocateData; - void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); + uint16 GetMaxSkillAfterSpecializationRules(SkillUseTypes skillid, uint16 maxSkill); + void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0); + void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...); + bool PendingTranslocate; + time_t TranslocateTime; + bool PendingSacrifice; + std::string SacrificeCaster; + 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); + 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); } @@ -1047,7 +1047,7 @@ public: void HandleLDoNSenseTraps(NPC *target, uint16 skill, uint8 type); void HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type); void HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type); - int LDoNChest_SkillCheck(NPC *target, int skill); + int LDoNChest_SkillCheck(NPC *target, int skill); void MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count=1); @@ -1087,7 +1087,7 @@ public: inline bool GetPendingGuildInvitation() { return PendingGuildInvitation; } void LocateCorpse(); void SendTargetCommand(uint32 EntityID); - bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false); + bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false); void HandleRespawnFromHover(uint32 Option); bool IsHoveringForRespawn() { return RespawnFromHoverTimer.Enabled(); } std::list respawn_options; @@ -1129,12 +1129,13 @@ public: void RemoveAutoXTargets(); void ShowXTargets(Client *c); void InitializeMercInfo(); + bool CheckCanSpawnMerc(uint32 template_id); bool CheckCanHireMerc(Mob* merchant, uint32 template_id); bool CheckCanRetainMerc(uint32 upkeep); bool CheckCanUnsuspendMerc(); bool CheckCanDismissMerc(); - inline uint32 GetMercID() const { return mercid; } - inline uint8 GetMercSlot() const { return mercSlot; } + inline uint32 GetMercID() const { return mercid; } + inline uint8 GetMercSlot() const { return mercSlot; } void SetMercID( uint32 newmercid) { mercid = newmercid; } void SetMercSlot( uint8 newmercslot) { mercSlot = newmercslot; } Merc* GetMerc(); @@ -1142,9 +1143,11 @@ public: MercInfo& GetMercInfo() { return m_mercinfo[mercSlot]; } uint8 GetNumMercs(); void SetMerc(Merc* newmerc); + void SendMercResponsePackets(uint32 ResponseType); void SendMercMerchantResponsePacket(int32 response_type); void SendMercenaryUnknownPacket(uint8 type); void SendMercenaryUnsuspendPacket(uint8 type); + void SendMercTimer(Merc* merc = nullptr); void SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval = 900000, int32 unk01 = 180000); void SendMercSuspendResponsePacket(uint32 suspended_time); void SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02); @@ -1229,10 +1232,10 @@ protected: void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; - int16 GetFocusEffect(focusType type, uint16 spell_id); - int16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); + int16 GetFocusEffect(focusType type, uint16 spell_id); + int16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); - Mob* bind_sight_target; + Mob* bind_sight_target; Map::Vertex aa_los_me; Map::Vertex aa_los_them; @@ -1257,52 +1260,52 @@ protected: private: eqFilterMode ClientFilters[_FilterCount]; - int32 HandlePacket(const EQApplicationPacket *app); - void OPTGB(const EQApplicationPacket *app); - void OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z); - void OPMemorizeSpell(const EQApplicationPacket *app); - void OPMoveCoin(const EQApplicationPacket* app); - void MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type); - void OPGMTraining(const EQApplicationPacket *app); - void OPGMEndTraining(const EQApplicationPacket *app); - void OPGMTrainSkill(const EQApplicationPacket *app); - void OPGMSummon(const EQApplicationPacket *app); - void OPCombatAbility(const EQApplicationPacket *app); + int32 HandlePacket(const EQApplicationPacket *app); + void OPTGB(const EQApplicationPacket *app); + void OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z); + void OPMemorizeSpell(const EQApplicationPacket *app); + void OPMoveCoin(const EQApplicationPacket* app); + void MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type); + void OPGMTraining(const EQApplicationPacket *app); + void OPGMEndTraining(const EQApplicationPacket *app); + void OPGMTrainSkill(const EQApplicationPacket *app); + void OPGMSummon(const EQApplicationPacket *app); + void OPCombatAbility(const EQApplicationPacket *app); // Bandolier Methods - void CreateBandolier(const EQApplicationPacket *app); - void RemoveBandolier(const EQApplicationPacket *app); - void SetBandolier(const EQApplicationPacket *app); + void CreateBandolier(const EQApplicationPacket *app); + void RemoveBandolier(const EQApplicationPacket *app); + void SetBandolier(const EQApplicationPacket *app); - void HandleTraderPriceUpdate(const EQApplicationPacket *app); + void HandleTraderPriceUpdate(const EQApplicationPacket *app); - int32 CalcAC(); - int32 GetACMit(); - int32 GetACAvoid(); - int32 CalcATK(); - int32 CalcHaste(); + int32 CalcAC(); + int32 GetACMit(); + int32 GetACAvoid(); + int32 CalcATK(); + int32 CalcHaste(); - int32 CalcAlcoholPhysicalEffect(); - int32 CalcSTR(); - int32 CalcSTA(); - int32 CalcDEX(); - int32 CalcAGI(); - int32 CalcINT(); - int32 CalcWIS(); - int32 CalcCHA(); + int32 CalcAlcoholPhysicalEffect(); + int32 CalcSTR(); + int32 CalcSTA(); + int32 CalcDEX(); + int32 CalcAGI(); + int32 CalcINT(); + int32 CalcWIS(); + int32 CalcCHA(); - int32 CalcMR(); - int32 CalcFR(); - int32 CalcDR(); - int32 CalcPR(); - int32 CalcCR(); - int32 CalcCorrup(); - int32 CalcMaxHP(); - int32 CalcBaseHP(); - int32 CalcHPRegen(); - int32 CalcManaRegen(); - int32 CalcBaseManaRegen(); - uint32 GetClassHPFactor(); + int32 CalcMR(); + int32 CalcFR(); + int32 CalcDR(); + int32 CalcPR(); + int32 CalcCR(); + int32 CalcCorrup(); + int32 CalcMaxHP(); + int32 CalcBaseHP(); + int32 CalcHPRegen(); + int32 CalcManaRegen(); + int32 CalcBaseManaRegen(); + uint32 GetClassHPFactor(); void DoHPRegen(); void DoManaRegen(); void DoStaminaUpdate(); @@ -1314,75 +1317,75 @@ private: EQStreamInterface* eqs; - uint32 ip; - uint16 port; - CLIENT_CONN_STATUS client_state; - uint32 character_id; - uint32 WID; - uint32 account_id; - char account_name[30]; - uint32 lsaccountid; - char lskey[30]; - int16 admin; - uint32 guild_id; - uint8 guildrank; // player's rank in the guild, 0-GUILD_MAX_RANK - bool GuildBanker; - uint16 duel_target; - bool duelaccepted; + uint32 ip; + uint16 port; + CLIENT_CONN_STATUS client_state; + uint32 character_id; + uint32 WID; + uint32 account_id; + char account_name[30]; + uint32 lsaccountid; + char lskey[30]; + int16 admin; + uint32 guild_id; + uint8 guildrank; // player's rank in the guild, 0-GUILD_MAX_RANK + bool GuildBanker; + uint16 duel_target; + bool duelaccepted; std::list keyring; - bool tellsoff; // GM /toggle - bool gmhideme; - bool LFG; - bool LFP; - uint8 LFGFromLevel; - uint8 LFGToLevel; - bool LFGMatchFilter; - char LFGComments[64]; - bool AFK; - bool auto_attack; - bool auto_fire; - uint8 gmspeed; - bool medding; - uint16 horseId; - bool revoked; - uint32 pQueuedSaveWorkID; - uint16 pClientSideTarget; - uint32 weight; - bool berserk; - bool dead; - uint16 BoatID; - uint16 TrackingID; - uint16 CustomerID; - uint32 account_creation; - uint8 firstlogon; - uint32 mercid; // current merc - uint8 mercSlot; // selected merc slot - bool Trader; - bool Buyer; - std::string BuyerWelcomeMessage; - bool AbilityTimer; + bool tellsoff; // GM /toggle + bool gmhideme; + bool LFG; + bool LFP; + uint8 LFGFromLevel; + uint8 LFGToLevel; + bool LFGMatchFilter; + char LFGComments[64]; + bool AFK; + bool auto_attack; + bool auto_fire; + uint8 gmspeed; + bool medding; + uint16 horseId; + bool revoked; + uint32 pQueuedSaveWorkID; + uint16 pClientSideTarget; + uint32 weight; + bool berserk; + bool dead; + uint16 BoatID; + uint16 TrackingID; + uint16 CustomerID; + uint32 account_creation; + uint8 firstlogon; + uint32 mercid; // current merc + uint8 mercSlot; // selected merc slot + bool Trader; + bool Buyer; + std::string BuyerWelcomeMessage; + bool AbilityTimer; int Haste; //precalced value - int32 max_end; - int32 cur_end; + int32 max_end; + int32 cur_end; - PlayerProfile_Struct m_pp; - ExtendedProfile_Struct m_epp; - Inventory m_inv; - Object* m_tradeskill_object; - PetInfo m_petinfo; // current pet data, used while loading from and saving to DB - PetInfo m_suspendedminion; // pet data for our suspended minion. - MercInfo m_mercinfo[MAXMERCS]; // current mercenary - InspectMessage_Struct m_inspect_message; + PlayerProfile_Struct m_pp; + ExtendedProfile_Struct m_epp; + Inventory m_inv; + Object* m_tradeskill_object; + PetInfo m_petinfo; // current pet data, used while loading from and saving to DB + PetInfo m_suspendedminion; // pet data for our suspended minion. + MercInfo m_mercinfo[MAXMERCS]; // current mercenary + InspectMessage_Struct m_inspect_message; void NPCSpawn(const Seperator* sep); uint32 GetEXPForLevel(uint16 level); - bool CanBeInZone(); - void SendLogoutPackets(); - bool AddPacket(const EQApplicationPacket *, bool); - bool AddPacket(EQApplicationPacket**, bool); - bool SendAllPackets(); + bool CanBeInZone(); + void SendLogoutPackets(); + bool AddPacket(const EQApplicationPacket *, bool); + bool AddPacket(EQApplicationPacket**, bool); + bool SendAllPackets(); LinkedList clientpackets; //Zoning related stuff @@ -1391,62 +1394,62 @@ private: void DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instance_id, float dest_x, float dest_y, float dest_z, float dest_h, int8 ignore_r); void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm); void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - float zonesummon_x; - float zonesummon_y; - float zonesummon_z; - uint16 zonesummon_id; - uint8 zonesummon_ignorerestrictions; + float zonesummon_x; + float zonesummon_y; + float zonesummon_z; + uint16 zonesummon_id; + uint8 zonesummon_ignorerestrictions; ZoneMode zone_mode; - Timer position_timer; - uint8 position_timer_counter; + Timer position_timer; + uint8 position_timer_counter; - PTimerList p_timers; //persistent timers - Timer hpupdate_timer; - Timer camp_timer; - Timer process_timer; - Timer stamina_timer; - Timer zoneinpacket_timer; - Timer linkdead_timer; - Timer dead_timer; - Timer global_channel_timer; - Timer shield_timer; - Timer fishing_timer; - Timer endupkeep_timer; - Timer forget_timer; // our 2 min everybody forgets you timer - Timer autosave_timer; + PTimerList p_timers; //persistent timers + Timer hpupdate_timer; + Timer camp_timer; + Timer process_timer; + Timer stamina_timer; + Timer zoneinpacket_timer; + Timer linkdead_timer; + Timer dead_timer; + Timer global_channel_timer; + Timer shield_timer; + Timer fishing_timer; + Timer endupkeep_timer; + Timer forget_timer; // our 2 min everybody forgets you timer + Timer autosave_timer; #ifdef REVERSE_AGGRO - Timer scanarea_timer; + Timer scanarea_timer; #endif - Timer tribute_timer; + Timer tribute_timer; - Timer proximity_timer; - Timer TaskPeriodic_Timer; - Timer charm_update_timer; - Timer rest_timer; - Timer charm_class_attacks_timer; - Timer charm_cast_timer; - Timer qglobal_purge_timer; - Timer TrackingTimer; - Timer RespawnFromHoverTimer; - Timer merc_timer; + Timer proximity_timer; + Timer TaskPeriodic_Timer; + Timer charm_update_timer; + Timer rest_timer; + Timer charm_class_attacks_timer; + Timer charm_cast_timer; + Timer qglobal_purge_timer; + Timer TrackingTimer; + Timer RespawnFromHoverTimer; + Timer merc_timer; - float proximity_x; - float proximity_y; - float proximity_z; + float proximity_x; + float proximity_y; + float proximity_z; - void BulkSendInventoryItems(); + void BulkSendInventoryItems(); faction_map factionvalues; uint32 tribute_master_id; FILE *SQL_log; - uint32 max_AAXP; - uint32 staminacount; - AA_Array* aa[MAX_PP_AA_ARRAY]; //this list contains pointers into our player profile + uint32 max_AAXP; + uint32 staminacount; + AA_Array* aa[MAX_PP_AA_ARRAY]; //this list contains pointers into our player profile std::map aa_points; bool npcflag; uint8 npclevel; @@ -1454,14 +1457,14 @@ private: bool zoning; bool tgb; bool instalog; - int32 last_reported_mana; - int32 last_reported_endur; + int32 last_reported_mana; + int32 last_reported_endur; unsigned int AggroCount; // How many mobs are aggro on us. - unsigned int RestRegenHP; - unsigned int RestRegenMana; - unsigned int RestRegenEndurance; + unsigned int RestRegenHP; + unsigned int RestRegenMana; + unsigned int RestRegenEndurance; bool EngagedRaidTarget; uint32 SavedRaidRestTimer; @@ -1479,7 +1482,7 @@ private: uint32 AttemptedMessages; EQClientVersion ClientVersion; - uint32 ClientVersionBit; + uint32 ClientVersionBit; int XPRate; @@ -1494,18 +1497,18 @@ private: //Connecting debug code. enum { //connecting states, used for debugging only - NoPacketsReceived, //havent gotten anything - //this is the point where the client changes to the loading screen - ReceivedZoneEntry, //got the first packet, loading up PP - PlayerProfileLoaded, //our DB work is done, sending it - ZoneInfoSent, //includes PP, tributes, tasks, spawns, time and weather - //this is the point where the client shows a status bar zoning in - NewZoneRequested, //received and sent new zone request - ClientSpawnRequested, //client sent ReqClientSpawn - ZoneContentsSent, //objects, doors, zone points - ClientReadyReceived, //client told us its ready, send them a bunch of crap like guild MOTD, etc - //this is the point where the client releases the mouse - ClientConnectFinished //client finally moved to finished state, were done here + NoPacketsReceived, //havent gotten anything + //this is the point where the client changes to the loading screen + ReceivedZoneEntry, //got the first packet, loading up PP + PlayerProfileLoaded, //our DB work is done, sending it + ZoneInfoSent, //includes PP, tributes, tasks, spawns, time and weather + //this is the point where the client shows a status bar zoning in + NewZoneRequested, //received and sent new zone request + ClientSpawnRequested, //client sent ReqClientSpawn + ZoneContentsSent, //objects, doors, zone points + ClientReadyReceived, //client told us its ready, send them a bunch of crap like guild MOTD, etc + //this is the point where the client releases the mouse + ClientConnectFinished //client finally moved to finished state, were done here } conn_state; void ReportConnectingState(); @@ -1513,8 +1516,8 @@ private: bool PendingGuildInvitation; int PendingRezzXP; uint32 PendingRezzDBID; - uint16 PendingRezzSpellID; // Only used for resurrect while hovering. - std::string PendingRezzCorpseName; // Only used for resurrect while hovering. + uint16 PendingRezzSpellID; // Only used for resurrect while hovering. + std::string PendingRezzCorpseName; // Only used for resurrect while hovering. std::set PlayerBlockedBuffs; std::set PetBlockedBuffs; @@ -1538,4 +1541,3 @@ private: }; #endif - diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3cd0bb157..15c4f3497 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3034,47 +3034,23 @@ void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) 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. - // + // Some clients this seems to nuke the charm text (ex. Adventurer's Stone) - if (app->size != sizeof(AugmentInfo_Struct)) - { + if (app->size != sizeof(AugmentInfo_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", sizeof(AugmentInfo_Struct), app->size); - DumpPacket(app); - return; } + AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*)app->pBuffer; - - char *outstring = nullptr; - const Item_Struct * item = database.GetItem(AugInfo->itemid); - if (item) - { - MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - - BookText_Struct *out = (BookText_Struct *)outapp->pBuffer; - - out->window = AugInfo->window; - - out->type = 2; - - out->invslot = 0; - - strcpy(out->booktext, outstring); - - safe_delete_array(outstring); - - FastQueuePacket(&outapp); + if (item) { + strn0cpy(AugInfo->augment_info, item->Name, 64); + AugInfo->itemid = 0; + QueuePacket(app); } } @@ -3842,19 +3818,23 @@ void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) { - - if (app->size <= 5) + // this sends unclean mob name, so capped at 64 + // a_boat006 + if (app->size <= 5 || app->size > 64) { + LogFile->write(EQEMuLog::Error, "Size mismatch in OP_BoardBoad. Expected greater than 5 less than 64, got %i", app->size); + DumpPacket(app); return; + } - char *boatname; - boatname = new char[app->size - 3]; - memset(boatname, 0, app->size - 3); - memcpy(boatname, app->pBuffer, app->size - 4); + char boatname[64]; + memcpy(boatname, app->pBuffer, app->size); + boatname[63] = '\0'; 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); + if (!boat || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) + return; + BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat + return; } @@ -9719,17 +9699,13 @@ void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); // Handle the dismiss here... - if (GetMercID()) { - Merc* merc = GetMerc(); - - if (merc) { - if (CheckCanDismissMerc()) { - merc->Dismiss(); - } + Merc* merc = GetMerc(); + if (merc) { + if (CheckCanDismissMerc()) { + merc->Dismiss(); } } - //SendMercMerchantResponsePacket(10); } void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) @@ -9769,21 +9745,22 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) return; } - if (RuleB(Mercs, ChargeMercPurchaseCost)) { - uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold - TakeMoneyFromPP(cost, true); - } - // Set time remaining to max on Hire GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - if (merc) { + if (merc) + { SpawnMerc(merc, true); merc->Save(); + if (RuleB(Mercs, ChargeMercPurchaseCost)) { + uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold + TakeMoneyFromPP(cost, true); + } + // 0 is approved hire request SendMercMerchantResponsePacket(0); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c6265accf..e26dcd2e3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -78,7 +78,8 @@ bool Client::Process() { if(Connected() || IsLD()) { // try to send all packets that weren't sent before - if(!IsLD() && zoneinpacket_timer.Check()){ + if(!IsLD() && zoneinpacket_timer.Check()) + { SendAllPackets(); } @@ -145,7 +146,9 @@ bool Client::Process() { if(mana_timer.Check()) SendManaUpdatePacket(); - if(dead && dead_timer.Check()) { + + 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; @@ -176,14 +179,16 @@ bool Client::Process() { if(TaskPeriodic_Timer.Check() && taskstate) taskstate->TaskPeriodicChecks(this); - if(linkdead_timer.Check()){ + if(linkdead_timer.Check()) + { + LeaveGroup(); Save(); if (GetMerc()) { GetMerc()->Save(); GetMerc()->Depop(); } - LeaveGroup(); + Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { @@ -192,7 +197,8 @@ bool Client::Process() { return false; //delete client } - if (camp_timer.Check()) { + if (camp_timer.Check()) + { LeaveGroup(); Save(); if (GetMerc()) @@ -228,20 +234,22 @@ bool Client::Process() { } else { if(!ApplyNextBardPulse(bardsong, song_target, bardsong_slot)) InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); -// SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); + //SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); } } if(GetMerc()) { - UpdateMercTimer(); + UpdateMercTimer(); } if(GetMercInfo().MercTemplateID != 0 && GetMercInfo().IsSuspended) { - if(p_timers.Expired(&database, pTimerMercSuspend, false)) { - CheckMercSuspendTimer(); - } + //CheckMercSuspendTimer(); + if(p_timers.Expired(&database, pTimerMercSuspend, false)) + { + CheckMercSuspendTimer(); + } } if(IsAIControlled()) @@ -715,16 +723,13 @@ bool Client::Process() { } #endif - if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) { + if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) + { //client logged out or errored out //ResetTrade(); if (client_state != CLIENT_KICKED) { Save(); } - if (GetMerc()) - { - GetMerc()->Depop(); - } client_state = CLIENT_LINKDEAD; if (zoning || instalog || GetGM()) @@ -732,23 +737,32 @@ bool Client::Process() { Group *mygroup = GetGroup(); if (mygroup) { - if (!zoning) { + if (!zoning) + { entity_list.MessageGroup(this, true, 15, "%s logged out.", GetName()); - mygroup->DelMember(this); - } else { + LeaveGroup(); + } + else + { entity_list.MessageGroup(this, true, 15, "%s left the zone.", GetName()); mygroup->MemberZoned(this); + if (GetMerc() && GetMerc()->HasGroup() && GetMerc()->GetGroup() == mygroup) + { + mygroup->DelMember(GetMerc()); + } } } Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { - if (!zoning) { + if (!zoning) + { //entity_list.MessageGroup(this,true,15,"%s logged out.",GetName()); - //mygroup->DelMember(this); myraid->MemberZoned(this); - } else { + } + else + { //entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName()); myraid->MemberZoned(this); } @@ -774,8 +788,14 @@ bool Client::Process() { /* Just a set of actions preformed all over in Client::Process */ void Client::OnDisconnect(bool hard_disconnect) { - if(hard_disconnect) { - LeaveGroup(); + if(hard_disconnect) + { + LeaveGroup(); + if (GetMerc()) + { + GetMerc()->Save(); + GetMerc()->Depop(); + } Raid *MyRaid = entity_list.GetRaidByClient(this); if (MyRaid) @@ -791,7 +811,8 @@ void Client::OnDisconnect(bool hard_disconnect) { } Mob *Other = trade->With(); - if(Other) { + if(Other) + { mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); FinishTrade(this); diff --git a/zone/groups.cpp b/zone/groups.cpp index ae51f51c9..fb1f4204e 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1069,14 +1069,29 @@ void Group::GroupMessage_StringID(Mob* sender, uint32 type, uint32 string_id, co void Client::LeaveGroup() { Group *g = GetGroup(); - if(g) { + if(g) + { if(g->GroupCount() < 3) + { g->DisbandGroup(); + } else + { g->DelMember(this); - } else { + if (GetMerc() && GetMerc()->HasGroup() && GetMerc()->GetGroup() == g) + { + g->DelMember(GetMerc()); + } + } + } + else + { //force things a little database.SetGroupID(GetName(), 0, CharacterID()); + if (GetMerc()) + { + database.SetGroupID(GetMerc()->GetName(), 0, CharacterID()); + } } isgrouped = false; diff --git a/zone/merc.cpp b/zone/merc.cpp index e8c2b7ed9..4984fcca6 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -17,7 +17,7 @@ extern volatile bool ZoneLoaded; Merc::Merc(const NPCType* d, float x, float y, float z, float heading) - : NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000) +: NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000) { base_hp = d->max_hp; base_mana = d->Mana; @@ -75,7 +75,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); - entity_list.RemoveMerc(this->GetID()); + //entity_list.RemoveMerc(this->GetID()); UninitializeBuffSlots(); } @@ -117,50 +117,50 @@ void Merc::GetMercSize() { 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; + 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->size = MercSize; @@ -277,9 +277,9 @@ void Merc::CalcItemBonuses(StatBonuses* newbon) { //Power Source Slot /*if (GetClientVersion() >= EQClientSoF) { - const ItemInst* inst = m_inv[MainPowerSource]; - if(inst) - AddItemBonuses(inst, newbon); + const ItemInst* inst = m_inv[MainPowerSource]; + if(inst) + AddItemBonuses(inst, newbon); }*/ // Caps @@ -635,14 +635,14 @@ int Merc::GroupLeadershipAAHealthEnhancement() switch(g->GetLeadershipAA(groupAAHealthEnhancement)) { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; } return 0; @@ -657,14 +657,14 @@ int Merc::GroupLeadershipAAManaEnhancement() switch(g->GetLeadershipAA(groupAAManaEnhancement)) { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; } return 0; @@ -679,14 +679,14 @@ int Merc::GroupLeadershipAAHealthRegeneration() switch(g->GetLeadershipAA(groupAAHealthRegeneration)) { - case 0: - return 0; - case 1: - return 4; - case 2: - return 6; - case 3: - return 8; + case 0: + return 0; + case 1: + return 4; + case 2: + return 6; + case 3: + return 8; } return 0; @@ -701,18 +701,18 @@ int Merc::GroupLeadershipAAOffenseEnhancement() switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) { - case 0: - return 0; - case 1: - return 10; - case 2: - return 19; - case 3: - return 28; - case 4: - return 34; - case 5: - return 40; + case 0: + return 0; + case 1: + return 10; + case 2: + return 19; + case 3: + return 28; + case 4: + return 34; + case 5: + return 40; } return 0; } @@ -812,7 +812,7 @@ int32 Merc::CalcCHA() { //The AA multipliers are set to be 5, but were 2 on WR //The resistant discipline which I think should be here is implemented //in Mob::ResistSpell -int32 Merc::CalcMR() +int32 Merc::CalcMR() { MR = _baseMR + itembonuses.MR + spellbonuses.MR + aabonuses.MR; @@ -822,7 +822,7 @@ int32 Merc::CalcMR() return(MR); } -int32 Merc::CalcFR() +int32 Merc::CalcFR() { FR = _baseFR + itembonuses.FR + spellbonuses.FR + aabonuses.FR; @@ -832,7 +832,7 @@ int32 Merc::CalcFR() return(FR); } -int32 Merc::CalcDR() +int32 Merc::CalcDR() { DR = _baseDR + itembonuses.DR + spellbonuses.DR + aabonuses.DR; @@ -842,7 +842,7 @@ int32 Merc::CalcDR() return(DR); } -int32 Merc::CalcPR() +int32 Merc::CalcPR() { PR = _basePR + itembonuses.PR + spellbonuses.PR + aabonuses.PR; @@ -852,7 +852,7 @@ int32 Merc::CalcPR() return(PR); } -int32 Merc::CalcCR() +int32 Merc::CalcCR() { CR = _baseCR + itembonuses.CR + spellbonuses.CR + aabonuses.CR; @@ -862,7 +862,7 @@ int32 Merc::CalcCR() return(CR); } -int32 Merc::CalcCorrup() +int32 Merc::CalcCorrup() { Corrup = _baseCorrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; @@ -905,7 +905,7 @@ int32 Merc::CalcMaxHP() { //but the actual effect sent on live causes the client //to apply it to (basehp + itemhp).. I will oblige to the client's whims over //the aa description - nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability + nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue max_hp += spellbonuses.HP + aabonuses.HP; @@ -936,20 +936,20 @@ int32 Merc::CalcMaxMana() { switch(GetCasterClass()) { - case 'I': - case 'W': { - max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); - break; - } - case 'N': { - max_mana = 0; - break; - } - default: { - LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); - max_mana = 0; - break; - } + case 'I': + case 'W': { + max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); + break; + } + case 'N': { + max_mana = 0; + break; + } + default: { + LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + max_mana = 0; + break; + } } if (max_mana < 0) { max_mana = 0; @@ -1031,12 +1031,12 @@ int32 Merc::CalcManaRegenCap() int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; switch(GetCasterClass()) { - case 'I': - cap += (itembonuses.HeroicINT / 25); - break; - case 'W': - cap += (itembonuses.HeroicWIS / 25); - break; + case 'I': + cap += (itembonuses.HeroicINT / 25); + break; + case 'W': + cap += (itembonuses.HeroicWIS / 25); + break; } return (cap * RuleI(Character, ManaRegenMultiplier) / 100); @@ -1177,7 +1177,7 @@ void Merc::DoEnduranceUpkeep() { has_effect = true; if(cost_redux > 0) { if(upkeep <= cost_redux) - continue; //reduced to 0 + continue; //reduced to 0 upkeep -= cost_redux; } if((upkeep+upkeep_sum) > GetEndurance()) { @@ -1252,112 +1252,28 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.lfg = 0; ns->spawn.anon = 0; ns->spawn.gm = 0; - ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild - ns->spawn.is_npc = 1; // 0=no, 1=yes + ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild + ns->spawn.is_npc = 1; // 0=no, 1=yes ns->spawn.is_pet = 0; ns->spawn.guildrank = 0; ns->spawn.showhelm = 1; ns->spawn.flymode = 0; ns->spawn.size = 0; - ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse + ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse ns->spawn.IsMercenary = 1; - /*const Item_Struct* item = 0; - const ItemInst* inst = 0; - uint32 spawnedmercid = 0; - spawnedmercid = this->GetID(); - - inst = GetBotItem(SLOT_HANDS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_HANDS] = item->Material; - ns->spawn.colors[MATERIAL_HANDS].color = GetEquipmentColor(MATERIAL_HANDS); + unsigned int i; + //should not include 21 (SLOT_AMMO) + for (i = 0; i < MainAmmo; i++) { + if(equipment[i] == 0) + continue; + const Item_Struct* item = database.GetItem(equipment[i]); + if(item) + { + ns->spawn.equipment[i] = item->Material; + ns->spawn.colors[i].color = item->Color; } } - - inst = GetBotItem(SLOT_HEAD); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_HEAD] = item->Material; - ns->spawn.colors[MATERIAL_HEAD].color = GetEquipmentColor(MATERIAL_HEAD); - } - } - - inst = GetBotItem(SLOT_ARMS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_ARMS] = item->Material; - ns->spawn.colors[MATERIAL_ARMS].color = GetEquipmentColor(MATERIAL_ARMS); - } - } - - inst = GetBotItem(SLOT_BRACER01); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_BRACER] = item->Material; - ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); - } - } - - inst = GetBotItem(SLOT_BRACER02); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_BRACER] = item->Material; - ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); - } - } - - inst = GetBotItem(SLOT_CHEST); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_CHEST] = item->Material; - ns->spawn.colors[MATERIAL_CHEST].color = GetEquipmentColor(MATERIAL_CHEST); - } - } - - inst = GetBotItem(SLOT_LEGS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_LEGS] = item->Material; - ns->spawn.colors[MATERIAL_LEGS].color = GetEquipmentColor(MATERIAL_LEGS); - } - } - - inst = GetBotItem(SLOT_FEET); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_FEET] = item->Material; - ns->spawn.colors[MATERIAL_FEET].color = GetEquipmentColor(MATERIAL_FEET); - } - } - - inst = GetBotItem(SLOT_PRIMARY); - if(inst) { - item = inst->GetItem(); - if(item) { - if(strlen(item->IDFile) > 2) - ns->spawn.equipment[MATERIAL_PRIMARY] = atoi(&item->IDFile[2]); - ns->spawn.colors[MATERIAL_PRIMARY].color = GetEquipmentColor(MATERIAL_PRIMARY); - } - } - - inst = GetBotItem(SLOT_SECONDARY); - if(inst) { - item = inst->GetItem(); - if(item) { - if(strlen(item->IDFile) > 2) - ns->spawn.equipment[MATERIAL_SECONDARY] = atoi(&item->IDFile[2]); - ns->spawn.colors[MATERIAL_SECONDARY].color = GetEquipmentColor(MATERIAL_SECONDARY); - } - }*/ } } @@ -1371,25 +1287,24 @@ bool Merc::Process() if (GetDepop()) { - SetMercCharacterID(0); - SetOwnerID(0); + //SetMercCharacterID(0); + //SetOwnerID(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 + //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; + return false; } - if (HasGroup() && GetFollowID() == 0) { + if (HasGroup() && GetMercOwner() && GetFollowID() == 0) { SetFollowID(GetMercOwner()->GetID()); } - SpellProcess(); if(tic_timer.Check()) @@ -1448,7 +1363,7 @@ bool Merc::IsMercCasterCombatRange(Mob *target) { range *= range; - // half the max so the bot doesn't always stop at max range to allow combat movement + // half the max so the merc doesn't always stop at max range to allow combat movement range *= .5; float targetDistance = DistNoRootNoZ(*target); @@ -1470,19 +1385,30 @@ void Merc::AI_Process() { return; // A bot wont start its AI if not grouped - if(!GetOwner() || !HasGroup()) { + if(!HasGroup()) { return; } - if(GetAppearance() == eaDead) - return; - Mob* MercOwner = GetOwner(); - // The bots need an owner - if(!MercOwner) + if(GetAppearance() == eaDead) + { + if(!MercOwner) + { + Depop(); + } return; + } + // The merc needs an owner + if(!MercOwner) { + //SetTarget(0); + //SetOwnerID(0); + // TODO: Need to wait and try casting rez if merc is a healer with a dead owner + return; + } + + /* try { if(MercOwner->CastToClient()->IsDead()) { SetTarget(0); @@ -1495,6 +1421,7 @@ void Merc::AI_Process() { SetOwnerID(0); return; } + */ if(check_target_timer.Check()) { CheckHateList(); @@ -1605,8 +1532,10 @@ void Merc::AI_Process() { atCombatRange = true; } - if(atCombatRange) { - if(IsMoving()) { + if(atCombatRange) + { + if(IsMoving()) + { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetRunAnimSpeed(0); @@ -1617,25 +1546,30 @@ void Merc::AI_Process() { } } - if(AImovement_timer->Check()) { - if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) { + if(AImovement_timer->Check()) + { + if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) + { // Move the rogue to behind the mob float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { + if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) + { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; } } - else if(!IsMoving() && GetClass() != ROGUE && (DistNoRootNoZ(*GetTarget()) < GetTarget()->GetSize())) { + else if(!IsMoving() && GetClass() != ROGUE && (DistNoRootNoZ(*GetTarget()) < GetTarget()->GetSize())) + { // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { + if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) + { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; } @@ -1647,26 +1581,28 @@ void Merc::AI_Process() { SendPosition(); } - if(!IsMercCaster() && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) { + if(!IsMercCaster() && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) + { // we can't fight if we don't have a target, are stun/mezzed or dead.. // Stop attacking if the target is enraged if(IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) return; //TODO: Implement Stances. /*if(GetBotStance() == BotStancePassive) - return;*/ + return;*/ // First, special attack per class (kick, backstab etc..) DoClassAttacks(GetTarget()); //try main hand first - if(attack_timer.Check()) { + if(attack_timer.Check()) + { Attack(GetTarget(), MainPrimary); bool tripleSuccess = false; - if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) { - + if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) + { if(GetOwner()) { Attack(GetTarget(), MainPrimary, true); } @@ -1698,90 +1634,95 @@ void Merc::AI_Process() { int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; if (GetTarget() && ExtraAttackChanceBonus) { - if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) - { - Attack(GetTarget(), MainPrimary, false); - } - } + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(GetTarget(), MainPrimary, false); + } + } } // TODO: Do mercs berserk? Find this out on live... //if (GetClass() == WARRIOR || GetClass() == BERSERKER) { - // if(GetHP() > 0 && !berserk && this->GetHPRatio() < 30) { - // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); - // this->berserk = true; - // } - // if (berserk && this->GetHPRatio() > 30) { - // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); - // this->berserk = false; - // } + // if(GetHP() > 0 && !berserk && this->GetHPRatio() < 30) { + // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); + // this->berserk = true; + // } + // if (berserk && this->GetHPRatio() > 30) { + // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); + // this->berserk = false; + // } //} //now off hand - if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) { + if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) + { + int weapontype = 0; // No weapon type + bool bIsFist = true; - int weapontype = 0; // No weapon type - bool bIsFist = true; + if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) + { + float DualWieldProbability = 0.0f; - if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) { - float DualWieldProbability = 0.0f; + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + float random = MakeRandomFloat(0, 1); - float random = MakeRandomFloat(0, 1); + // Max 78% of DW + if (random < DualWieldProbability) + { + Attack(GetTarget(), MainSecondary); // Single attack with offhand - if (random < DualWieldProbability){ // Max 78% of DW - - Attack(GetTarget(), MainSecondary); // Single attack with offhand - - if( CanThisClassDoubleAttack()) { - if(GetTarget() && GetTarget()->GetHP() > -10) - Attack(GetTarget(), MainSecondary); // Single attack with offhand - } + if(CanThisClassDoubleAttack()) { + if(GetTarget() && GetTarget()->GetHP() > -10) + Attack(GetTarget(), MainSecondary); // Single attack with offhand } } } } - }// end in combat range - else { - if(GetTarget()->IsFeared() && !spellend_timer.Enabled()){ - // This is a mob that is fleeing either because it has been feared or is low on hitpoints - //TODO: Implement Stances. - //if(GetStance() != MercStancePassive) - AI_PursueCastCheck(); - } - - if (AImovement_timer->Check()) { - if(!IsRooted()) { - mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); - CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); - return; - } - - if(IsMoving()) - SendPosUpdate(); - else - SendPosition(); - } - } // end not in combat range - - if(!IsMoving() && !spellend_timer.Enabled()) { - - //TODO: Implement Stances. - //if(GetStance() == MercStancePassive) - // return; - - if(AI_EngagedCastCheck()) { - MercMeditate(false); - } - else if(GetArchetype() == ARCHETYPE_CASTER) - MercMeditate(true); } - } // end IsEngaged() - else { + } + else + { + if(GetTarget()->IsFeared() && !spellend_timer.Enabled()) { + // This is a mob that is fleeing either because it has been feared or is low on hitpoints + //TODO: Implement Stances. + //if(GetStance() != MercStancePassive) + AI_PursueCastCheck(); + } + + if (AImovement_timer->Check()) + { + if(!IsRooted()) { + mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); + CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); + return; + } + + if(IsMoving()) + SendPosUpdate(); + else + SendPosition(); + } + } // end not in combat range + + if(!IsMoving() && !spellend_timer.Enabled()) + { + //TODO: Implement Stances. + //if(GetStance() == MercStancePassive) + // return; + + if(AI_EngagedCastCheck()) { + MercMeditate(false); + } + else if(GetArchetype() == ARCHETYPE_CASTER) + MercMeditate(true); + } + } + else + { // Not engaged in combat SetTarget(0); SetHatedCount(0); @@ -1791,8 +1732,8 @@ void Merc::AI_Process() { if(!check_target_timer.Enabled()) check_target_timer.Start(2000, false); - if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) { - + if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) + { //TODO: Implement passive stances. //if(GetStance() != MercStancePassive) { if(!AI_IdleCastCheck() && !IsCasting()) { @@ -1802,11 +1743,14 @@ void Merc::AI_Process() { } } - if(AImovement_timer->Check()) { - if(GetFollowID()) { + if(AImovement_timer->Check()) + { + if(GetFollowID()) + { Mob* follow = entity_list.GetMob(GetFollowID()); - if(follow) { + if(follow) + { float dist = DistNoRoot(*follow); float speed = GetRunspeed(); @@ -1867,9 +1811,9 @@ bool Merc::AI_EngagedCastCheck() { bool result = false; bool failedToCast = false; - if (GetTarget() && AIautocastspell_timer->Check(false)) { - - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + if (GetTarget() && AIautocastspell_timer->Check(false)) + { + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. mlog(AI__SPELLS, "Engaged autocast check triggered (MERCS)."); @@ -1877,36 +1821,36 @@ bool Merc::AI_EngagedCastCheck() { switch(mercClass) { - case TANK: + case TANK: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + failedToCast = true; + } + } + break; + case HEALER: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), MercAISpellRange, SpellType_Heal)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Buff), MercAISpellRange, SpellType_Buff)) { + failedToCast = true; + } + } + break; + case MELEEDPS: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { failedToCast = true; } } - break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Buff), MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } + } + break; + case CASTERDPS: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + failedToCast = true; } - break; - case MELEEDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { - failedToCast = true; - } - } - } - break; - case CASTERDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - break; + } + break; } if(!AIautocastspell_timer->Enabled()) { @@ -1928,36 +1872,36 @@ bool Merc::AI_IdleCastCheck() { #if MobAI_DEBUG_Spells >= 25 std::cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << std::endl; #endif - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. //Ok, IdleCastCheck depends of class. int8 mercClass = GetClass(); switch(mercClass) { - case TANK: - failedToCast = true; + case TANK: + failedToCast = true; break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Cure)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Resurrect)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } + case HEALER: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Cure)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Resurrect)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { + failedToCast = true; } } } - result = true; - break; - case MELEEDPS: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } - break; - case CASTERDPS: + } + result = true; + break; + case MELEEDPS: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { failedToCast = true; - break; + } + break; + case CASTERDPS: + failedToCast = true; + break; } if(!AIautocastspell_timer->Enabled()) @@ -1998,17 +1942,17 @@ bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, if(caster->HasGroup()) { if( mercCasterClass == HEALER) { - if( iSpellTypes == SpellType_Heal ) { + if( iSpellTypes == SpellType_Heal ) { if(caster->AICastSpell(100, SpellType_Heal)) return true; } - if( iSpellTypes == SpellType_Cure ) { + if( iSpellTypes == SpellType_Cure ) { if(caster->AICastSpell(100, SpellType_Cure)) return true; } - if( iSpellTypes == SpellType_Resurrect ) { + if( iSpellTypes == SpellType_Resurrect ) { if(caster->AICastSpell(100, SpellType_Resurrect)) return true; } @@ -2047,11 +1991,11 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon dist2 = DistNoRoot(*tar); if (((((spells[spellid].targettype==ST_GroupTeleport && mercSpell.type==SpellType_Heal) - || spells[spellid].targettype==ST_AECaster - || spells[spellid].targettype==ST_Group - || spells[spellid].targettype==ST_AEBard) - && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) - || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) + || spells[spellid].targettype==ST_AECaster + || spells[spellid].targettype==ST_Group + || spells[spellid].targettype==ST_AEBard) + && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) + || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) { SetRunAnimSpeed(0); SendPosition(); @@ -2093,7 +2037,7 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { int8 mercClass = GetClass(); uint8 mercLevel = GetLevel(); - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. + bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. bool castedSpell = false; bool isDiscipline = false; @@ -2111,417 +2055,417 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { switch(mercClass) { - case TANK: - case MELEEDPS: - isDiscipline = true; + case TANK: + case MELEEDPS: + isDiscipline = true; break; - default: - isDiscipline = false; + default: + isDiscipline = false; break; } switch (iSpellTypes) { - case SpellType_Heal: { - Mob* tar = nullptr; - int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); - int8 checkHPR = IsEngaged() ? 95 : 99; - int8 checkPetHPR = IsEngaged() ? 95 : 99; + case SpellType_Heal: { + Mob* tar = nullptr; + int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); + int8 checkHPR = IsEngaged() ? 95 : 99; + int8 checkPetHPR = IsEngaged() ? 95 : 99; - //todo: check stance to determine healing spell selection + //todo: check stance to determine healing spell selection - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - int8 hpr = (int8)g->members[i]->GetHPRatio(); + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + int8 hpr = (int8)g->members[i]->GetHPRatio(); - if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < checkHPR) { - if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { - tar = g->members[i]->GetPet(); - checkPetHPR = g->members[i]->GetPet()->GetHPRatio() + 25; - } - } + if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < checkHPR) { + if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { + tar = g->members[i]->GetPet(); + checkPetHPR = g->members[i]->GetPet()->GetHPRatio() + 25; + } + } - if(hpr > checkHPR) { - continue; - } + if(hpr > checkHPR) { + continue; + } - if(IsEngaged() && (g->members[i]->GetClass() == NECROMANCER && hpr >= 50) - || (g->members[i]->GetClass() == SHAMAN && hpr >= 80)) { - //allow necros to lifetap & shaman to canni without wasting mana - continue; - } + if(IsEngaged() && (g->members[i]->GetClass() == NECROMANCER && hpr >= 50) + || (g->members[i]->GetClass() == SHAMAN && hpr >= 80)) { + //allow necros to lifetap & shaman to canni without wasting mana + continue; + } - if(hpr < checkHPR && g->members[i] == GetMercOwner()) { - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; //check owner first - } - else if(hpr < checkHPR && g->HasRole(g->members[i], RoleTank)){ - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; - } - else if( hpr < checkHPR && (!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR)))) { - tar = g->members[i]; - } - } - } - - if(numToHeal > 2) { - selectedMercSpell = GetBestMercSpellForGroupHeal(this); - } - - if(tar && selectedMercSpell.spellid == 0) { - if(tar->GetHPRatio() < 15) { - //check for very fast heals first (casting time < 1 s) - selectedMercSpell = GetBestMercSpellForVeryFastHeal(this); - - //check for fast heals next (casting time < 2 s) - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForFastHeal(this); - } - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 35) { - //check for fast heals next (casting time < 2 s) - selectedMercSpell = GetBestMercSpellForFastHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 80) { - selectedMercSpell = GetBestMercSpellForPercentageHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else { - //check for heal over time. if not present, try it first - if(!tar->FindType(SE_HealOverTime)) { - selectedMercSpell = GetBestMercSpellForHealOverTime(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - } - } - - if(selectedMercSpell.spellid > 0) { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - - if(castedSpell) { - char* gmsg = 0; - - if(tar != this) { - //we don't need spam of bots healing themselves - MakeAnyLenString(&gmsg, "Casting %s on %s.", spells[selectedMercSpell.spellid].name, tar->GetCleanName()); - if(gmsg) - { - MercGroupSay(this, gmsg); - safe_delete_array(gmsg); - } - } - } - - break; - } - case SpellType_Root: { - break; - } - case SpellType_Buff: { - - if(GetClass() == HEALER && GetManaRatio() < 50) { - return false; //mercs buff when Mana > 50% - } - - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_Buff); - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || - spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || - spells[selectedMercSpell.spellid].targettype == ST_Self))) { - continue; - } - - if(spells[selectedMercSpell.spellid].targettype == ST_Self) { - if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - if( this->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; + if(hpr < checkHPR && g->members[i] == GetMercOwner()) { + if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) + tar = g->members[i]; //check owner first + } + else if(hpr < checkHPR && g->HasRole(g->members[i], RoleTank)){ + if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) + tar = g->members[i]; + } + else if( hpr < checkHPR && (!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR)))) { + tar = g->members[i]; + } + } } - uint32 TempDontBuffMeBeforeTime = this->DontBuffMeBefore(); + if(numToHeal > 2) { + selectedMercSpell = GetBestMercSpellForGroupHeal(this); + } + + if(tar && selectedMercSpell.spellid == 0) { + if(tar->GetHPRatio() < 15) { + //check for very fast heals first (casting time < 1 s) + selectedMercSpell = GetBestMercSpellForVeryFastHeal(this); + + //check for fast heals next (casting time < 2 s) + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForFastHeal(this); + } + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else if (tar->GetHPRatio() < 35) { + //check for fast heals next (casting time < 2 s) + selectedMercSpell = GetBestMercSpellForFastHeal(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else if (tar->GetHPRatio() < 80) { + selectedMercSpell = GetBestMercSpellForPercentageHeal(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else { + //check for heal over time. if not present, try it first + if(!tar->FindType(SE_HealOverTime)) { + selectedMercSpell = GetBestMercSpellForHealOverTime(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + } + } + + if(selectedMercSpell.spellid > 0) { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); + } + + if(castedSpell) { + char* gmsg = 0; + + if(tar != this) { + //we don't need spam of bots healing themselves + MakeAnyLenString(&gmsg, "Casting %s on %s.", spells[selectedMercSpell.spellid].name, tar->GetCleanName()); + if(gmsg) + { + MercGroupSay(this, gmsg); + safe_delete_array(gmsg); + } + } + } + + break; + } + case SpellType_Root: { + break; + } + case SpellType_Buff: { + + if(GetClass() == HEALER && GetManaRatio() < 50) { + return false; //mercs buff when Mana > 50% + } + + std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_Buff); + + for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { + MercSpell selectedMercSpell = *itr; + + if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || + spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || + spells[selectedMercSpell.spellid].targettype == ST_Self))) { + continue; + } + + if(spells[selectedMercSpell.spellid].targettype == ST_Self) { + if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + if( this->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { + continue; + } + + uint32 TempDontBuffMeBeforeTime = this->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore()) + this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + } + else { + for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i]) { + Mob* tar = g->members[i]; + + if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + if( tar->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { + continue; + } + + uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) + tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + + if(!castedSpell && tar->GetPet()) { + + //don't cast group spells on pets + if(IsGroupSpell(selectedMercSpell.spellid) + || spells[selectedMercSpell.spellid].targettype == ST_Group + || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { + continue; + } + + if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore()) + tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + } + } + } + } + } + break; + } + case SpellType_Nuke: { + switch(mercClass) + { + case TANK: + //check for taunt + if(CheckAETaunt()) { + if(MERC_DEBUG > 0) + GetOwner()->Message(7, "AE Taunting"); + //get AE taunt + selectedMercSpell = GetBestMercSpellForAETaunt(this); + } + + if(selectedMercSpell.spellid == 0 && CheckTaunt()) { + //get taunt + selectedMercSpell = GetBestMercSpellForTaunt(this); + } + + //get hate disc + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForHate(this); + } + + break; + case HEALER: + break; + case MELEEDPS: + break; + case CASTERDPS: + Mob* tar = GetTarget(); + + selectedMercSpell = GetBestMercSpellForAENuke(this, tar); + + if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) { + uint8 stunChance = 15; + if(MakeRandomInt(1, 100) <= stunChance) { + selectedMercSpell = GetBestMercSpellForStun(this); + } + } + + if(selectedMercSpell.spellid == 0) { + uint8 lureChance = 25; + if(MakeRandomInt(1, 100) <= lureChance) { + selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar); + } + } + + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForNuke(this); + } + + break; + } if(selectedMercSpell.spellid > 0) { if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID()); } else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore()) - this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1); } } - } - } - else { - for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i]) { - Mob* tar = g->members[i]; + + break; + } + case SpellType_InCombatBuff: { + std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_InCombatBuff); + Mob* tar = this; + + for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { + MercSpell selectedMercSpell = *itr; + + if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { + continue; + } + + if(spells[selectedMercSpell.spellid].skill == SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { + if(!hidden) { + continue; + } + } if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - if( tar->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; - } - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) - tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); - } - } - } - - if(!castedSpell && tar->GetPet()) { - - //don't cast group spells on pets - if(IsGroupSpell(selectedMercSpell.spellid) - || spells[selectedMercSpell.spellid].targettype == ST_Group - || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { - continue; - } - - if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); if(selectedMercSpell.spellid > 0) { if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID()); + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); } else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime); + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1); + } + } + } + } + break; + } + case SpellType_Cure: { + Mob* tar = nullptr; + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + if(GetNeedsCured(g->members[i]) && (g->members[i]->DontCureMeBefore() < Timer::GetCurrentTime())) { + tar = g->members[i]; + } + } + } - if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore()) - tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + if(tar && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 25 : 40, false) > 0) && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 40 : 60, false) > 2)) + { + selectedMercSpell = GetBestMercSpellForCure(this, tar); + + if(selectedMercSpell.spellid == 0) + break; + + uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); + + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, spells[selectedMercSpell.spellid].mana, &TempDontCureMeBeforeTime); + + if(castedSpell) { + if(IsGroupSpell(selectedMercSpell.spellid)){ + + if(this->HasGroup()) { + Group *g = this->GetGroup(); + + if(g) { + for( int i = 0; imembers[i] && !g->members[i]->qglobal) { + if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) + g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); + } + } } } } - } - } - } - } - } - break; - } - case SpellType_Nuke: { - switch(mercClass) - { - case TANK: - //check for taunt - if(CheckAETaunt()) { - if(MERC_DEBUG > 0) - GetOwner()->Message(7, "AE Taunting"); - //get AE taunt - selectedMercSpell = GetBestMercSpellForAETaunt(this); - } - - if(selectedMercSpell.spellid == 0 && CheckTaunt()) { - //get taunt - selectedMercSpell = GetBestMercSpellForTaunt(this); - } - - //get hate disc - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForHate(this); - } - - break; - case HEALER: - break; - case MELEEDPS: - break; - case CASTERDPS: - Mob* tar = GetTarget(); - - selectedMercSpell = GetBestMercSpellForAENuke(this, tar); - - if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) { - uint8 stunChance = 15; - if(MakeRandomInt(1, 100) <= stunChance) { - selectedMercSpell = GetBestMercSpellForStun(this); - } - } - - if(selectedMercSpell.spellid == 0) { - uint8 lureChance = 25; - if(MakeRandomInt(1, 100) <= lureChance) { - selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar); - } - } - - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForNuke(this); - } - - break; - } - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1); - } - } - - break; - } - case SpellType_InCombatBuff: { - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_InCombatBuff); - Mob* tar = this; - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { - continue; - } - - if(spells[selectedMercSpell.spellid].skill == SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { - if(!hidden) { - continue; - } - } - - if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1); - } - } - } - } - break; - } - case SpellType_Cure: { - Mob* tar = nullptr; - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - if(GetNeedsCured(g->members[i]) && (g->members[i]->DontCureMeBefore() < Timer::GetCurrentTime())) { - tar = g->members[i]; - } - } - } - - if(tar && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 25 : 40, false) > 0) && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 40 : 60, false) > 2)) - { - selectedMercSpell = GetBestMercSpellForCure(this, tar); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, spells[selectedMercSpell.spellid].mana, &TempDontCureMeBeforeTime); - - if(castedSpell) { - if(IsGroupSpell(selectedMercSpell.spellid)){ - - if(this->HasGroup()) { - Group *g = this->GetGroup(); - - if(g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } + else { + if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) + tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); } } } - } - else { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - break; - } - case SpellType_Resurrect: { - Corpse *corpse = GetGroupMemberCorpse(); - - if(corpse) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Resurrect); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontRootMeBeforeTime = corpse->DontRootMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, corpse, spells[selectedMercSpell.spellid].mana, &TempDontRootMeBeforeTime); - - //CastSpell(selectedMercSpell.spellid, corpse->GetID(), 1, -1, -1, &TempDontRootMeBeforeTime); - corpse->SetDontRootMeBefore(TempDontRootMeBeforeTime); - } - - break; - } - case SpellType_Escape: { - Mob* tar = GetTarget(); - uint8 hpr = (uint8)GetHPRatio(); - bool mayGetAggro = false; - - if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) { - mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells - - if (mayGetAggro) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape); - - if(selectedMercSpell.spellid == 0) break; + } + case SpellType_Resurrect: { + Corpse *corpse = GetGroupMemberCorpse(); - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - } - } - break; - } + if(corpse) { + selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Resurrect); + + if(selectedMercSpell.spellid == 0) + break; + + uint32 TempDontRootMeBeforeTime = corpse->DontRootMeBefore(); + + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, corpse, spells[selectedMercSpell.spellid].mana, &TempDontRootMeBeforeTime); + + //CastSpell(selectedMercSpell.spellid, corpse->GetID(), 1, -1, -1, &TempDontRootMeBeforeTime); + corpse->SetDontRootMeBefore(TempDontRootMeBeforeTime); + } + + break; + } + case SpellType_Escape: { + Mob* tar = GetTarget(); + uint8 hpr = (uint8)GetHPRatio(); + bool mayGetAggro = false; + + if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) { + mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells + + if (mayGetAggro) { + selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape); + + if(selectedMercSpell.spellid == 0) + break; + + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); + } + } + } + break; + } } } } @@ -2539,11 +2483,11 @@ void Merc::CheckHateList() { if(g) { Mob* MercOwner = GetOwner(); if(MercOwner && MercOwner->GetTarget() && MercOwner->GetTarget()->IsNPC() && (MercOwner->GetTarget()->GetHateAmount(MercOwner) || MercOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(MercOwner->GetTarget())) { - float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); - range = range * range; - if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) { - AddToHateList(MercOwner->GetTarget(), 1); - } + float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); + range = range * range; + if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) { + AddToHateList(MercOwner->GetTarget(), 1); + } } else { std::list npc_list; @@ -2746,26 +2690,26 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { // AA Focus /*if (aabonuses.FocusEffects[type]){ - int16 Total3 = 0; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; + int16 Total3 = 0; + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) - { - aa_AA = this->aa[i]->AA; - aa_value = this->aa[i]->value; - if (aa_AA < 1 || aa_value < 1) - continue; + for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + { + aa_AA = this->aa[i]->AA; + aa_value = this->aa[i]->value; + if (aa_AA < 1 || aa_value < 1) + continue; - Total3 = CalcAAFocus(type, aa_AA, spell_id); - if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { - realTotal3 = Total3; - } - else if (Total3 < 0 && Total3 < realTotal3) { - realTotal3 = Total3; - } - } + Total3 = CalcAAFocus(type, aa_AA, spell_id); + if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { + realTotal3 = Total3; + } + else if (Total3 < 0 && Total3 < realTotal3) { + realTotal3 = Total3; + } + } }*/ if(type == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) @@ -2791,13 +2735,13 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); int chance = RuleI(Spells, BaseCritChance); - chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; + chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; if (chance > 0){ - int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. - if (MakeRandomInt(1,100) <= chance){ + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack; @@ -2833,34 +2777,34 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value = (value * GetSpellScale() / 100); entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits, - OTHER_CRIT_BLAST, GetName(), itoa(-value)); + OTHER_CRIT_BLAST, GetName(), itoa(-value)); return value; } } - value = value_BaseEffect; + value = value_BaseEffect; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; - if (target) { + if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; value -= target->GetFcDamageAmtIncoming(this, spell_id); - } + } - value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); + value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); - value -= GetFocusEffect(focusFcDamageAmt, spell_id); + value -= GetFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); value = (value * GetSpellScale() / 100); return value; - } +} int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { @@ -2992,126 +2936,126 @@ int8 Merc::GetChanceToCastBySpellType(int16 spellType) { int8 chance = 0; switch (spellType) { - case SpellType_Nuke: { - switch(mercClass) - { + case SpellType_Nuke: { + switch(mercClass) + { case TANK: { chance = 100; break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 100; break; - } + } case CASTERDPS:{ chance = 100; break; + } } - } - break; - } - case SpellType_Heal: { - switch(mercClass) - { + break; + } + case SpellType_Heal: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ chance = 100; break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Root: { - switch(mercClass) - { + break; + } + case SpellType_Root: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Buff: { - switch(mercClass) - { + break; + } + case SpellType_Buff: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ chance = IsEngaged() ? 0 : 100; break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_InCombatBuff: { - switch(mercClass) - { + break; + } + case SpellType_InCombatBuff: { + switch(mercClass) + { case TANK: { chance = 50; break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 50; break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Escape: { - switch(mercClass) - { + break; + } + case SpellType_Escape: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 100; break; - } + } case CASTERDPS:{ chance = 100; break; + } } - } - break; - } - default: - chance = 0; - break; + break; + } + default: + chance = 0; + break; } return chance; @@ -3124,7 +3068,7 @@ bool Merc::CheckStance(int16 stance) { if(stance == 0 || (stance > 0 && stance == GetStance()) || (stance < 0 && abs(stance) != GetStance())) { - return true; + return true; } return false; @@ -3183,14 +3127,14 @@ MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) { if((mercSpellList[i].type & spellType) && caster->CheckStance(mercSpellList[i].stance) && CheckSpellRecastTimers(caster, mercSpellList[i].spellid)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; + result.spellid = mercSpellList[i].spellid; + result.stance = mercSpellList[i].stance; + result.type = mercSpellList[i].type; + result.slot = mercSpellList[i].slot; + result.proc_chance = mercSpellList[i].proc_chance; + result.time_cancast = mercSpellList[i].time_cancast; - break; + break; } } } @@ -3220,14 +3164,14 @@ MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) { if((mercSpellList[i].spellid == spellid) && caster->CheckStance(mercSpellList[i].stance)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; + result.spellid = mercSpellList[i].spellid; + result.stance = mercSpellList[i].stance; + result.type = mercSpellList[i].type; + result.slot = mercSpellList[i].slot; + result.proc_chance = mercSpellList[i].proc_chance; + result.time_cancast = mercSpellList[i].time_cancast; - break; + break; } } } @@ -3314,14 +3258,14 @@ MercSpell Merc::GetBestMercSpellForVeryFastHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsVeryFastHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3346,14 +3290,14 @@ MercSpell Merc::GetBestMercSpellForFastHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsFastHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3418,14 +3362,14 @@ MercSpell Merc::GetBestMercSpellForPercentageHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsCompleteHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3450,14 +3394,14 @@ MercSpell Merc::GetBestMercSpellForRegularSingleTargetHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3483,14 +3427,14 @@ MercSpell Merc::GetFirstMercSpellForSingleTargetHeal(Merc* caster) { if((IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) || IsFastHealSpell(mercSpellListItr->spellid)) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3515,14 +3459,14 @@ MercSpell Merc::GetBestMercSpellForGroupHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsRegularGroupHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3587,14 +3531,14 @@ MercSpell Merc::GetBestMercSpellForGroupCompleteHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsGroupCompleteHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3621,14 +3565,14 @@ MercSpell Merc::GetBestMercSpellForAETaunt(Merc* caster) { || spells[mercSpellListItr->spellid].targettype == ST_AETarget || spells[mercSpellListItr->spellid].targettype == ST_UndeadAE) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3653,14 +3597,14 @@ MercSpell Merc::GetBestMercSpellForTaunt(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if((spells[mercSpellListItr->spellid].targettype == ST_Target) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3868,15 +3812,15 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - initialCastChance = 50; - break; - case MercStanceBalanced: - initialCastChance = 25; - break; - case MercStanceBurn: - initialCastChance = 0; - break; + case MercStanceBurnAE: + initialCastChance = 50; + break; + case MercStanceBalanced: + initialCastChance = 25; + break; + case MercStanceBurn: + initialCastChance = 0; + break; } //check of we even want to cast an AE nuke @@ -3903,8 +3847,8 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -3916,13 +3860,13 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; + case MercStanceBurnAE: + numTargetsCheck = 1; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 2; + break; } if(caster) { @@ -3932,19 +3876,19 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsAENukeSpell(mercSpellListItr->spellid) && !IsAERainNukeSpell(mercSpellListItr->spellid) && !IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - uint8 numTargets = 0; - if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { - if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + uint8 numTargets = 0; + if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { + if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) { + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; + } } - } - break; + break; } } } @@ -3954,8 +3898,8 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -3967,13 +3911,13 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 2; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 3; - break; + case MercStanceBurnAE: + numTargetsCheck = 2; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 3; + break; } if(caster) { @@ -4004,8 +3948,8 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -4017,13 +3961,13 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; + case MercStanceBurnAE: + numTargetsCheck = 1; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 2; + break; } if(caster) { @@ -4054,7 +3998,7 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) bool spellSelected = false; result.spellid = 0; @@ -4070,15 +4014,15 @@ MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) { for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) - && MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + && MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -4459,40 +4403,40 @@ bool Merc::CheckConfidence() { CurrentCon = this->GetLevelCon(mob->GetLevel()); switch(CurrentCon) { - case CON_GREEN: { - ConRating = 0; - break; - } + case CON_GREEN: { + ConRating = 0; + break; + } - case CON_LIGHTBLUE: { - ConRating = 0.2; - break; - } + case CON_LIGHTBLUE: { + ConRating = 0.2; + break; + } - case CON_BLUE: { - ConRating = 0.6; - break; - } + case CON_BLUE: { + ConRating = 0.6; + break; + } - case CON_WHITE: { - ConRating = 1.0; - break; - } + case CON_WHITE: { + ConRating = 1.0; + break; + } - case CON_YELLOW: { - ConRating = 1.2; - break; - } + case CON_YELLOW: { + ConRating = 1.2; + break; + } - case CON_RED: { - ConRating = 1.5; - break; - } + case CON_RED: { + ConRating = 1.5; + break; + } - default: { - ConRating = 0; - break; - } + default: { + ConRating = 0; + break; + } } ConfidenceCheck += ConRating; @@ -4510,8 +4454,13 @@ bool Merc::CheckConfidence() { } void Merc::MercMeditate(bool isSitting) { + // Don't try to meditate if engaged or dead + if (IsEngaged() || GetAppearance() == eaDead) + { + return; + } if(isSitting) { - // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate + // If the merc is a caster and has less than 99% mana while its not engaged, he needs to sit to meditate if(GetManaRatio() < 99.0f) { if(!IsSitting()) @@ -4616,7 +4565,7 @@ float Merc::GetMaxMeleeRangeToTarget(Mob* target) { void Merc::DoClassAttacks(Mob *target) { if(target == nullptr) - return; //gotta have a target for all these + return; //gotta have a target for all these bool ca_time = classattack_timer.Check(false); @@ -4630,65 +4579,65 @@ void Merc::DoClassAttacks(Mob *target) { float HasteModifier = GetHaste() * 0.01f; int level = GetLevel(); - int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will + int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will bool did_attack = false; //class specific stuff... switch(GetClass()) { - case MELEEDPS: - if(level >= 10) { - reuse = BackstabReuseTime * 1000; - TryBackstab(target, reuse); - did_attack = true; - } - break; - case TANK:{ - if(level >= RuleI(Combat, NPCBashKickLevel)){ - if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. - { - DoAnim(animKick); - int32 dmg = 0; - - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillKick, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetKickDamage(); - else - dmg = MakeRandomInt(1, GetKickDamage()); - - } - } - - reuse = KickReuseTime * 1000; - DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); + case MELEEDPS: + if(level >= 10) { + reuse = BackstabReuseTime * 1000; + TryBackstab(target, reuse); did_attack = true; } - else - { - DoAnim(animTailRake); - int32 dmg = 0; + break; + case TANK:{ + if(level >= RuleI(Combat, NPCBashKickLevel)){ + if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. + { + DoAnim(animKick); + int32 dmg = 0; - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillBash, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetBashDamage(); - else - dmg = MakeRandomInt(1, GetBashDamage()); + if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ + dmg = -5; } - } + else{ + if(target->CheckHitChance(this, SkillKick, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); - reuse = BashReuseTime * 1000; - DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); - did_attack = true; + } + } + + reuse = KickReuseTime * 1000; + DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); + did_attack = true; + } + else + { + DoAnim(animTailRake); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, SkillBash, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + } + } + + reuse = BashReuseTime * 1000; + DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); + did_attack = true; + } } - } - break; - } + break; + } } classattack_timer.Start(reuse / HasteModifier); @@ -4749,21 +4698,23 @@ Mob* Merc::GetOwnerOrSelf() { bool Merc::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) { if(!NPC::Death(killerMob, damage, spell, attack_skill)) + { return false; + } Save(); - Mob *give_exp = hate_list.GetDamageTop(this); - Client *give_exp_client = nullptr; + //no corpse, no exp if we're a merc. + //We'll suspend instead, since that's what live does. + //Not actually sure live supports 'depopping' merc corpses. + //if(entity_list.GetCorpseByID(GetID())) + // entity_list.GetCorpseByID(GetID())->Depop(); - if(give_exp && give_exp->IsClient()) - give_exp_client = give_exp->CastToClient(); - - bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - - //no corpse, no exp if we're a merc. We'll suspend instead, since that's what live does. I'm not actually sure live supports 'depopping' merc corpses. - if(entity_list.GetCorpseByID(GetID())) - entity_list.GetCorpseByID(GetID())->Depop(); + // If client is in zone, suspend merc, else depop it. + if (!Suspend()) + { + Depop(); + } return true; } @@ -4809,8 +4760,8 @@ const char* Merc::GetRandomName(){ bool vwl=false; bool dbl=false; if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" + { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel + rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" rndname[0]=paircons[rndnum]; rndname[1]=paircons[rndnum+1]; n=2; @@ -4829,37 +4780,37 @@ const char* Merc::GetRandomName(){ for (int i=n;i46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" + { // pick a cons pair + if (i>namlen-3) // last 2 chars in name? + { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" rndnum=MakeRandomInt(0, 7)*2; } else - { // pick any from the set + { // pick any from the set rndnum=(rndnum-47)*2; } rndname[i]=paircons[rndnum]; rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below + dlc=true; // flag keeps second letter from being doubled below i+=1; } else - { // select a single cons + { // select a single cons rndname[i]=cons[rndnum]; } } else - { // select a vowel + { // select a vowel rndname[i]=vowels[MakeRandomInt(0, 16)]; } vwl=!vwl; if (!dbl && !dlc) - { // one chance at double letters in name - if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name + { // one chance at double letters in name + if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name { rndname[i+1]=rndname[i]; dbl=true; @@ -4890,7 +4841,6 @@ const char* Merc::GetRandomName(){ return name; } - bool Compare_Merc_Spells(MercSpell i, MercSpell j); bool Compare_Merc_Spells(MercSpell i, MercSpell j) @@ -4944,7 +4894,7 @@ bool Merc::LoadMercSpells() { GetMercOwner()->Message(7, "Mercenary Debug: Spell list for merc."); for (int i = merc_spells.size() - 1; i >= 0; i--) { - GetMercOwner()->Message(7, "%i] Slot: %i, SpellID: %i, Type: %i, Stance: %i, Proc Chance: %i", i, merc_spells[i].slot, merc_spells[i].spellid, merc_spells[i].type, merc_spells[i].stance, merc_spells[i].proc_chance); + GetMercOwner()->Message(7, "%i] Slot: %i, SpellID: %i, Type: %i, Stance: %i, Proc Chance: %i", i, merc_spells[i].slot, merc_spells[i].spellid, merc_spells[i].type, merc_spells[i].stance, merc_spells[i].proc_chance); }*/ } @@ -4952,28 +4902,33 @@ bool Merc::LoadMercSpells() { } bool Merc::Save() { - bool Result = false; + if(database.SaveMerc(this)){ - Result = true; + return true; } - return Result; + return false; } Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, bool updateFromDB) { - if(c) { - if(c->GetMercID()) { + if(c) + { + if(c->GetMercID()) + { merc_template = zone->GetMercTemplate(c->GetMercInfo().MercTemplateID); } } //get mercenary data - if(merc_template) { - const NPCType* npc_type_to_copy = database.GetMercType(merc_template->MercNPCID, merc_template->RaceID, c->GetLevel()); //TODO: Maybe add a way of updating client merc stats in a seperate function? like, for example, on leveling up. + if(merc_template) + { + //TODO: Maybe add a way of updating client merc stats in a seperate function? like, for example, on leveling up. + const NPCType* npc_type_to_copy = database.GetMercType(merc_template->MercNPCID, merc_template->RaceID, c->GetLevel()); if(npc_type_to_copy != nullptr) { - NPCType* npc_type = new NPCType; //This is actually a very terrible method of assigning stats, and should be changed at some point. See the comment in merc's deconstructor. + //This is actually a very terrible method of assigning stats, and should be changed at some point. See the comment in merc's deconstructor. + NPCType* npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); memcpy(npc_type, npc_type_to_copy, sizeof(NPCType)); if(c && !updateFromDB) @@ -4982,7 +4937,6 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, { snprintf(c->GetMercInfo().merc_name, 64, "%s", GetRandomName()); //sanity check. } - snprintf(c->GetEPP().merc_name, 64, "%s", c->GetMercInfo().merc_name); snprintf(npc_type->name, 64, "%s", c->GetMercInfo().merc_name); } uint8 gender = 0; @@ -5006,17 +4960,18 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, Merc* merc = new Merc(npc_type, c->GetX(), c->GetY(), c->GetZ(), 0); - if(merc) { + if(merc) + { merc->SetMercData( merc_template->MercTemplateID ); database.LoadMercEquipment(merc); merc->UpdateMercStats(c); - if(updateFromDB) { + if(updateFromDB) + { database.LoadCurrentMerc(c); merc->SetMercID(c->GetMercInfo().mercid); snprintf(merc->name, 64, "%s", c->GetMercInfo().merc_name); - snprintf(c->GetEPP().merc_name, 64, "%s", c->GetMercInfo().merc_name); merc->SetSuspended(c->GetMercInfo().IsSuspended); merc->gender = c->GetMercInfo().Gender; merc->SetHP(c->GetMercInfo().hp <= 0 ? merc->GetMaxHP() : c->GetMercInfo().hp); @@ -5112,7 +5067,18 @@ void Merc::UpdateMercStats(Client *c) { } } -void Merc::UpdateMercAppearance(Client *c) { +void Merc::UpdateMercAppearance() { + // Copied from Bot Code: + uint32 itemID = NO_ITEM; + uint8 materialFromSlot = _MaterialInvalid; + for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { + itemID = equipment[i]; + if(itemID != NO_ITEM) { + materialFromSlot = Inventory::CalcMaterialFromSlot(i); + if(materialFromSlot != _MaterialInvalid) + this->SendWearChange(materialFromSlot); + } + } } void Merc::AddItem(uint8 slot, uint32 item_id) { @@ -5120,8 +5086,6 @@ void Merc::AddItem(uint8 slot, uint32 item_id) { } bool Merc::Spawn(Client *owner) { - if(!RuleB(Mercs, AllowMercs)) - return false; if(!owner) return false; @@ -5135,25 +5099,153 @@ bool Merc::Spawn(Client *owner) { SendPosition(); + //UpdateMercAppearance(); + //printf("Spawned Merc with ID %i\n", npc->GetID()); fflush(stdout); - /* - uint32 itemID = NO_ITEM; - uint8 materialFromSlot = _MaterialInvalid; - for(int i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::EQUIPMENT_END; ++i) { - itemID = GetMercItemBySlot(i); - if(itemID != NO_ITEM) { - materialFromSlot = Inventory::CalcMaterialFromSlot(i); - if(materialFromSlot != _MaterialInvalid) { - this->SendWearChange(materialFromSlot); - } - } - } - */ - return true; } +void Client::SendMercResponsePackets(uint32 ResponseType) +{ + switch (ResponseType) + { + case 0: // Mercenary Spawned Successfully? + SendMercMerchantResponsePacket(0); + break; + case 1: //You do not have enough funds to make that purchase! + SendMercMerchantResponsePacket(1); + break; + case 2: //Mercenary does not exist! + SendMercMerchantResponsePacket(2); + break; + case 3: //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + break; + case 4: //Mercenaries are not allowed in raids! + SendMercMerchantResponsePacket(4); + break; + case 5: //You already have a pending mercenary purchase! + SendMercMerchantResponsePacket(5); + break; + case 6: //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! + SendMercMerchantResponsePacket(6); + break; + case 7: //You must dismiss your suspended mercenary before purchasing a new one! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(7); + else + //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! + SendMercMerchantResponsePacket(6); + break; + case 8: //You can not purchase a mercenary because your group is full! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(8); + else + SendMercMerchantResponsePacket(7); + break; + case 9: //You can not purchase a mercenary because you are in combat! + if (GetClientVersion() < EQClientRoF) + //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + else + SendMercMerchantResponsePacket(8); + break; + case 10: //You have recently dismissed a mercenary and must wait a few more seconds before you can purchase a new one! + if (GetClientVersion() < EQClientRoF) + //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + else + SendMercMerchantResponsePacket(9); + break; + case 11: //An error occurred created your mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(9); + else + SendMercMerchantResponsePacket(10); + break; + case 12: //Upkeep Charge Message + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(10); + else + SendMercMerchantResponsePacket(11); + break; + case 13: // ??? + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(11); + else + SendMercMerchantResponsePacket(12); + break; + case 14: //You ran out of funds to pay for your mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(12); + else + SendMercMerchantResponsePacket(13); + break; + case 15: // ??? + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(13); + else + SendMercMerchantResponsePacket(14); + break; + case 16: //Your mercenary is about to be suspended due to insufficient funds! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(14); + else + SendMercMerchantResponsePacket(15); + break; + case 17: //There is no mercenary liaison nearby! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(15); + else + SendMercMerchantResponsePacket(16); + break; + case 18: //You are too far from the liaison! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(16); + else + SendMercMerchantResponsePacket(17); + break; + case 19: //You do not meet the requirements for that mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(18); + break; + case 20: //You are unable to interact with the liaison! + if (GetClientVersion() < EQClientRoF) + //You are too far from the liaison! + SendMercMerchantResponsePacket(16); + else + SendMercMerchantResponsePacket(19); + break; + case 21: //You do not have a high enough membership level to purchase this mercenary! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(20); + break; + case 22: //Your purchase has failed because this mercenary requires a Gold membership! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(21); + break; + case 23: //Your purchase has failed because this mercenary requires at least a Silver membership! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(22); + break; + default: //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + break; + } +} + void Client::UpdateMercTimer() { Merc *merc = GetMerc(); @@ -5162,135 +5254,87 @@ void Client::UpdateMercTimer() { if(GetMercTimer()->Check()) { - uint32 upkeep = Merc::CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); + uint32 upkeep = merc->CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); - if(CheckCanRetainMerc(upkeep)) { - if(RuleB(Mercs, ChargeMercUpkeepCost)) { + if(CheckCanRetainMerc(upkeep)) + { + if(RuleB(Mercs, ChargeMercUpkeepCost)) + { TakeMoneyFromPP((upkeep * 100), true); } } - else { + else + { merc->Suspend(); return; } + // Reset the upkeep timer GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - SendMercTimerPacket(GetMercID(), 5, 0, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + SendMercTimer(merc); GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); GetMercTimer()->SetTimer(GetMercInfo().MercTimerRemaining); - // Send upkeep charge message and reset the upkeep timer - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(10); - else - SendMercMerchantResponsePacket(11); + // Send upkeep charge message + SendMercResponsePackets(12); - /* - uint32 upkeep_plat = 0; - uint32 upkeep_gold = 0; - - if (upkeep >= 10) - upkeep_plat = (int)(upkeep / 10); - - if (upkeep - (upkeep_plat * 10) >= 1) - upkeep_gold = (int)((upkeep - (upkeep_plat * 10)) / 100); - */ + // Warn that mercenary is about to be suspended due to insufficient funds (on next upkeep) + if (RuleB(Mercs, ChargeMercUpkeepCost) && upkeep > 0 && !HasMoney(upkeep * 100)) + { + SendMercResponsePackets(16); + } // Normal upkeep charge message //Message(7, "You have been charged a mercenary upkeep cost of %i plat, and %i gold and your mercenary upkeep cost timer has been reset to 15 minutes.", upkeep_plat, upkeep_gold, (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); // Message below given when too low level to be charged - // Temporarily enabled for all upkeep costs until mercenary stuff is completed //Message(7, "Your mercenary waived an upkeep cost of %i plat, and %i gold or %i %s and your mercenary upkeep cost timer has been reset to %i minutes", upkeep_plat, upkeep_gold, 1, "Bayle Marks", (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); } } } bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) { + + if (!CheckCanSpawnMerc(template_id)) + { + return false; + } + MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); - //invalid merc data - if(!mercTemplate) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(9); - else - SendMercMerchantResponsePacket(10); + //check for suspended merc + if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { + SendMercResponsePackets(6); return false; } - //check client version - if(GetClientVersion() < mercTemplate->ClientVersion) { - SendMercMerchantResponsePacket(3); + // Check if max number of mercs is already reached + if(GetNumMercs() >= MAXMERCS) { + SendMercResponsePackets(6); return false; } - if(GetClientVersion() >= EQClientRoF && GetNumMercs() >= MAXMERCS) { - SendMercMerchantResponsePacket(6); - return false; - } - else if(GetMerc()) { //check for current merc - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(6); - else - SendMercMerchantResponsePacket(6); - return false; - } - else if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { //has suspended merc - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(7); - else - SendMercMerchantResponsePacket(6); - return false; - } - - //check for sufficient funds - if(RuleB(Mercs, ChargeMercPurchaseCost)) { - uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold - if(cost > 0 && !HasMoney(cost)) { - SendMercMerchantResponsePacket(1); - return false; - } - } - - //check for raid - if(HasRaid()) { - SendMercMerchantResponsePacket(4); - return false; - } - - //check group size - if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(8); - else - SendMercMerchantResponsePacket(7); - return false; - } - - //check in combat - if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) { - SendMercMerchantResponsePacket(8); - return false; - } - - //check for valid merchant - can check near area for any merchants + //check for valid merchant if(!merchant) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(14); - else - SendMercMerchantResponsePacket(16); + SendMercResponsePackets(17); return false; } //check for merchant too far away if(DistNoRoot(*merchant) > USE_NPC_RANGE2) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(15); - else - SendMercMerchantResponsePacket(17); + SendMercResponsePackets(18); return false; } + //check for sufficient funds and remove them last + if(RuleB(Mercs, ChargeMercPurchaseCost)) { + uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold + if(cost > 0 && !HasMoney(cost)) { + SendMercResponsePackets(1); + return false; + } + } + return true; } @@ -5301,7 +5345,7 @@ bool Client::CheckCanRetainMerc(uint32 upkeep) { if(RuleB(Mercs, ChargeMercPurchaseCost)) { if(merc) { if(upkeep > 0 && !HasMoney(upkeep * 100)) { - SendMercMerchantResponsePacket(1); + SendMercResponsePackets(14); return false; } } @@ -5310,116 +5354,127 @@ bool Client::CheckCanRetainMerc(uint32 upkeep) { return true; } -bool Client::CheckCanUnsuspendMerc() { - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID); +bool Client::CheckCanSpawnMerc(uint32 template_id) { - //invalid merc data - if(!mercTemplate) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(9); - else - SendMercMerchantResponsePacket(10); + // Check if mercs are enabled globally + if(!RuleB(Mercs, AllowMercs)) { return false; } - //check client version - if(GetClientVersion() < mercTemplate->ClientVersion) { - SendMercMerchantResponsePacket(3); - return false; - } - - //check for raid - if(HasRaid()) { - SendMercMerchantResponsePacket(4); - return false; - } - - //check group size - if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(8); - else - SendMercMerchantResponsePacket(7); - return false; - } - - //check if zone allows mercs + // Check if zone allows mercs if(!zone->AllowMercs()) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(4); // ?? - else - SendMercMerchantResponsePacket(4); // ?? + SendMercResponsePackets(3); return false; } - //check in combat - if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) { - SendMercMerchantResponsePacket(8); + MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); + + // Invalid merc data + if(!mercTemplate) { + SendMercResponsePackets(11); return false; } + // Check client version + if(GetClientVersion() < mercTemplate->ClientVersion) { + SendMercResponsePackets(3); + return false; + } + + // Check for raid + if(HasRaid()) { + SendMercResponsePackets(4); + return false; + } + + // Check group size + if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) { + SendMercResponsePackets(8); + return false; + } + + // Check in combat + if(GetAggroCount() > 0) { + SendMercResponsePackets(9); + return false; + } + + return true; +} + +bool Client::CheckCanUnsuspendMerc() { + + if (!CheckCanSpawnMerc(GetMercInfo().MercTemplateID)) + { + return false; + } + + MercTemplate* mercTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID); + if(!GetPTimers().Expired(&database, pTimerMercSuspend, false)) { - SendMercMerchantResponsePacket(16); - Message(0, "You must wait %i seconds before unsuspending your mercenary.", GetPTimers().GetRemainingTime(pTimerMercSuspend)); //todo: find this packet response and tell them properly. + SendMercResponsePackets(10); + //TODO: find this packet response and tell them properly. + Message(0, "You must wait %i seconds before unsuspending your mercenary.", GetPTimers().GetRemainingTime(pTimerMercSuspend)); return false; } + return true; } bool Client::CheckCanDismissMerc() { - if(!GetMerc()) { - Message(7, "You have no mercenary to dismiss."); - return false; - } - if(GetMerc()->IsCasting()) { - Message(7, "Unable to dismiss mercenary."); + if(!GetMercID()) + { + Message(7, "You have no mercenary to dismiss."); return false; } return true; } -void Client::CheckMercSuspendTimer() -{ - if(GetMercInfo().SuspendedTime != 0) { - if(time(nullptr) >= GetMercInfo().SuspendedTime){ - SendMercSuspendResponsePacket(0); +void Client::CheckMercSuspendTimer() { + + if(GetMercInfo().SuspendedTime != 0) + { + if(time(nullptr) >= GetMercInfo().SuspendedTime) + { + GetMercInfo().SuspendedTime = 0; + SendMercResponsePackets(0); } } } -void Client::SuspendMercCommand() -{ +void Client::SuspendMercCommand() { + bool ExistsMerc = GetMercInfo().MercTemplateID != 0; if(ExistsMerc == true) { - if(GetMercInfo().IsSuspended) { - //p_timers.Enable(pTimerMercReuse); - - // Set time remaining to max on unsuspend - there is a charge for unsuspending as well - // GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - if(!CheckCanUnsuspendMerc()){ + if(GetMercInfo().IsSuspended) + { + if(!CheckCanUnsuspendMerc()) + { return; } // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) { + if(merc) + { SpawnMerc(merc, true); } - else { + else + { //merc failed to spawn - SendMercMerchantResponsePacket(3); + SendMercResponsePackets(3); } } else { Merc* CurrentMerc = GetMerc(); - if(CurrentMerc && GetMercID()) { - //CurrentMerc->Save(); + if(CurrentMerc && GetMercID()) + { CurrentMerc->Suspend(); } } @@ -5429,68 +5484,89 @@ void Client::SuspendMercCommand() // Handles all client zone change event void Merc::ProcessClientZoneChange(Client* mercOwner) { - if(mercOwner) { + + if(mercOwner) + { Zone(); } } -void Client::SpawnMercOnZone() -{ +void Client::SpawnMercOnZone() { + if(!RuleB(Mercs, AllowMercs)) return; if (GetMerc()) return; - bool ExistsMerc = GetEPP().merc_name[0] != 0; - if(ExistsMerc == true) + if(database.LoadMercInfo(this)) { - if(!GetMercInfo().IsSuspended) { + if(!GetMercInfo().IsSuspended) + { GetMercInfo().SuspendedTime = 0; // Get merc, assign it to client & spawn - if(database.LoadMercInfo(this)) { - if(!CheckCanUnsuspendMerc()){ - return; - } - Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) { - SpawnMerc(merc, false); - SendMercTimerPacket(merc->GetID(), 5, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - } + Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); + if(merc) + { + SpawnMerc(merc, false); } } else { // Send Mercenary Status/Timer packet - SendMercTimerPacket(0, 1, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + SendMercTimer(GetMerc()); SendMercPersonalInfo(); - if(GetMercInfo().SuspendedTime != 0) { - if(time(nullptr) >= GetMercInfo().SuspendedTime){ - GetMercInfo().SuspendedTime = 0; - } - } + CheckMercSuspendTimer(); SendMercSuspendResponsePacket(GetMercInfo().SuspendedTime); } } } -void Client::SpawnMerc(Merc* merc, bool setMaxStats) -{ - if(!RuleB(Mercs, AllowMercs)) - return; +void Client::SendMercTimer(Merc* merc) { - if(merc) { - merc->Spawn(this); - merc->SetSuspended(false); - SetMerc(merc); - merc->Unsuspend(setMaxStats); - merc->SetStance(GetMercInfo().Stance); + if (GetMercInfo().mercid == 0) + { + //return; + } + + if (!merc) + { + SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + } + else if (merc->IsSuspended()) + { + SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + } + else + { + SendMercTimerPacket(merc->GetID(), MERC_STATE_NORMAL, NOT_SUSPENDED_TIME, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); } } +void Client::SpawnMerc(Merc* merc, bool setMaxStats) { + + if (!merc || !CheckCanSpawnMerc(merc->GetMercTemplateID())) + { + return; + } + + merc->Spawn(this); + merc->SetSuspended(false); + SetMerc(merc); + merc->Unsuspend(setMaxStats); + merc->SetStance(GetMercInfo().Stance); + GetMercInfo().SuspendedTime = 0; + + SendMercTimer(merc); + + return; + +} + bool Merc::Suspend() { + Client* mercOwner = GetMercOwner(); if(!mercOwner) @@ -5507,13 +5583,18 @@ bool Merc::Suspend() { mercOwner->SendMercSuspendResponsePacket(mercOwner->GetMercInfo().SuspendedTime); + mercOwner->SendMercTimer(this); + Depop(); - mercOwner->SendMercTimerPacket(0, 1, mercOwner->GetMercInfo().SuspendedTime, mercOwner->GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + // Start the timer to send the packet that refreshes the Unsuspend Button + mercOwner->GetPTimers().Start(pTimerMercSuspend, RuleI(Mercs, SuspendIntervalS)); + return true; } bool Merc::Unsuspend(bool setMaxStats) { + Client* mercOwner = nullptr; bool loaded = false; @@ -5524,10 +5605,9 @@ bool Merc::Unsuspend(bool setMaxStats) { if(!mercOwner) return false; - if(GetID()) { - uint32 mercState = 5; - uint32 suspendedTime = 0; - + if(GetID()) + { + // Set time remaining to max on unsuspend - there is a charge for unsuspending as well SetSuspended(false); mercOwner->GetMercInfo().mercid = GetMercID(); @@ -5538,14 +5618,16 @@ bool Merc::Unsuspend(bool setMaxStats) { mercOwner->GetMercInfo().SuspendedTime = 0; mercOwner->GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); mercOwner->GetMercTimer()->SetTimer(mercOwner->GetMercInfo().MercTimerRemaining); - mercOwner->SendMercTimerPacket(GetID(), mercState, suspendedTime, mercOwner->GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + mercOwner->SendMercTimer(this); if(!mercOwner->GetPTimers().Expired(&database, pTimerMercSuspend, false)) mercOwner->GetPTimers().Clear(&database, pTimerMercSuspend); mercOwner->SendMercPersonalInfo(); Group* g = entity_list.GetGroupByClient(mercOwner); - if(!g) { //nobody from our group is here... start a new group + //nobody from our group is here... start a new group + if(!g) + { g = new Group(mercOwner); if(!g) { @@ -5562,7 +5644,8 @@ bool Merc::Unsuspend(bool setMaxStats) { return false; } - if(AddMercToGroup(this, g)) { + if(AddMercToGroup(this, g)) + { entity_list.AddGroup(g, g->GetID()); database.SetGroupLeaderName(g->GetID(), mercOwner->GetName()); database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID()); @@ -5571,17 +5654,21 @@ bool Merc::Unsuspend(bool setMaxStats) { g->SaveGroupLeaderAA(); loaded = true; } - else { + else + { g->DisbandGroup(); } - } //else, somebody from our group is already here... - else if (AddMercToGroup(this, mercOwner->GetGroup())) { + } + else if (AddMercToGroup(this, mercOwner->GetGroup())) + { + // Group already exists database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true); database.RefreshGroupFromDB(mercOwner); loaded = true; } - else { + else + { if(MERC_DEBUG > 0) mercOwner->Message(7, "Mercenary failed to join the group - Suspending"); @@ -5589,35 +5676,45 @@ bool Merc::Unsuspend(bool setMaxStats) { } - if(loaded) { + if(loaded) + { LoadMercSpells(); - if(setMaxStats) { + if(setMaxStats) + { SetHP(GetMaxHP()); SetMana(GetMaxMana()); SetEndurance(GetMaxEndurance()); } + + //check for sufficient funds and remove them last + if(RuleB(Mercs, ChargeMercUpkeepCost)) + { + uint32 cost = CalcUpkeepCost(GetMercTemplateID(), GetLevel()) * 100; // Cost is in gold + if(cost > 0 && !mercOwner->HasMoney(cost)) + { + mercOwner->SendMercResponsePackets(1); + return false; + } + } } } return true; } -bool Merc::Dismiss(){ +bool Merc::Dismiss() { Client* mercOwner = GetMercOwner(); - if(!mercOwner) - return false; - - mercOwner->SendClearMercInfo(); - - //SetMercEntityID(0); - - mercOwner->SetMerc(0); - database.DeleteMerc(GetMercID()); - + + if(mercOwner) + { + mercOwner->SendClearMercInfo(); + mercOwner->SetMerc(0); + } + Depop(); return true; @@ -5629,18 +5726,27 @@ void Merc::Zone() { } void Merc::Depop() { + WipeHateList(); - entity_list.RemoveMerc(this->GetID()); + + if(IsCasting()) + { + InterruptSpell(); + } + entity_list.RemoveFromHateLists(this); if(HasGroup()) - Merc::RemoveMercFromGroup(this, GetGroup()); - - if(HasPet()) { - GetPet()->Depop(); + { + RemoveMercFromGroup(this, GetGroup()); } - SetOwnerID(0); + entity_list.RemoveMerc(this->GetID()); + + if(HasPet()) + { + GetPet()->Depop(); + } p_depop = true; @@ -5648,14 +5754,19 @@ void Merc::Depop() { } bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { + bool Result = false; - if(merc && group) { - if(merc->HasGroup()) { - if(!group->IsLeader(merc)) { + if(merc && group) + { + if(merc->HasGroup()) + { + if(!group->IsLeader(merc)) + { merc->SetFollowID(0); - if(group->DelMember(merc)) { + if(group->DelMember(merc, true)) + { if(merc->GetMercCharacterID() != 0) database.SetGroupID(merc->GetName(), 0, merc->GetMercCharacterID(), true); } @@ -5665,8 +5776,10 @@ bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { group->DisbandGroup(); } } - else { - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + else + { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) + { if(!group->members[i]) continue; @@ -5675,11 +5788,14 @@ bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { Merc* groupmerc = group->members[i]->CastToMerc(); - groupmerc->SetOwnerID(0); + //groupmerc->SetOwnerID(0); + if (!groupmerc->IsSuspended()) + { + groupmerc->Suspend(); + database.SetGroupID(groupmerc->GetCleanName(), 0, groupmerc->GetMercCharacterID(), true); + } } - group->DisbandGroup(); - database.SetGroupID(merc->GetCleanName(), 0, merc->GetMercCharacterID(), true); } Result = true; @@ -5695,15 +5811,17 @@ bool Merc::AddMercToGroup(Merc* merc, Group* group) { if(merc && group) { // Remove merc from current group if any if(merc->HasGroup()) { - Merc::RemoveMercFromGroup(merc, merc->GetGroup()); + merc->RemoveMercFromGroup(merc, merc->GetGroup()); } //Try and add the member, followed by checking if the merc owner exists. - if(group->AddMember(merc) && merc->GetMercOwner() != nullptr) { - merc->SetFollowID(merc->GetMercOwner()->GetID()); - Result = true; + if(group->AddMember(merc) && merc->GetMercOwner() != nullptr) + { + merc->SetFollowID(merc->GetMercOwner()->GetID()); + Result = true; } - else { - //Suspend it if the member is not added and the merc's owner is not valid. + else + { + //Suspend it if the member is not added or the merc's owner is not valid. merc->Suspend(); } } @@ -5712,34 +5830,45 @@ bool Merc::AddMercToGroup(Merc* merc, Group* group) { } void Client::InitializeMercInfo() { - for(int i=0; iGetOwnerID() != GetID()) { + if(tmp->GetOwnerID() != GetID()) + { SetMercID(0); - return(nullptr); + return (nullptr); } - return(tmp); + return (tmp); } uint8 Client::GetNumMercs() { + uint8 numMercs = 0; - for(int i=0; iGetMercTemplate(template_id); SetMercTemplateID( merc_template->MercTemplateID ); SetMercType( merc_template->MercType ); @@ -5756,6 +5886,7 @@ void Merc::SetMercData( uint32 template_id ) { SetTierID( merc_template->TierID ); SetCostFormula( merc_template->CostFormula ); SetMercNameType( merc_template->MercNameType ); + } MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { @@ -5763,11 +5894,15 @@ MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { } void Client::SetMerc(Merc* newmerc) { + Merc* oldmerc = GetMerc(); - if (oldmerc) { + if (oldmerc) + { oldmerc->SetOwnerID(0); } - if (!newmerc) { + + if (!newmerc) + { SetMercID(0); GetMercInfo().mercid = 0; GetMercInfo().MercTemplateID = 0; @@ -5777,10 +5912,10 @@ void Client::SetMerc(Merc* newmerc) { GetMercInfo().Gender = 0; GetMercInfo().State = 0; memset(GetMercInfo().merc_name, 0, 64); - memset(GetEPP().merc_name, 0, 64); - } else { + } + else + { SetMercID(newmerc->GetID()); - //newmerc->SetMercEntityID(newmerc->GetID()); //Client* oldowner = entity_list.GetClientByID(newmerc->GetOwnerID()); newmerc->SetOwnerID(this->GetID()); newmerc->SetMercCharacterID(this->CharacterID()); @@ -5792,43 +5927,52 @@ void Client::SetMerc(Merc* newmerc) { GetMercInfo().SuspendedTime = 0; GetMercInfo().Gender = newmerc->GetGender(); //GetMercInfo().State = newmerc->GetStance(); + snprintf(GetMercInfo().merc_name, 64, "%s", newmerc->GetName()); } } void Client::UpdateMercLevel() { Merc* merc = GetMerc(); - if (merc) { + if (merc) + { merc->UpdateMercStats(this); } } void Client::SendMercMerchantResponsePacket(int32 response_type) { // This response packet brings up the Mercenary Manager window - if(GetClientVersion() >= EQClientSoD) { + if(GetClientVersion() >= EQClientSoD) + { EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct)); MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer; - mmr->ResponseType = response_type; // send specified response type + mmr->ResponseType = response_type; // send specified response type FastQueuePacket(&outapp); } } void Client::SendMercenaryUnknownPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnknown1, 1); outapp->WriteUInt8(type); FastQueuePacket(&outapp); + } void Client::SendMercenaryUnsuspendPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnsuspendResponse, 1); outapp->WriteUInt8(type); 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 + smr->SuspendTime = suspended_time; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp FastQueuePacket(&outapp); + } void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval, int32 unk01) { @@ -5842,6 +5986,7 @@ void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspen mss->UpdateInterval = update_interval; // Seen 900000 - 15 minutes in ms mss->MercUnk01 = unk01; // Seen 180000 - 3 minutes in ms - Used for the unsuspend button refresh timer FastQueuePacket(&outapp); + } void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { @@ -5856,19 +6001,21 @@ void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { void NPC::LoadMercTypes() { std::string query = StringFormat("SELECT DISTINCT MTyp.dbstring, MTyp.clientversion " - "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " - "merc_types MTyp, merc_templates MTem " - "WHERE MME.merchant_id = %i " - "AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " - "AND MMTE.merc_template_id = MTem.merc_template_id " - "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); - return; - } + "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " + "merc_types MTyp, merc_templates MTem " + "WHERE MME.merchant_id = %i " + "AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " + "AND MMTE.merc_template_id = MTem.merc_template_id " + "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + return; + } - for (auto row = results.begin(); row != results.end(); ++row) { + for (auto row = results.begin(); row != results.end(); ++row) + { MercType tempMercType; tempMercType.Type = atoi(row[0]); @@ -5879,26 +6026,29 @@ void NPC::LoadMercTypes() { } -void NPC::LoadMercs(){ +void NPC::LoadMercs() { std::string query = StringFormat("SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, " - "MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, " - "CASE WHEN MTem.clientversion > MTyp.clientversion " - "THEN MTem.clientversion " - "ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id " - "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " - "merc_types MTyp, merc_templates MTem " - "WHERE MME.merchant_id = %i AND " - "MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " - "AND MMTE.merc_template_id = MTem.merc_template_id " - "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); + "MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, " + "CASE WHEN MTem.clientversion > MTyp.clientversion " + "THEN MTem.clientversion " + "ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id " + "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " + "merc_types MTyp, merc_templates MTem " + "WHERE MME.merchant_id = %i AND " + "MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " + "AND MMTE.merc_template_id = MTem.merc_template_id " + "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); - return; - } - for (auto row = results.begin(); row != results.end(); ++row) { + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + { MercData tempMerc; tempMerc.MercTemplateID = atoi(row[0]); @@ -5913,8 +6063,8 @@ void NPC::LoadMercs(){ } -int NPC::GetNumMercTypes(uint32 clientVersion) -{ +int NPC::GetNumMercTypes(uint32 clientVersion) { + int count = 0; std::list mercTypeList = GetMercTypesList(); @@ -5926,8 +6076,8 @@ int NPC::GetNumMercTypes(uint32 clientVersion) return count; } -int NPC::GetNumMercs(uint32 clientVersion) -{ +int NPC::GetNumMercs(uint32 clientVersion) { + int count = 0; std::list mercDataList = GetMercsList(); @@ -5940,11 +6090,15 @@ int NPC::GetNumMercs(uint32 clientVersion) } std::list NPC::GetMercTypesList(uint32 clientVersion) { + std::list result; - if(GetNumMercTypes() > 0) { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - if(mercTypeListItr->ClientVersion <= clientVersion) { + if(GetNumMercTypes() > 0) + { + for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) + { + if(mercTypeListItr->ClientVersion <= clientVersion) + { MercType mercType; mercType.Type = mercTypeListItr->Type; mercType.ClientVersion = mercTypeListItr->ClientVersion; @@ -5957,14 +6111,19 @@ std::list NPC::GetMercTypesList(uint32 clientVersion) { } std::list NPC::GetMercsList(uint32 clientVersion) { + std::list result; - if(GetNumMercs() > 0) { - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { - if(mercListItr->ClientVersion <= clientVersion) { + if(GetNumMercs() > 0) + { + for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) + { + if(mercListItr->ClientVersion <= clientVersion) + { MercTemplate *merc_template = zone->GetMercTemplate(mercListItr->MercTemplateID); - if(merc_template) { + if(merc_template) + { MercData mercData; mercData.MercTemplateID = mercListItr->MercTemplateID; mercData.MercType = merc_template->MercType; @@ -5981,54 +6140,70 @@ std::list NPC::GetMercsList(uint32 clientVersion) { return result; } -uint32 Merc::CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type) { +uint32 Merc::CalcPurchaseCost(uint32 templateID , uint8 level, uint8 currency_type) { + uint32 cost = 0; MercTemplate *mercData = zone->GetMercTemplate(templateID); - if(mercData) { - if(currency_type == 0) { //calculate cost in coin - cost in gold + if(mercData) + { + //calculate cost in coin - cost in gold + if(currency_type == 0) + { int levels_above_cutoff; - switch (mercData->CostFormula) { + switch (mercData->CostFormula) + { case 0: levels_above_cutoff = level > 10 ? (level - 10) : 0; cost = levels_above_cutoff * 300; cost += level >= 10 ? 100 : 0; + cost /= 100; break; default: break; } } - else if(currency_type == 19) { - cost = 0; - } - } - - return cost/100; -} - -uint32 Merc::CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type) { - uint32 cost = 0; - - MercTemplate *mercData = zone->GetMercTemplate(templateID); - - if(mercData) { - if(currency_type == 0) { //calculate cost in coin - cost in gold - int levels_above_cutoff; - switch (mercData->CostFormula) { - case 0: - levels_above_cutoff = level > 10 ? (level - 10) : 0; - cost = levels_above_cutoff * 300; - cost += level >= 10 ? 100 : 0; - break; - default: - break; - } - } - else if(currency_type == 19) { // cost in Bayle Marks + else if(currency_type == 19) + { + // cost in Bayle Marks cost = 1; } } - return cost/100; + return cost; } + +uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type) { + + uint32 cost = 0; + + MercTemplate *mercData = zone->GetMercTemplate(templateID); + + if(mercData) + { + //calculate cost in coin - cost in gold + if(currency_type == 0) + { + int levels_above_cutoff; + switch (mercData->CostFormula) + { + case 0: + levels_above_cutoff = level > 10 ? (level - 10) : 0; + cost = levels_above_cutoff * 300; + cost += level >= 10 ? 100 : 0; + cost /= 100; + break; + default: + break; + } + } + else if(currency_type == 19) + { + // cost in Bayle Marks + cost = 1; + } + } + + return cost; +} \ No newline at end of file diff --git a/zone/merc.h b/zone/merc.h index 838789f49..66c310759 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -4,12 +4,18 @@ #include "zonedb.h" #include "npc.h" -#define MAXMERCS 1 -#define MERC_DEBUG 0 -#define TANK 1 -#define HEALER 2 -#define MELEEDPS 9 -#define CASTERDPS 12 +#define MAXMERCS 1 +#define MERC_DEBUG 0 +#define TANK 1 +#define HEALER 2 +#define MELEEDPS 9 +#define CASTERDPS 12 + +#define NO_MERC_ID 0 +#define MERC_STATE_NORMAL 5 +#define MERC_STATE_SUSPENDED 1 +#define NOT_SUSPENDED_TIME 0 + const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this enum MercStanceType { @@ -25,19 +31,19 @@ enum MercStanceType { }; struct MercSpell { - uint16 spellid; // <= 0 = no spell - uint32 type; // 0 = never, must be one (and only one) of the defined values - int16 stance; // 0 = all, + = only this stance, - = all except this stance - int16 slot; - uint16 proc_chance; - uint32 time_cancast; // when we can cast this spell next + uint16 spellid; // <= 0 = no spell + uint32 type; // 0 = never, must be one (and only one) of the defined values + int16 stance; // 0 = all, + = only this stance, - = all except this stance + int16 slot; + uint16 proc_chance; + uint32 time_cancast; // when we can cast this spell next }; struct MercTimer { - uint16 timerid; // EndurTimerIndex - uint8 timertype; // 1 = spell, 2 = disc - uint16 spellid; // <= 0 = no spell - uint32 time_cancast; // when we can cast this spell next + uint16 timerid; // EndurTimerIndex + uint8 timertype; // 1 = spell, 2 = disc + uint16 spellid; // <= 0 = no spell + uint32 time_cancast; // when we can cast this spell next }; class Merc : public NPC { @@ -48,8 +54,8 @@ public: //abstract virtual function implementations requird by base abstract class virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill); virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); - virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, - bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); + virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, + bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); virtual bool HasRaid() { return false; } virtual bool HasGroup() { return (GetGroup() ? true : false); } virtual Raid* GetRaid() { return 0; } @@ -126,7 +132,7 @@ public: static Merc* LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, bool updateFromDB = false); void UpdateMercInfo(Client *c); void UpdateMercStats(Client *c); - void UpdateMercAppearance(Client *c); + void UpdateMercAppearance(); void AddItem(uint8 slot, uint32 item_id); static const char *GetRandomName(); bool Spawn(Client *owner); @@ -139,8 +145,8 @@ public: bool GetDepop() { return p_depop; } bool IsDead() { return GetHP() < 0;}; - bool IsMedding() {return _medding; }; - bool IsSuspended() {return _suspended; }; + bool IsMedding() { return _medding; }; + bool IsSuspended() { return _suspended; }; static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); @@ -164,75 +170,75 @@ public: inline const uint8 GetClientVersion() const { return _OwnerClientVersion; } virtual void SetTarget(Mob* mob); - bool HasSkill(SkillUseTypes skill_id) const; - bool CanHaveSkill(SkillUseTypes skill_id) const; - uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; - inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + bool HasSkill(SkillUseTypes skill_id) const; + bool CanHaveSkill(SkillUseTypes skill_id) const; + uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } virtual void DoClassAttacks(Mob *target); - void CheckHateList(); - bool CheckTaunt(); - bool CheckAETaunt(); - bool CheckConfidence(); - bool TryHide(); + void CheckHateList(); + bool CheckTaunt(); + bool CheckAETaunt(); + bool CheckConfidence(); + bool TryHide(); // stat functions virtual void CalcBonuses(); - int32 GetEndurance() const {return cur_end;} //This gets our current endurance - inline virtual int32 GetAC() const { return AC; } + int32 GetEndurance() const {return cur_end;} //This gets our current endurance + inline virtual int32 GetAC() const { return AC; } inline virtual int32 GetATK() const { return ATK; } inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } int32 GetRawACNoShield(int &shield_ac) const; - inline virtual int32 GetSTR() const { return STR; } - inline virtual int32 GetSTA() const { return STA; } - inline virtual int32 GetDEX() const { return DEX; } - inline virtual int32 GetAGI() const { return AGI; } - inline virtual int32 GetINT() const { return INT; } - inline virtual int32 GetWIS() const { return WIS; } - inline virtual int32 GetCHA() const { return CHA; } - inline virtual int32 GetMR() const { return MR; } - inline virtual int32 GetFR() const { return FR; } - inline virtual int32 GetDR() const { return DR; } - inline virtual int32 GetPR() const { return PR; } - inline virtual int32 GetCR() const { return CR; } - inline virtual int32 GetCorrup() const { return Corrup; } + inline virtual int32 GetSTR() const { return STR; } + inline virtual int32 GetSTA() const { return STA; } + inline virtual int32 GetDEX() const { return DEX; } + inline virtual int32 GetAGI() const { return AGI; } + inline virtual int32 GetINT() const { return INT; } + inline virtual int32 GetWIS() const { return WIS; } + inline virtual int32 GetCHA() const { return CHA; } + inline virtual int32 GetMR() const { return MR; } + inline virtual int32 GetFR() const { return FR; } + inline virtual int32 GetDR() const { return DR; } + inline virtual int32 GetPR() const { return PR; } + inline virtual int32 GetCR() const { return CR; } + inline virtual int32 GetCorrup() const { return Corrup; } - inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } - inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } - inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } - inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } - inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } - inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } - inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } - inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } - inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } - inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } - inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } - inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } - inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } + inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } + inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } + inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } + inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } + inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } + inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } + inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } + inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } + inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } + inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } + inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } + inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } // Mod2 - inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } - inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } - inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } - inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } - inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } - inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } - inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } - inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } - inline virtual int32 GetDS() const { return itembonuses.DamageShield; } + inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } + inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } + inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } + inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } + inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } + inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } + inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } + inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } + inline virtual int32 GetDS() const { return itembonuses.DamageShield; } // Mod3 - inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } - inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } - inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } - inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } + inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } + inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } + inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } + inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } - inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } - inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } - inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } - inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } - inline virtual int32 GetWindMod() const { return itembonuses.windMod; } + inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } + inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } + inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } + inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } + inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } // "SET" Class Methods void SetMercData (uint32 templateID ); @@ -267,68 +273,69 @@ protected: void AddItemBonuses(const Item_Struct *item, StatBonuses* newbon); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); - int16 GetFocusEffect(focusType type, uint16 spell_id); + int16 GetFocusEffect(focusType type, uint16 spell_id); std::vector merc_spells; std::map timers; - uint16 skills[HIGHEST_SKILL+1]; - uint32 equipment[EmuConstants::EQUIPMENT_SIZE]; //this is an array of item IDs - uint16 d_meele_texture1; //this is an item Material value - uint16 d_meele_texture2; //this is an item Material value (offhand) - uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation - uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation + uint16 skills[HIGHEST_SKILL+1]; + uint32 equipment[EmuConstants::EQUIPMENT_SIZE]; //this is an array of item IDs + uint16 d_meele_texture1; //this is an item Material value + uint16 d_meele_texture2; //this is an item Material value (offhand) + uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation + uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation private: - int32 CalcAC(); - int32 GetACMit(); - int32 GetACAvoid(); - int32 acmod(); - int32 CalcATK(); - //int CalcHaste(); + int32 CalcAC(); + int32 GetACMit(); + int32 GetACAvoid(); + int32 acmod(); + int32 CalcATK(); + //int CalcHaste(); - int32 CalcSTR(); - int32 CalcSTA(); - int32 CalcDEX(); - int32 CalcAGI(); - int32 CalcINT(); - int32 CalcWIS(); - int32 CalcCHA(); + int32 CalcSTR(); + int32 CalcSTA(); + int32 CalcDEX(); + int32 CalcAGI(); + int32 CalcINT(); + int32 CalcWIS(); + int32 CalcCHA(); - int32 CalcMR(); - int32 CalcFR(); - int32 CalcDR(); - int32 CalcPR(); - int32 CalcCR(); - int32 CalcCorrup(); - int32 CalcMaxHP(); - int32 CalcBaseHP(); - int32 GetClassHPFactor(); - int32 CalcHPRegen(); - int32 CalcHPRegenCap(); - int32 CalcMaxMana(); - int32 CalcBaseMana(); - int32 CalcManaRegen(); - int32 CalcBaseManaRegen(); - int32 CalcManaRegenCap(); - void CalcMaxEndurance(); //This calculates the maximum endurance we can have - int32 CalcBaseEndurance(); //Calculates Base End - int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call - int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() - int32 CalcEnduranceRegenCap(); - void SetEndurance(int32 newEnd); //This sets the current endurance to the new value - void DoEnduranceUpkeep(); //does the endurance upkeep - void CalcRestState(); + int32 CalcMR(); + int32 CalcFR(); + int32 CalcDR(); + int32 CalcPR(); + int32 CalcCR(); + int32 CalcCorrup(); + int32 CalcMaxHP(); + int32 CalcBaseHP(); + int32 GetClassHPFactor(); + int32 CalcHPRegen(); + int32 CalcHPRegenCap(); + int32 CalcMaxMana(); + int32 CalcBaseMana(); + int32 CalcManaRegen(); + int32 CalcBaseManaRegen(); + int32 CalcManaRegenCap(); + void CalcMaxEndurance(); //This calculates the maximum endurance we can have + int32 CalcBaseEndurance(); //Calculates Base End + int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call + int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() + int32 CalcEnduranceRegenCap(); + void SetEndurance(int32 newEnd); //This sets the current endurance to the new value + void DoEnduranceUpkeep(); //does the endurance upkeep + void CalcRestState(); - int GroupLeadershipAAHealthEnhancement(); - int GroupLeadershipAAManaEnhancement(); - int GroupLeadershipAAHealthRegeneration(); - int GroupLeadershipAAOffenseEnhancement(); + int GroupLeadershipAAHealthEnhancement(); + int GroupLeadershipAAManaEnhancement(); + int GroupLeadershipAAHealthRegeneration(); + int GroupLeadershipAAOffenseEnhancement(); void GetMercSize(); void GenerateAppearance(); + bool LoadMercSpells(); bool CheckStance(int16 stance); std::vector GetMercSpells() { return merc_spells; } @@ -344,8 +351,8 @@ private: uint32 _baseWIS; uint32 _baseCHA; uint32 _baseATK; - uint32 _baseRace; // Necessary to preserve the race otherwise mercs get their race updated in the db when they get an illusion. - uint8 _baseGender; // Merc gender. Necessary to preserve the original value otherwise it can be changed by illusions. + uint32 _baseRace; // Necessary to preserve the race otherwise mercs get their race updated in the db when they get an illusion. + uint8 _baseGender; // Merc gender. Necessary to preserve the original value otherwise it can be changed by illusions. uint32 _baseMR; uint32 _baseCR; uint32 _baseDR; @@ -368,19 +375,19 @@ private: uint32 _currentStance; Inventory m_inv; - int32 max_end; - int32 cur_end; - bool _medding; - bool _suspended; - bool p_depop; - bool _check_confidence; - bool _lost_confidence; - int _hatedCount; - uint32 owner_char_id; - Timer endupkeep_timer; - Timer rest_timer; - Timer confidence_timer; - Timer check_target_timer; + int32 max_end; + int32 cur_end; + bool _medding; + bool _suspended; + bool p_depop; + bool _check_confidence; + bool _lost_confidence; + int _hatedCount; + uint32 owner_char_id; + Timer endupkeep_timer; + Timer rest_timer; + Timer confidence_timer; + Timer check_target_timer; }; -#endif // MERC_H +#endif // MERC_H \ No newline at end of file diff --git a/zone/zone.cpp b/zone/zone.cpp index 62f3c1e8a..7a4ad8bac 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -400,7 +400,7 @@ void Zone::LoadTempMerchantData() { LogFile->write(EQEMuLog::Status, "Loading Temporary Merchant Lists..."); std::string query = StringFormat( "SELECT " - "ml.npcid, " + "DISTINCT ml.npcid, " "ml.slot, " "ml.charges, " "ml.itemid " diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index a1cf4ecfb..fa34e8d9e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2107,9 +2107,6 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client bool ZoneDatabase::LoadMercInfo(Client *client) { - if(client->GetEPP().merc_name[0] == 0) - return false; - std::string query = StringFormat("SELECT MercID, Slot, Name, TemplateID, SuspendedTime, " "IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, " "Endurance, Face, LuclinHairStyle, LuclinHairColor, " @@ -2119,6 +2116,9 @@ bool ZoneDatabase::LoadMercInfo(Client *client) { auto results = QueryDatabase(query); if (!results.Success()) return false; + + if(results.RowCount() == 0) + return false; for (auto row = results.begin(); row != results.end(); ++row) { uint8 slot = atoi(row[1]); @@ -2156,9 +2156,6 @@ bool ZoneDatabase::LoadMercInfo(Client *client) { bool ZoneDatabase::LoadCurrentMerc(Client *client) { - if(client->GetEPP().merc_name[0] == 0) - return false; - uint8 slot = client->GetMercSlot(); if(slot > MAXMERCS) @@ -2175,6 +2172,9 @@ bool ZoneDatabase::LoadCurrentMerc(Client *client) { if(!results.Success()) return false; + + if(results.RowCount() == 0) + return false; for (auto row = results.begin(); row != results.end(); ++row) { @@ -2211,71 +2211,73 @@ bool ZoneDatabase::SaveMerc(Merc *merc) { if(!owner) return false; - if(merc->GetMercID() == 0) { + if(merc->GetMercID() == 0) + { // New merc record - uint32 TempNewMercID = 0; std::string query = StringFormat("INSERT INTO mercs " - "(OwnerCharacterID, Slot, Name, TemplateID, " - "SuspendedTime, IsSuspended, TimerRemaining, " - "Gender, StanceID, HP, Mana, Endurance, Face, " - "LuclinHairStyle, LuclinHairColor, LuclinEyeColor, " - "LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " - "DrakkinHeritage, DrakkinTattoo, DrakkinDetails) " - "VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', " - "'%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', " - "'%i', '%i', '%i', '%i', '%i', '%i', '%i')", - merc->GetMercCharacterID(), owner->GetNumMercs(), - merc->GetCleanName(), merc->GetMercTemplateID(), - owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), - owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), - merc->GetStance(), merc->GetHP(), merc->GetMana(), - merc->GetEndurance(), merc->GetLuclinFace(), - merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), - merc->GetEyeColor2(), merc->GetBeardColor(), - merc->GetBeard(), merc->GetDrakkinHeritage(), - merc->GetDrakkinTattoo(), merc->GetDrakkinDetails()); - auto results = database.QueryDatabase(query); + "(OwnerCharacterID, Slot, Name, TemplateID, " + "SuspendedTime, IsSuspended, TimerRemaining, " + "Gender, StanceID, HP, Mana, Endurance, Face, " + "LuclinHairStyle, LuclinHairColor, LuclinEyeColor, " + "LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails) " + "VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', " + "'%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i')", + merc->GetMercCharacterID(), owner->GetNumMercs(), + merc->GetCleanName(), merc->GetMercTemplateID(), + owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), + owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), + merc->GetStance(), merc->GetHP(), merc->GetMana(), + merc->GetEndurance(), merc->GetLuclinFace(), + merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), + merc->GetEyeColor2(), merc->GetBeardColor(), + merc->GetBeard(), merc->GetDrakkinHeritage(), + merc->GetDrakkinTattoo(), merc->GetDrakkinDetails()); + + auto results = database.QueryDatabase(query); if(!results.Success()) { owner->Message(13, results.ErrorMessage().c_str()); return false; } else if (results.RowsAffected() != 1) { - owner->Message(13, "Unable to save merc to the database."); - return false; + owner->Message(13, "Unable to save merc to the database."); + return false; } - merc->SetMercID(TempNewMercID); - merc->UpdateMercInfo(owner); + merc->SetMercID(results.LastInsertedID()); + merc->UpdateMercInfo(owner); database.SaveMercBuffs(merc); - return true; + return true; } - // Update existing merc record - std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', " - "Name = '%s', TemplateID = '%u', SuspendedTime = '%u', " - "IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', " - "StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', " - "Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', " - "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', " - "LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', " - "DrakkinDetails = '%i' WHERE MercID = '%u'", - merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), - merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, - merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, - merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), - merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), - merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), - merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), - merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - owner->Message(13, results.ErrorMessage().c_str()); - return false; - } else if (results.RowsAffected() != 1) { - owner->Message(13, "Unable to save merc to the database."); - return false; - } + // Update existing merc record + std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', " + "Name = '%s', TemplateID = '%u', SuspendedTime = '%u', " + "IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', " + "StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', " + "Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', " + "LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', " + "DrakkinDetails = '%i' WHERE MercID = '%u'", + merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), + merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, + merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, + merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), + merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), + merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), + merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), + merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID()); - merc->UpdateMercInfo(owner); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + owner->Message(13, results.ErrorMessage().c_str()); + return false; + } else if (results.RowsAffected() != 1) { + owner->Message(13, "Unable to save merc to the database."); + return false; + } + + merc->UpdateMercInfo(owner); database.SaveMercBuffs(merc); return true; @@ -2386,25 +2388,27 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { bool ZoneDatabase::DeleteMerc(uint32 merc_id) { if(merc_id == 0) - return false; + return false; - bool firstQueryWorked = false; - // TODO: These queries need to be ran together as a transaction.. ie, - // if one or more fail then they all will fail to commit to the database. - std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercID = '%u'", merc_id); - auto results = database.QueryDatabase(query); + // TODO: These queries need to be ran together as a transaction.. ie, + // if one or more fail then they all will fail to commit to the database. + // ...Not all mercs will have buffs, so why is it required that both deletes succeed? + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = '%u'", merc_id); + auto results = database.QueryDatabase(query); if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); - else - firstQueryWorked = true; + { + LogFile->write(EQEMuLog::Error, "Error Deleting Merc Buffs: %s", results.ErrorMessage().c_str()); + } query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id); - if(!results.Success()) { + results = database.QueryDatabase(query); + if(!results.Success()) + { LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); return false; - } + } - return firstQueryWorked; + return true; } void ZoneDatabase::LoadMercEquipment(Merc *merc) {