Merge branch 'master' into inv2

This commit is contained in:
KimLS 2015-03-25 15:55:16 -07:00
commit 279ed8d86c
19 changed files with 536 additions and 86 deletions

View File

@ -1,5 +1,16 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 03/12/2015 ==
Akkadius: [eqemu_update.pl V7] Add Option 9) LUA Modules - Download latest LUA Modules (Required for Lua)
== 03/11/2015 ==
Akkadius: [eqemu_update.pl] Add Option 7) Plugins - Download latest Perl plugins
Akkadius: [eqemu_update.pl] Add Option 8) Quests - Download latest PEQ quests and stage updates
Akkadius: [eqemu_update.pl] Set version 5 of script
== 03/10/2015 ==
Akkadius: [eqemu_update.pl] Add Option 6) Download Latest map and water files
== 03/04/2015 == == 03/04/2015 ==
Akkadius: Fix Spell Book Deletion Akkadius: Fix Spell Book Deletion

View File

@ -494,7 +494,7 @@ bool Database::CheckDatabaseConversions() {
/* Check for a new version of this script, the arg passed /* Check for a new version of this script, the arg passed
would have to be higher than the copy they have downloaded would have to be higher than the copy they have downloaded
locally and they will re fetch */ locally and they will re fetch */
system("perl eqemu_update.pl V 2"); system("perl eqemu_update.pl V 7");
/* Run Automatic Database Upgrade Script */ /* Run Automatic Database Upgrade Script */
system("perl eqemu_update.pl ran_from_world"); system("perl eqemu_update.pl ran_from_world");

View File

@ -38,6 +38,7 @@
#define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists.
#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects
#define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] #define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks]
#define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary)
const int Z_AGGRO=10; const int Z_AGGRO=10;

View File

@ -9,12 +9,20 @@
$menu_displayed = 0; $menu_displayed = 0;
use Config; use Config;
use File::Copy qw(copy);
use POSIX qw(strftime);
use File::Path;
use File::Find;
use URI::Escape;
$time_stamp = strftime('%m-%d-%Y', gmtime());
$console_output .= " Operating System is: $Config{osname}\n"; $console_output .= " Operating System is: $Config{osname}\n";
if($Config{osname}=~/linux/i){ $OS = "Linux"; } if($Config{osname}=~/linux/i){ $OS = "Linux"; }
if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; } if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; }
#::: If current version is less than what world is reporting, then download a new one... #::: If current version is less than what world is reporting, then download a new one...
$current_version = 2; $current_version = 7;
if($ARGV[0] eq "V"){ if($ARGV[0] eq "V"){
if($ARGV[1] > $current_version){ if($ARGV[1] > $current_version){
@ -36,8 +44,13 @@ print "Perl Version is " . $perl_version . "\n";
if($perl_version > 5.12){ no warnings 'uninitialized'; } if($perl_version > 5.12){ no warnings 'uninitialized'; }
no warnings; no warnings;
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
#::: Cleanup staged folder...
rmtree("updates_staged/");
my $confile = "eqemu_config.xml"; #default my $confile = "eqemu_config.xml"; #default
open(F, "<$confile") or die "Unable to open config: $confile\n"; open(F, "<$confile");
my $indb = 0; my $indb = 0;
while(<F>) { while(<F>) {
s/\r//g; s/\r//g;
@ -97,12 +110,12 @@ if($path eq ""){
mkdir('db_update'); mkdir('db_update');
#::: Check if db_version table exists... #::: Check if db_version table exists...
if(trim(GetMySQLResult("SHOW COLUMNS FROM db_version LIKE 'Revision'")) ne ""){ if(trim(GetMySQLResult("SHOW COLUMNS FROM db_version LIKE 'Revision'")) ne "" && $db){
print GetMySQLResult("DROP TABLE db_version"); print GetMySQLResult("DROP TABLE db_version");
print "Old db_version table present, dropping...\n\n"; print "Old db_version table present, dropping...\n\n";
} }
if(GetMySQLResult("SHOW TABLES LIKE 'db_version'") eq ""){ if(GetMySQLResult("SHOW TABLES LIKE 'db_version'") eq "" && $db){
print GetMySQLResult(" print GetMySQLResult("
CREATE TABLE db_version ( CREATE TABLE db_version (
version int(11) DEFAULT '0' version int(11) DEFAULT '0'
@ -123,24 +136,24 @@ if($bin_db_ver == $local_db_ver && $ARGV[0] eq "ran_from_start"){
exit; exit;
} }
else{ else{
print $console_output; print $console_output if $db;
} }
if($db){
print " Binary Database Version: (" . $bin_db_ver . ")\n";
print " Local Database Version: (" . $local_db_ver . ")\n\n";
print " Binary Database Version: (" . $bin_db_ver . ")\n"; #::: If World ran this script, and our version is up to date, continue...
print " Local Database Version: (" . $local_db_ver . ")\n\n"; 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 World ran this script, and our version is up to date, continue... print "Retrieving latest database manifest...\n";
if($bin_db_ver <= $local_db_ver && $ARGV[0] eq "ran_from_world"){ GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt");
print " Database up to Date: Continuing World Bootup...\n";
print "============================================================\n";
exit;
} }
print "Retrieving latest database manifest...\n";
GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt");
# GetRemoteFile("https://dl.dropboxusercontent.com/u/50023467/dl/db_update_manifest.txt", "db_update/db_update_manifest.txt");
if($local_db_ver < $bin_db_ver && $ARGV[0] eq "ran_from_world"){ 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"; print "You have missing database updates, type 1 or 2 to backup your database before running them as recommended...\n\n";
#::: Display Menu #::: Display Menu
@ -152,6 +165,10 @@ else{
ShowMenuPrompt(); ShowMenuPrompt();
} }
sub UpdateSelf{
GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_update.pl", "eqemu_update.pl");
die "Rerun eqemu_update.pl";
}
sub ShowMenuPrompt { sub ShowMenuPrompt {
my %dispatch = ( my %dispatch = (
@ -160,6 +177,11 @@ sub ShowMenuPrompt {
3 => \&Run_Database_Check, 3 => \&Run_Database_Check,
4 => \&AA_Fetch, 4 => \&AA_Fetch,
5 => \&OpCodes_Fetch, 5 => \&OpCodes_Fetch,
6 => \&MapFiles_Fetch,
7 => \&Plugins_Fetch,
8 => \&QuestFiles_Fetch,
9 => \&LUA_Modules_Fetch,
20 => \&UpdateSelf,
0 => \&Exit, 0 => \&Exit,
); );
@ -198,25 +220,30 @@ sub ShowMenuPrompt {
} }
sub MenuOptions { sub MenuOptions {
if(@total_updates){ if(@total_updates){
$option[3] = "Run pending REQUIRED updates... (" . scalar (@total_updates) . ")"; $option[3] = "Run pending REQUIRED updates... (" . scalar (@total_updates) . ")";
} }
else{ else{
$option[3] = "Check for pending REQUIRED Database updates $option[3] = "Check and stage pending REQUIRED Database updates";
Stages updates for automatic upgrade...";
} }
return <<EO_MENU; return <<EO_MENU;
Database Management Menu (Please Select): EQEmu Update Utility Menu:
1) Backup Database - (Saves to Backups folder) 1) Backup Database - (Saves to Backups folder)
Ideal to perform before performing updates
2) Backup Database Compressed - (Saves to Backups folder) 2) Backup Database Compressed - (Saves to Backups folder)
Ideal to perform before performing updates
3) $option[3] 3) $option[3]
4) AAs - Get Latest AA's from PEQ (This deletes AA's already in the database) 4) AAs - Download Latest AA's from PEQ (This overwrites existing data)
5) OPCodes - Download latest opcodes from repository 5) OPCodes - Download latest opcodes
6) Maps - Download latest map and water files
7) Plugins - Download latest Perl plugins
8) Quests - Download latest PEQ quests and stage updates
9) LUA Modules - Download latest LUA Modules (Required for Lua)
20) Force update this script (Redownload)
0) Exit 0) Exit
Enter numbered option and press enter...
EO_MENU EO_MENU
} }
@ -249,6 +276,7 @@ sub Exit{ }
#::: Returns Tab Delimited MySQL Result from Command Line #::: Returns Tab Delimited MySQL Result from Command Line
sub GetMySQLResult{ sub GetMySQLResult{
my $run_query = $_[0]; my $run_query = $_[0];
if(!$db){ return; }
if($OS eq "Windows"){ return `"$path" --host $host --user $user --password="$pass" $db -N -B -e "$run_query"`; } if($OS eq "Windows"){ return `"$path" --host $host --user $user --password="$pass" $db -N -B -e "$run_query"`; }
if($OS eq "Linux"){ if($OS eq "Linux"){
$run_query =~s/`//g; $run_query =~s/`//g;
@ -258,6 +286,7 @@ sub GetMySQLResult{
sub GetMySQLResultFromFile{ sub GetMySQLResultFromFile{
my $update_file = $_[0]; my $update_file = $_[0];
if(!$db){ return; }
if($OS eq "Windows"){ return `"$path" --host $host --user $user --password="$pass" --force $db < $update_file`; } if($OS eq "Windows"){ return `"$path" --host $host --user $user --password="$pass" --force $db < $update_file`; }
if($OS eq "Linux"){ return `"$path" --host $host --user $user --password="$pass" --force $db < $update_file`; } if($OS eq "Linux"){ return `"$path" --host $host --user $user --password="$pass" --force $db < $update_file`; }
} }
@ -267,34 +296,67 @@ sub GetMySQLResultFromFile{
sub GetRemoteFile{ sub GetRemoteFile{
my $URL = $_[0]; my $URL = $_[0];
my $Dest_File = $_[1]; my $Dest_File = $_[1];
my $content_type = $_[2];
#::: Build file path of the destination file so that we may check for the folder's existence and make it if necessary
if($Dest_File=~/\//i){
my @dir_path = split('/', $Dest_File);
$build_path = "";
$di = 0;
while($dir_path[$di]){
$build_path .= $dir_path[$di] . "/";
#::: If path does not exist, create the directory...
if (!-d $build_path) {
mkdir($build_path);
}
if(!$dir_path[$di + 2] && $dir_path[$di + 1]){
# print $actual_path . "\n";
$actual_path = $build_path;
last;
}
$di++;
}
}
if($OS eq "Windows"){ if($OS eq "Windows"){
require LWP::UserAgent; #::: For non-text type requests...
my $ua = LWP::UserAgent->new; if($content_type == 1){
$ua->timeout(10); use LWP::Simple qw(getstore);
$ua->env_proxy; if(!getstore($URL, $Dest_File)){
my $response = $ua->get($URL); print "Error, no connection or failed request...\n\n";
}
if ($response->is_success){ else{
open (FILE, '> ' . $Dest_File . ''); print " o URL: (" . $URL . ")\n";
print FILE $response->decoded_content; print " o Saved: (" . $Dest_File . ") \n";
close (FILE); }
print " URL: " . $URL . "\n";
print " Saved: " . $Dest_File . " \n";
} }
else { else{
print "Error, no connection to the internet...\n\n"; require LWP::UserAgent;
die $response->status_line; my $ua = LWP::UserAgent->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 " o URL: (" . $URL . ")\n";
print " o Saved: (" . $Dest_File . ") \n";
}
else {
print "Error, no connection or failed request...\n\n";
}
} }
} }
if($OS eq "Linux"){ 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 -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt
$wget = `wget --no-check-certificate --quiet -O $Dest_File $URL`; $wget = `wget --no-check-certificate --quiet -O $Dest_File $URL`;
print " URL: " . $URL . "\n"; print " o URL: (" . $URL . ")\n";
print " Saved: " . $Dest_File . " \n"; print " o Saved: (" . $Dest_File . ") \n";
if($wget=~/unable to resolve/i){ if($wget=~/unable to resolve/i){
print "Error, no connection to the internet...\n\n"; print "Error, no connection or failed request...\n\n";
die; #die;
} }
} }
} }
@ -309,6 +371,11 @@ sub trim {
#::: Fetch Latest PEQ AA's #::: Fetch Latest PEQ AA's
sub AA_Fetch{ sub AA_Fetch{
if(!$db){
print "No database present, check your eqemu_config.xml for proper MySQL/MariaDB configuration...\n";
return;
}
print "Pulling down PEQ AA Tables...\n"; print "Pulling down PEQ AA Tables...\n";
GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/peq_aa_tables.sql", "db_update/peq_aa_tables.sql"); GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/peq_aa_tables.sql", "db_update/peq_aa_tables.sql");
print "\n\nInstalling AA Tables...\n"; print "\n\nInstalling AA Tables...\n";
@ -341,13 +408,292 @@ sub OpCodes_Fetch{
print "\nDownloading (" . $opcodes{$loop}[0] . ") File: '" . $file_name . "'...\n\n"; print "\nDownloading (" . $opcodes{$loop}[0] . ") File: '" . $file_name . "'...\n\n";
GetRemoteFile($opcodes{$loop}[1], $file_name); GetRemoteFile($opcodes{$loop}[1], $file_name);
$loop++; $loop++;
} }
print "\nDone...\n\n"; print "\nDone...\n\n";
} }
sub CopyFile{
$l_source_file = $_[0];
$l_dest_file = $_[1];
if($l_dest_file=~/\//i){
my @dir_path = split('/', $l_dest_file);
$build_path = "";
$di = 0;
while($dir_path[$di]){
$build_path .= $dir_path[$di] . "/";
#::: If path does not exist, create the directory...
if (!-d $build_path) {
mkdir($build_path);
}
if(!$dir_path[$di + 2] && $dir_path[$di + 1]){
# print $actual_path . "\n";
$actual_path = $build_path;
last;
}
$di++;
}
}
copy $l_source_file, $l_dest_file;
}
sub MapFiles_Fetch{
print "\n --- Fetching Latest Maps --- \n";
GetRemoteFile("https://raw.githubusercontent.com/Akkadius/EQEmuMaps/master/!eqemu_maps_manifest.txt", "updates_staged/eqemu_maps_manifest.txt");
#::: Get Data from manifest
open (FILE, "updates_staged/eqemu_maps_manifest.txt");
$i = 0;
while (<FILE>){
chomp;
$o = $_;
@manifest_map_data = split(',', $o);
if($manifest_map_data[0] ne ""){
$maps_manifest[$i] = [$manifest_map_data[0], $manifest_map_data[1]];
$i++;
}
}
#::: Download
$fc = 0;
for($m = 0; $m <= $i; $m++){
my $file_existing = $maps_manifest[$m][0];
my $file_existing_size = (stat $file_existing)[7];
if($file_existing_size != $maps_manifest[$m][1]){
print "Updating: '" . $maps_manifest[$m][0] . "'\n";
GetRemoteFile("https://raw.githubusercontent.com/Akkadius/EQEmuMaps/master/" . $maps_manifest[$m][0], $maps_manifest[$m][0], 1);
$fc++;
}
}
if($fc == 0){
print "\nNo Map Updates found... \n\n";
}
}
sub QuestFiles_Fetch{
if (!-e "updates_staged/Quests-Plugins-master/quests/") {
print "\n --- Fetching Latest Quests --- \n";
GetRemoteFile("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
print "\nFetched latest quests...\n";
mkdir('updates_staged');
UnZip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
}
$fc = 0;
use File::Find;
use File::Compare;
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/quests/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
);
for my $file (@files) {
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$dest_file = $file;
$dest_file =~s/updates_staged\/Quests-Plugins-master\///g;
if (!-e $dest_file) {
CopyFile($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n";
$fc++;
}
else{
$diff = Diff($dest_file, $staged_file);
if($diff ne ""){
$backup_dest = "updates_backups/" . $time_stamp . "/" . $dest_file;
print $diff . "\n";
print "\nFile Different :: '" . $dest_file . "'\n";
print "\nDo you wish to update this Quest? '" . $dest_file . "' [Yes (Enter) - No (N)] \nA backup will be found in '" . $backup_dest . "'\n";
my $input = <STDIN>;
if($input=~/N/i){}
else{
#::: Make a backup
CopyFile($dest_file, $backup_dest);
#::: Copy staged to running
copy($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n\n";
}
$fc++;
}
}
}
}
if($fc == 0){
print "\nNo Quest Updates found... \n\n";
}
}
sub LUA_Modules_Fetch{
if (!-e "updates_staged/Quests-Plugins-master/quests/lua_modules/") {
print "\n --- Fetching Latest LUA Modules --- \n";
GetRemoteFile("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
print "\nFetched latest LUA Modules...\n";
UnZip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
}
$fc = 0;
use File::Find;
use File::Compare;
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/quests/lua_modules/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
);
for my $file (@files) {
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$dest_file = $file;
$dest_file =~s/updates_staged\/Quests-Plugins-master\/quests\///g;
if (!-e $dest_file) {
CopyFile($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n";
$fc++;
}
else{
$diff = Diff($dest_file, $staged_file);
if($diff ne ""){
$backup_dest = "updates_backups/" . $time_stamp . "/" . $dest_file;
print $diff . "\n";
print "\nFile Different :: '" . $dest_file . "'\n";
print "\nDo you wish to update this LUA Module? '" . $dest_file . "' [Yes (Enter) - No (N)] \nA backup will be found in '" . $backup_dest . "'\n";
my $input = <STDIN>;
if($input=~/N/i){}
else{
#::: Make a backup
CopyFile($dest_file, $backup_dest);
#::: Copy staged to running
copy($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n\n";
}
$fc++;
}
}
}
}
if($fc == 0){
print "\nNo LUA Modules Updates found... \n\n";
}
}
sub Plugins_Fetch{
if (!-e "updates_staged/Quests-Plugins-master/plugins/") {
print "\n --- Fetching Latest Plugins --- \n";
GetRemoteFile("https://github.com/EQEmu/Quests-Plugins/archive/master.zip", "updates_staged/Quests-Plugins-master.zip", 1);
print "\nFetched latest plugins...\n";
UnZip('updates_staged/Quests-Plugins-master.zip', 'updates_staged/');
}
$fc = 0;
use File::Find;
use File::Compare;
my @files;
my $start_dir = "updates_staged/Quests-Plugins-master/plugins/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
);
for my $file (@files) {
if($file=~/\.pl|\.lua|\.ext/i){
$staged_file = $file;
$dest_file = $file;
$dest_file =~s/updates_staged\/Quests-Plugins-master\///g;
if (!-e $dest_file) {
CopyFile($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n";
$fc++;
}
else{
$diff = Diff($dest_file, $staged_file);
if($diff ne ""){
$backup_dest = "updates_backups/" . $time_stamp . "/" . $dest_file;
print $diff . "\n";
print "\nFile Different :: '" . $dest_file . "'\n";
print "\nDo you wish to update this Plugin? '" . $dest_file . "' [Yes (Enter) - No (N)] \nA backup will be found in '" . $backup_dest . "'\n";
my $input = <STDIN>;
if($input=~/N/i){}
else{
#::: Make a backup
CopyFile($dest_file, $backup_dest);
#::: Copy staged to running
copy($staged_file, $dest_file);
print "Installing :: '" . $dest_file . "'\n\n";
}
$fc++;
}
}
}
}
if($fc == 0){
print "\nNo Plugin Updates found... \n\n";
}
}
sub Diff{
$file_1 = $_[0];
$file_2 = $_[1];
if($OS eq "Windows"){
eval "use Text::Diff";
$diff = diff($file_1, $file_2, { STYLE => "Unified" });
return $diff;
}
if($OS eq "Linux"){
# print 'diff -u "$file_1" "$file_2"' . "\n";
return `diff -u "$file_1" "$file_2"`;
}
}
sub UnZip{
$archive_to_unzip = $_[0];
$dest_folder = $_[1];
if($OS eq "Windows"){
eval "use Archive::Zip qw( :ERROR_CODES :CONSTANTS )";
my $zip = Archive::Zip->new();
unless ( $zip->read($archive_to_unzip) == AZ_OK ) {
die 'read error';
}
print "Extracting...\n";
$zip->extractTree('', $dest_folder);
}
if($OS eq "Linux"){
print `unzip -o "$archive_to_unzip" -d "$dest_folder"`;
}
}
sub AreFileSizesDifferent{
$file_1 = $_[0];
$file_2 = $_[1];
my $file_1 = (stat $file_1)[7];
my $file_2 = (stat $file_2)[7];
# print $file_1 . " :: " . $file_2 . "\n";
if($file_1 != $file_2){
return 1;
}
return;
}
#::: Responsible for Database Upgrade Routines #::: Responsible for Database Upgrade Routines
sub Run_Database_Check{ sub Run_Database_Check{
if(!$db){
print "No database present, check your eqemu_config.xml for proper MySQL/MariaDB configuration...\n";
return;
}
#::: Run 2 - Running pending updates... #::: Run 2 - Running pending updates...
if(defined(@total_updates)){ if(defined(@total_updates)){
@total_updates = sort @total_updates; @total_updates = sort @total_updates;

View File

@ -3650,6 +3650,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
(frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) && (frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) &&
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) { !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) {
Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist); Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist);
} else { } else {
// Normal stun resist check. // Normal stun resist check.
if (stun_resist && zone->random.Roll(stun_resist)) { if (stun_resist && zone->random.Roll(stun_resist)) {
@ -3994,7 +3995,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const ItemData *weapon, Mob *on, u
// We can proc once here, either weapon or one aug // We can proc once here, either weapon or one aug
bool proced = false; // silly bool to prevent augs from going if weapon does bool proced = false; // silly bool to prevent augs from going if weapon does
skillinuse = GetSkillByItemType(weapon->ItemType); skillinuse = GetSkillByItemType(weapon->ItemType);
if (weapon->Proc.Type == ET_CombatProc) { if (weapon->Proc.Type == ET_CombatProc && IsValidSpell(weapon->Proc.Effect)) {
float WPC = ProcChance * (100.0f + // Proc chance for this weapon float WPC = ProcChance * (100.0f + // Proc chance for this weapon
static_cast<float>(weapon->ProcRate)) / 100.0f; static_cast<float>(weapon->ProcRate)) / 100.0f;
if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
@ -4032,7 +4033,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const ItemData *weapon, Mob *on, u
if (!aug) if (!aug)
continue; continue;
if (aug->Proc.Type == ET_CombatProc) { if (aug->Proc.Type == ET_CombatProc && IsValidSpell(aug->Proc.Effect)) {
float APC = ProcChance * (100.0f + // Proc chance for this aug float APC = ProcChance * (100.0f + // Proc chance for this aug
static_cast<float>(aug->ProcRate)) / 100.0f; static_cast<float>(aug->ProcRate)) / 100.0f;
if (zone->random.Roll(APC)) { if (zone->random.Roll(APC)) {

View File

@ -1291,7 +1291,7 @@ protected:
bool client_data_loaded; bool client_data_loaded;
int16 GetFocusEffect(focusType type, uint16 spell_id); int16 GetFocusEffect(focusType type, uint16 spell_id);
int16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id);
Mob* bind_sight_target; Mob* bind_sight_target;

View File

@ -2607,7 +2607,7 @@ void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app)
++iter; ++iter;
} }
if (item_id == 0) { if (item_id == 0 || reclaim->count == 0) {
return; return;
} }
@ -13319,6 +13319,10 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
if (database.GetItem(gis->Items[i])) { if (database.GetItem(gis->Items[i])) {
database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i], database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i],
gis->Charges[i], ints->ItemCost[i], i); gis->Charges[i], ints->ItemCost[i], i);
auto inst = FindTraderItemBySerialNumber(gis->SerialNumber[i]);
if(inst)
inst->SetPrice(ints->ItemCost[i]);
} }
else { else {
//return; //sony doesnt memset so assume done on first bad item //return; //sony doesnt memset so assume done on first bad item

View File

@ -3429,6 +3429,16 @@ XS(XS__clear_npctype_cache)
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
XS(XS__reloadzonestaticdata);
XS(XS__reloadzonestaticdata)
{
dXSARGS;
quest_manager.ReloadZoneStaticData();
XSRETURN_EMPTY;
}
XS(XS__qs_send_query); XS(XS__qs_send_query);
XS(XS__qs_send_query) XS(XS__qs_send_query)
{ {
@ -3686,6 +3696,7 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file); newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file);
newXS(strcpy(buf, "rain"), XS__rain, file); newXS(strcpy(buf, "rain"), XS__rain, file);
newXS(strcpy(buf, "rebind"), XS__rebind, file); newXS(strcpy(buf, "rebind"), XS__rebind, file);
newXS(strcpy(buf, "reloadzonestaticdata"), XS__reloadzonestaticdata, file);
newXS(strcpy(buf, "removetitle"), XS__removetitle, file); newXS(strcpy(buf, "removetitle"), XS__removetitle, file);
newXS(strcpy(buf, "repopzone"), XS__repopzone, file); newXS(strcpy(buf, "repopzone"), XS__repopzone, file);
newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file); newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file);

View File

@ -1242,6 +1242,10 @@ void lua_clear_npctype_cache(int npctype_id) {
quest_manager.ClearNPCTypeCache(npctype_id); quest_manager.ClearNPCTypeCache(npctype_id);
} }
void lua_reloadzonestaticdata() {
quest_manager.ReloadZoneStaticData();
}
double lua_clock() { double lua_clock() {
timeval read_time; timeval read_time;
gettimeofday(&read_time, nullptr); gettimeofday(&read_time, nullptr);
@ -1592,6 +1596,7 @@ luabind::scope lua_register_general() {
luabind::def("enable_recipe", &lua_enable_recipe), luabind::def("enable_recipe", &lua_enable_recipe),
luabind::def("disable_recipe", &lua_disable_recipe), luabind::def("disable_recipe", &lua_disable_recipe),
luabind::def("clear_npctype_cache", &lua_clear_npctype_cache), luabind::def("clear_npctype_cache", &lua_clear_npctype_cache),
luabind::def("reloadzonestaticdata", &lua_reloadzonestaticdata),
luabind::def("clock", &lua_clock), luabind::def("clock", &lua_clock),
luabind::def("create_npc", &lua_create_npc), luabind::def("create_npc", &lua_create_npc),
luabind::def("debug", (void(*)(std::string))&lua_debug), luabind::def("debug", (void(*)(std::string))&lua_debug),

View File

@ -3616,36 +3616,41 @@ bool Mob::TryFadeEffect(int slot)
void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
{ {
if(target == nullptr || !IsValidSpell(spell_id)) if(target == nullptr || !IsValidSpell(spell_id) || !IsClient())
return; return;
int focus_spell = CastToClient()->GetSympatheticFocusEffect(focusSympatheticProc,spell_id); uint16 focus_spell = CastToClient()->GetSympatheticFocusEffect(focusSympatheticProc,spell_id);
if(IsValidSpell(focus_spell)){ if(!IsValidSpell(focus_spell))
int focus_trigger = spells[focus_spell].base2[0]; return;
// For beneficial spells, if the triggered spell is also beneficial then proc it on the target
// if the triggered spell is detrimental, then it will trigger on the caster(ie cursed items)
if(IsBeneficialSpell(spell_id))
{
if(IsBeneficialSpell(focus_trigger))
SpellFinished(focus_trigger, target);
else uint16 focus_trigger = GetSympatheticSpellProcID(focus_spell);
SpellFinished(focus_trigger, this, 10, 0, -1, spells[focus_trigger].ResistDiff);
}
// For detrimental spells, if the triggered spell is beneficial, then it will land on the caster
// if the triggered spell is also detrimental, then it will land on the target
else
{
if(IsBeneficialSpell(focus_trigger))
SpellFinished(focus_trigger, this);
else if(!IsValidSpell(focus_trigger))
SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff); return;
}
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell); // For beneficial spells, if the triggered spell is also beneficial then proc it on the target
} // if the triggered spell is detrimental, then it will trigger on the caster(ie cursed items)
if(IsBeneficialSpell(spell_id))
{
if(IsBeneficialSpell(focus_trigger))
SpellFinished(focus_trigger, target);
else
SpellFinished(focus_trigger, this, 10, 0, -1, spells[focus_trigger].ResistDiff);
}
// For detrimental spells, if the triggered spell is beneficial, then it will land on the caster
// if the triggered spell is also detrimental, then it will land on the target
else
{
if(IsBeneficialSpell(focus_trigger))
SpellFinished(focus_trigger, this);
else
SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff);
}
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
} }
int32 Mob::GetItemStat(uint32 itemid, const char *identifier) int32 Mob::GetItemStat(uint32 itemid, const char *identifier)

View File

@ -1067,6 +1067,8 @@ protected:
void PrintRoute(); void PrintRoute();
virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0); virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0);
int16 GetSympatheticSpellProcRate(uint16 spell_id);
uint16 GetSympatheticSpellProcID(uint16 spell_id);
enum {MAX_PROCS = 4}; enum {MAX_PROCS = 4};
tProc PermaProcs[MAX_PROCS]; tProc PermaProcs[MAX_PROCS];

View File

@ -1839,7 +1839,7 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns)
if(swarmOwner->IsClient()) if(swarmOwner->IsClient())
{ {
SetPetOwnerClient(true); //Simple flag to determine if pet belongs to a client SetPetOwnerClient(true); //Simple flag to determine if pet belongs to a client
SetAllowBeneficial(1);//Allow temp pets to receive buffs and heals if owner is client. SetAllowBeneficial(true);//Allow temp pets to receive buffs and heals if owner is client.
//This will allow CLIENT swarm pets NOT to be targeted with F8. //This will allow CLIENT swarm pets NOT to be targeted with F8.
ns->spawn.targetable_with_hotkey = 0; ns->spawn.targetable_with_hotkey = 0;
no_target_hotkey = 1; no_target_hotkey = 1;
@ -2258,8 +2258,6 @@ bool NPC::CanTalk()
0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473}; 0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473};
int talk_check = TalkRace[GetRace() - 1];
if (TalkRace[GetRace() - 1] > 0) if (TalkRace[GetRace() - 1] > 0)
return true; return true;

View File

@ -1478,6 +1478,11 @@ int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 reques
{ {
for(uint32 i = 0; i < Head.PathNodeCount; ++i) for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{ {
if(PathNodes[i].id - new_id > 1) {
new_id = PathNodes[i].id - 1;
break;
}
if(PathNodes[i].id > new_id) if(PathNodes[i].id > new_id)
new_id = PathNodes[i].id; new_id = PathNodes[i].id;
} }

View File

@ -2530,6 +2530,32 @@ XS(XS_NPC_ClearLastName)
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
XS(XS_NPC_GetCombatState); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_GetCombatState)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: NPC::GetCombatState(THIS)");
{
NPC * THIS;
bool RETVAL;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetCombatEvent();
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
#endif #endif
@ -2643,6 +2669,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$");
newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$"); newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$");
newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$");
newXSproto(strcpy(buf, "GetCombatState"), XS_NPC_GetCombatState, file, "$");
XSRETURN_YES; XSRETURN_YES;
} }

View File

@ -2927,6 +2927,13 @@ void QuestManager::ClearNPCTypeCache(int npctype_id) {
} }
} }
void QuestManager::ReloadZoneStaticData()
{
if (zone) {
zone->ReloadStaticData();
}
}
Client *QuestManager::GetInitiator() const { Client *QuestManager::GetInitiator() const {
if(!quests_running_.empty()) { if(!quests_running_.empty()) {
running_quest e = quests_running_.top(); running_quest e = quests_running_.top();

View File

@ -249,6 +249,7 @@ public:
bool EnableRecipe(uint32 recipe_id); bool EnableRecipe(uint32 recipe_id);
bool DisableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id);
void ClearNPCTypeCache(int npctype_id); void ClearNPCTypeCache(int npctype_id);
void ReloadZoneStaticData();
Client *GetInitiator() const; Client *GetInitiator() const;
NPC *GetNPC() const; NPC *GetNPC() const;

View File

@ -5177,13 +5177,12 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
return(value*lvlModifier/100); return(value*lvlModifier/100);
} }
int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
if (IsBardSong(spell_id)) if (IsBardSong(spell_id))
return 0; return 0;
uint16 proc_spellid = 0; uint16 proc_spellid = 0;
uint8 MAX_SYMPATHETIC = 10;
float ProcChance = 0.0f; float ProcChance = 0.0f;
std::vector<int> SympatheticProcList; std::vector<int> SympatheticProcList;
@ -5195,7 +5194,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
for(int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++) for(int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++)
{ {
if (SympatheticProcList.size() > MAX_SYMPATHETIC) if (SympatheticProcList.size() > MAX_SYMPATHETIC_PROCS)
continue; continue;
TempItem = nullptr; TempItem = nullptr;
@ -5215,7 +5214,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
for (int y = AUG_BEGIN; y < EmuConstants::ITEM_COMMON_SIZE; ++y) for (int y = AUG_BEGIN; y < EmuConstants::ITEM_COMMON_SIZE; ++y)
{ {
if (SympatheticProcList.size() > MAX_SYMPATHETIC) if (SympatheticProcList.size() > MAX_SYMPATHETIC_PROCS)
continue; continue;
ItemInst *aug = nullptr; ItemInst *aug = nullptr;
@ -5243,18 +5242,18 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
int buff_max = GetMaxTotalSlots(); int buff_max = GetMaxTotalSlots();
for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { for (buff_slot = 0; buff_slot < buff_max; buff_slot++) {
if (SympatheticProcList.size() > MAX_SYMPATHETIC) if (SympatheticProcList.size() > MAX_SYMPATHETIC_PROCS)
continue; continue;
focusspellid = buffs[buff_slot].spellid; focusspellid = buffs[buff_slot].spellid;
if (IsValidSpell(focusspellid)) if (!IsValidSpell(focusspellid))
continue; continue;
proc_spellid = CalcFocusEffect(type, focusspellid, spell_id); proc_spellid = CalcFocusEffect(type, focusspellid, spell_id);
if (IsValidSpell(proc_spellid)){ if (IsValidSpell(proc_spellid)){
ProcChance = GetSympatheticProcChances(spell_id, spells[focusspellid].base[0]); ProcChance = GetSympatheticProcChances(spell_id, GetSympatheticSpellProcRate(spell_id));
if(zone->random.Roll(ProcChance)) if(zone->random.Roll(ProcChance))
SympatheticProcList.push_back(proc_spellid); SympatheticProcList.push_back(proc_spellid);
} }
@ -5275,7 +5274,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
if (aa_AA < 1 || aa_value < 1) if (aa_AA < 1 || aa_value < 1)
continue; continue;
if (SympatheticProcList.size() > MAX_SYMPATHETIC) if (SympatheticProcList.size() > MAX_SYMPATHETIC_PROCS)
continue; continue;
proc_spellid = CalcAAFocus(type, aa_AA, spell_id); proc_spellid = CalcAAFocus(type, aa_AA, spell_id);
@ -5930,6 +5929,26 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I
return ProcChance; return ProcChance;
} }
int16 Mob::GetSympatheticSpellProcRate(uint16 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; i++){
if (spells[spell_id].effectid[i] == SE_SympatheticProc)
return spells[spell_id].base[i];
}
return 0;
}
uint16 Mob::GetSympatheticSpellProcID(uint16 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; i++){
if (spells[spell_id].effectid[i] == SE_SympatheticProc)
return spells[spell_id].base2[i];
}
return 0;
}
bool Mob::DoHPToManaCovert(uint16 mana_cost) bool Mob::DoHPToManaCovert(uint16 mana_cost)
{ {
if (spellbonuses.HPToManaConvert){ if (spellbonuses.HPToManaConvert){

View File

@ -2151,7 +2151,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
// it can affect up to 7 people if the targeted group is not our own // it can affect up to 7 people if the targeted group is not our own
// Allow pets who cast group spells to affect the group. // Allow pets who cast group spells to affect the group.
if (spell_target->IsPetOwnerClient()){ if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()){
Mob* owner = spell_target->GetOwner(); Mob* owner = spell_target->GetOwner();
if (owner) if (owner)

View File

@ -1583,6 +1583,8 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic
return; return;
} }
tbs->Price = BuyItem->GetPrice();
Log.Out(Logs::Detail, Logs::Trading, "Buyitem: Name: %s, IsStackable: %i, Requested Quantity: %i, Charges on Item %i", Log.Out(Logs::Detail, Logs::Trading, "Buyitem: Name: %s, IsStackable: %i, Requested Quantity: %i, Charges on Item %i",
BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges()); BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges());
// If the item is not stackable, then we can only be buying one of them. // If the item is not stackable, then we can only be buying one of them.
@ -1650,7 +1652,12 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic
outtbs->Price = TotalCost; outtbs->Price = TotalCost;
} }
this->TakeMoneyFromPP(TotalCost); if(!TakeMoneyFromPP(TotalCost)) {
database.SetHackerFlag(account_name, name, "Attempted to buy something in bazaar but did not have enough money.");
TradeRequestFailed(app);
safe_delete(outapp);
return;
}
Log.Out(Logs::Detail, Logs::Trading, "Customer Paid: %d in Copper", TotalCost); Log.Out(Logs::Detail, Logs::Trading, "Customer Paid: %d in Copper", TotalCost);