Go Back   EQEmulator Home > EQEmulator Forums > Quests > Quests::Completed

Quests::Completed This is where Completed quests are.

Reply
 
Thread Tools Display Modes
  #1  
Old 07-26-2013, 04:51 PM
Secrets's Avatar
Secrets
Demi-God
 
Join Date: May 2007
Location: b
Posts: 1,450
Default

A better idea would to be to take the instance ID, have a central zone controller, and scale NPCs in the zone based on level range in a perl script upon NPC spawn with signals and/or entity variables.

You can accomplish this with the modifynpcstat perl command and Akkadius' zone copying tool.
Reply With Quote
  #2  
Old 07-27-2013, 12:29 AM
jshows1
Sarnak
 
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
Default

Quote:
Originally Posted by Secrets View Post
A better idea would to be to take the instance ID, have a central zone controller, and scale NPCs in the zone based on level range in a perl script upon NPC spawn with signals and/or entity variables.

You can accomplish this with the modifynpcstat perl command and Akkadius' zone copying tool.
This sounds like great information. Thanks a bunch, Secrets. I'll definitely look into this method and learn how to implement what you're talking about. Certainly sounds more efficient than what we did. Again, we're just learning this Pearl stuff, so we're still a little clunky lol.

And thanks a bunch for the encouragement, Rhyotte!
Reply With Quote
  #3  
Old 07-27-2013, 01:18 AM
Akkadius's Avatar
Akkadius
Administrator
 
Join Date: Feb 2009
Location: MN
Posts: 2,071
Default

Quote:
Originally Posted by jshows1 View Post
This sounds like great information. Thanks a bunch, Secrets. I'll definitely look into this method and learn how to implement what you're talking about. Certainly sounds more efficient than what we did. Again, we're just learning this Pearl stuff, so we're still a little clunky lol.

And thanks a bunch for the encouragement, Rhyotte!
Secrets pretty much ripped this idea from what I had implemented and talked about. I've had it on my server for over 8 months, however I haven't had a real content push in a long time YET.

You have a NPC that iterates through the entity list and modify's their stats on the fly based on criteria you specify.

In my case I do really crafty and flexible loading from a database, and this NPC exists in every zone and the NPC's are scaled based on two keys:
  • Level
  • Type (Trash, Named, Raid)

Here is my live and working zone controller if you want to try and spin an idea of how I do this.

Code:
### Akkadius
### Zonecontroller
### This Entity sits in every zone ready to accept signals to handle various requests
### First use of this will be to handle automatic scaling of the zone if desired

### Insert Zone Controller into database
### SQL: (Assumes NPC ID 50)
### INSERT INTO `npc_types` (`id`, `name`, `lastname`, `level`, `race`, `class`, `bodytype`, `hp`, `mana`, `gender`, `texture`, `helmtexture`, `size`, `hp_regen_rate`, `mana_regen_rate`, `loottable_id`, `merchant_id`, `alt_currency_id`, `npc_spells_id`, `npc_faction_id`, `adventure_template_id`, `trap_template`, `mindmg`, `maxdmg`, `attack_count`, `npcspecialattks`, `aggroradius`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`, `luclin_beardcolor`, `luclin_beard`, `drakkin_heritage`, `drakkin_tattoo`, `drakkin_details`, `armortint_id`, `armortint_red`, `armortint_green`, `armortint_blue`, `d_meele_texture1`, `d_meele_texture2`, `prim_melee_type`, `sec_melee_type`, `runspeed`, `MR`, `CR`, `DR`, `FR`, `PR`, `Corrup`, `see_invis`, `see_invis_undead`, `qglobal`, `AC`, `npc_aggro`, `spawn_limit`, `attack_speed`, `findable`, `STR`, `STA`, `DEX`, `AGI`, `_INT`, `WIS`, `CHA`, `see_hide`, `see_improved_hide`, `trackable`, `isbot`, `exclude`, `ATK`, `Accuracy`, `slow_mitigation`, `version`, `maxlevel`, `scalerate`, `private_corpse`, `unique_spawn_by_name`, `underwater`, `isquest`, `emoteid`) VALUES (50, 'zonecontroller', NULL, 1, 240, 1, 11, 31, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 'ZiGH', 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 1.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 75, 75, 75, 80, 75, 75, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0);

sub EVENT_SPAWN{
	$npc->TempName("");
	#$Debug = " ";
	quest::gmsay("[Zonecontroller] [ALIVE] Zone: $zonesn Instance Version: $instanceversion InstID $instanceversion", 15);
	
	#LoadPetTable(); ### Load Pet Table into Memory for referencing
	#LoadPetScaleData(); ### Load Pet Scaling Table into Memory
	
	LoadStaticNPCScaling($zonesn); ### Load NPC Static Scaling
	LoadStaticZoneScaling(); ### Load Zone Static Scaling
	
	quest::settimer("scaleall", 3); ### Run Scaling routine
	$ScalingLoaded = "";
	
	# %DreamZones = plugin::LoadDreamZoneList();
	# $n = 1;
	# while($DreamZones{$n}[0]){
	# 	if($instanceversion == 1 && $DreamZones{$n}[0] eq $zonesn){ 
	# 		if(!$entity_list->GetNPCByNPCTypeID(1006398)){
	# 			$query = "SELECT zone, x, y, z, id FROM `zone_points` WHERE `zone` = '". $zonesn ." AND `version` = " . $instanceversion . "";
	# 			$connect = plugin::LoadMysql();
	# 			quest::shout("loading zone points for dream zones");
	# 			$query_handle = $connect->prepare($query); $query_handle->execute();
	# 			while(@row = $query_handle->fetchrow_array()){
	# 				quest::spawn2(1006398, 0, 0, int($row[1]), int($row[2]), int($row[3]), 0);
	# 			}
	# 		}
	# 	}
	# 	$n++;
	# }
}

sub EVENT_TIMER{
	if($timer eq "scaleall"){ ScaleProcedure(); quest::stoptimer("scaleall"); quest::gmsay("Scaling routine complete", 15);  }
	if($timer eq "levelset"){
		@ent = $entity_list->GetNPCList();
		$Level = $npc->GetEntityVariable("queuesetlevel2");
		$Variance = $npc->GetEntityVariable("queuesetlevel3");
		foreach $NPC (@ent){
			if($NPC->GetNPCTypeID() > 1000){
				if($Variance > 0){ } else{ $Variance = 0; }
				$NPC->SetLevel($Level + (RandomRange(0 - $Variance, $Variance)));
			}
		}
		$npc->SetEntityVariable("queuesetlevel", 0);
		$npc->SetEntityVariable("queuesetlevel2", 0);
		$npc->SetEntityVariable("queuesetlevel3", 0);
		quest::gmsay("Level Scaling Done...", 15);
		quest::stoptimer("levelset");
	}
}

sub EVENT_SIGNAL{
	if($signal == 10){ ScaleProcedure(); }
	if($signal == 11){ $npc->SetEntityVariable("queuesetlevel", 1); return; }
	if($npc->GetEntityVariable("queuesetlevel") > 0 && $npc->GetEntityVariable("queuesetlevel2") == 0){ $npc->SetEntityVariable("queuesetlevel2", $signal); quest::settimer("levelset", 1); }
	if($npc->GetEntityVariable("queuesetlevel2") > 0){ $npc->SetEntityVariable("queuesetlevel3", $signal); }
	### Getting Signal from Spawned NPC ###
	if($signal == 21){ $npc->SetEntityVariable("queuesetnpc", 1);  return; }
	if($npc->GetEntityVariable("queuesetnpc") eq "1" && $ScalingLoaded == 3){ $npc->SetEntityVariable("queuesetnpc", 0); ScaleProcedure($signal); }
	if($signal == 23){ $Debug = 1; }
}

sub ScaleProcedure{
	### Read from DB
	if(!$SD[1][0][0]){ LoadScaling(); } ### Scaling Vars Empty, reload them from DB
	$NPC = 0; $pop = "";
	
	if($_[0]){ 
		@ent = $entity_list->GetNPCByNPCTypeID($_[0]); 
		$pop = " POP"; 
		# quest::gmsay("Scaling NPC ID " . $_[0], 15);
	}
	else{ @ent = $entity_list->GetNPCList(); $ScalingLoaded = 2; }
	foreach $NPC (@ent){
		### Pet Scaling Handler
		my $IsPet = $PetD[$NPC->GetNPCTypeID()][0]; if(!$IsPet){ $IsPet = 0; }
		if(($NPC->GetOwnerID() != 0 || $IsPet > 0) && ($entity_list->GetClientByName("AkkaDark") || $entity_list->GetClientByName("Akkamage") || $entity_list->GetClientByName("Akkasham") || $entity_list->GetClientByName("Akkabeast"))){ 
			if($PetD[$NPC->GetNPCTypeID()][0] > 0 && $PetD[$NPC->GetNPCTypeID()][3] == 1){
				###Swarm Pet
				#quest::shout("A Swarm Pet has been casted... Processing code...");
				my $PetLevel = $NPC->GetLevel();
				$NPC->ModifyNPCStat("max_hp", $PetSD[$PetLevel][1]); 
				$NPC->SetHP($NPC->GetMaxHP());
				$NPC->ModifyNPCStat("max_mana", $PetSD[$PetLevel][2]);
				$NPC->ModifyNPCStat("min_hit", $PetSD[$PetLevel][3]);
				$NPC->ModifyNPCStat("max_hit", $PetSD[$PetLevel][4]);
			}else{
				my $PetOwnerID = $entity_list->GetClientByID($NPC->GetOwnerID());
				if($PetOwnerID){ $PetOwner = $PetOwnerID->GetCleanName(); }
				my $PetOwnerEnt = $entity_list->GetClientByID($NPC->GetOwnerID());
				my $PetName = $NPC->GetCleanName();
				my $PetLevel = $PetOwnerEnt->GetEntityVariable("originlevel");
				#my $PetEntID = $PetOwnerEnt->SetEntityVariable($NPC);
				$NPC->SetLevel($PetOwnerEnt->GetLevel());
				my $PetPower = $PetOwnerEnt->GetEntityVariable("petpower") / 100 + 1; if($PetPower > 0){}else{ $PetPower = 1; }
				if($Debug){ quest::gmsay("[Zonecontroller]: Found pet to scale - Owner " . $PetOwner . " Pet: " . $PetName, 15); }
				$NPC->ModifyNPCStat("max_hp", $PetSD[$PetLevel][1] + ($PetOwnerEnt->GetINT() * 10) * $PetPower);
				if($Debug){ quest::gmsay("[Zonecontroller]: Pet: " . $PetName . " max_hp: " . $PetSD[$PetLevel][1] . " INT mod " . ($PetOwnerEnt->GetINT() * 10) . " Pet Power: " . $PetPower ." Final " . ($PetSD[$PetLevel][1] + ($PetOwnerEnt->GetINT() * 10) * $PetPower), 15); }
				$NPC->SetHP($NPC->GetMaxHP());
				$NPC->ModifyNPCStat("max_mana", $PetSD[$PetLevel][2]);
				$NPC->ModifyNPCStat("min_hit", $PetSD[$PetLevel][3]);
				$NPC->ModifyNPCStat("max_hit", $PetSD[$PetLevel][4] + ($PetOwnerEnt->GetCHA() * 5) * ($PetPower / 2));
				if($Debug){ quest::gmsay("[Zonecontroller]: Pet: " . $PetName . " max_hit: " . $PetSD[$PetLevel][4] . " CHA mod " . ($PetOwnerEnt->GetCHA() * 5) . " Pet Power: " . ($PetPower / 2) ." Final " . ($PetSD[$PetLevel][4] + ($PetOwnerEnt->GetCHA() * 5) * ($PetPower / 2)), 15); }
				$NPC->ModifyNPCStat("attack_speed", $PetSD[$PetLevel][5]);
				$NPC->ModifyNPCStat("ac",  $PetSD[$PetLevel][6]);
				$NPC->ModifyNPCStat("hp_regen", $PetSD[$PetLevel][7]);
				$NPC->ModifyNPCStat("spellscale", $PetSD[$PetLevel][8]);
				$NPC->ModifyNPCStat("healscale", $PetSD[$PetLevel][9]);
				$NPC->ModifyNPCStat("special_attacks", $PetSD[$PetLevel][10]);
				$NPC->ModifyNPCStat("accuracy", 50);
				#quest::shout($PetPower);
			}
		}
	
		### Hide Invis NPC Names 
		if(($NPC->GetRace() == 127 || $NPC->GetRace() == 240) && $NPC->GetLevel() < 100 && $NPC->GetNPCTypeID() > 1000){ if($NPC->GetCleanName()=~/shadowed/i){} else{ $NPC->TempName(""); $NPC->ModifyNPCStat("special_attacks", "AZ"); } }
		
		if($NPC->GetEntityVariable("Scaled") != 1){
			#Static NPC Scaling
			$n_name = $NPC->GetCleanName(); $n_name =~ tr/ /_/;
			if($N_SD{$n_name}[0]){  ScaleNPCStatic($NPC, $n_name);  }
		}
		### SCALING
		#$NPC->Say("Scaling...");
		if($NPC->GetPetSpellID() == 0 && $NPC->GetRace() != 127 && $NPC->GetRace() != 240 && $NPC->GetClass() < 20 && $NPC->GetNPCTypeID() > 1000 && $NPC->GetEntityVariable("Scaled") != 1){
			
			$NTYPE = 0; $NN = "NPC";
			if(substr($NPC->GetName(), 0, 1) eq "#" && substr($NPC->GetName(), 1, 2) ne "#"){ $NTYPE = 1; $NN = "Named"; } 
			if(substr($NPC->GetName(), 0, 2) eq "##" && substr($NPC->GetName(), 2, 3) ne "#"){ $NTYPE = 2; $NN = "Raid"; }
			
			$NPC->ModifyNPCStat("max_hp", $SD[$NPC->GetLevel()][$NTYPE][2] * $SZD{$zonesn}[$instanceversion][$NTYPE][2]);
			$NPC->ModifyNPCStat("max_mana", $SD[$NPC->GetLevel()][$NTYPE][3]  * $SZD{$zonesn}[$instanceversion][$NTYPE][3]);
			$NPC->ModifyNPCStat("min_hit", $SD[$NPC->GetLevel()][$NTYPE][4] *  $SZD{$zonesn}[$instanceversion][$NTYPE][4]);
			$NPC->ModifyNPCStat("max_hit", $SD[$NPC->GetLevel()][$NTYPE][5] *  $SZD{$zonesn}[$instanceversion][$NTYPE][5]);
			$NPC->ModifyNPCStat("attack_speed", $SD[$NPC->GetLevel()][$NTYPE][6] *  $SZD{$zonesn}[$instanceversion][$NTYPE][6]);
			$NPC->ModifyNPCStat("ac", $SD[$NPC->GetLevel()][$NTYPE][7] *  $SZD{$zonesn}[$instanceversion][$NTYPE][7]);
			$NPC->ModifyNPCStat("str", $SD[$NPC->GetLevel()][$NTYPE][8]);
			$NPC->ModifyNPCStat("sta", $SD[$NPC->GetLevel()][$NTYPE][9]);
			$NPC->ModifyNPCStat("dex", $SD[$NPC->GetLevel()][$NTYPE][10]);
			$NPC->ModifyNPCStat("agi", $SD[$NPC->GetLevel()][$NTYPE][11]);
			$NPC->ModifyNPCStat("int", $SD[$NPC->GetLevel()][$NTYPE][12]);
			$NPC->ModifyNPCStat("wis", $SD[$NPC->GetLevel()][$NTYPE][13]);
			$NPC->ModifyNPCStat("cha", $SD[$NPC->GetLevel()][$NTYPE][14]);
			$NPC->ModifyNPCStat("mr", $SD[$NPC->GetLevel()][$NTYPE][15]);
			$NPC->ModifyNPCStat("cr", $SD[$NPC->GetLevel()][$NTYPE][16]);
			$NPC->ModifyNPCStat("dr", $SD[$NPC->GetLevel()][$NTYPE][17]);
			$NPC->ModifyNPCStat("fr", $SD[$NPC->GetLevel()][$NTYPE][18]);
			$NPC->ModifyNPCStat("pr", $SD[$NPC->GetLevel()][$NTYPE][19]);
			
			
			### Is there a static zone entry?
			if($SZD{$zonesn}[$instanceversion][$NTYPE][11] != 1){
				$NPC->ModifyNPCStat("special_attacks", $SZD{$zonesn}[$instanceversion][$NTYPE][11]);
			}else{
				$NPC->ModifyNPCStat("special_attacks", $SD[$NPC->GetLevel()][$NTYPE][21]);
			}
			$NPC->ModifyNPCStat("hp_regen", $SD[$NPC->GetLevel()][$NTYPE][20]  *  $SZD{$zonesn}[$instanceversion][$NTYPE][10]);
			$NPC->SetEntityVariable("hpregen", $SD[$NPC->GetLevel()][$NTYPE][20]  *  $SZD{$zonesn}[$instanceversion][$NTYPE][10]);
			$NPC->ModifyNPCStat("spellscale", $SD[$NPC->GetLevel()][$NTYPE][22]  *  $SZD{$zonesn}[$instanceversion][$NTYPE][9]);
			$NPC->ModifyNPCStat("healscale", $SD[$NPC->GetLevel()][$NTYPE][23]  *  $SZD{$zonesn}[$instanceversion][$NTYPE][8]);
			
			$LID = (200000 + ($NTYPE * 1000) + $NPC->GetLevel());
			if($NPC->GetLoottableID() != $LID){
				$NPC->ModifyNPCStat("loottable_id", (210000 + ($NTYPE * 1000) + $NPC->GetLevel())); $NPC->AddLootTable();
				$NPC->ModifyNPCStat("loottable_id", (200000 + ($NTYPE * 1000) + $NPC->GetLevel())); $NPC->AddLootTable();  
			}
			$NPC->SetHP($NPC->GetMaxHP());
			$NPC->SetEntityVariable("Scaled", 1); if($Debug){ $NPC->SetEntityVariable("ScaledType", 1); }
			
			if($Debug){
				quest::gmsay("Scaling $pop $NN [" . $NPC->GetCleanName() . "] 
					HP:[". $SD[$NPC->GetLevel()][$NTYPE][2] * $SZD{$zonesn}[$instanceversion][$NTYPE][2] . "]
					hp_regen:[". ($SD[$NPC->GetLevel()][$NTYPE][20]  *  $SZD{$zonesn}[$instanceversion][$NTYPE][10]) . "]
					min_hit:[". ($SD[$NPC->GetLevel()][$NTYPE][4] *  $SZD{$zonesn}[$instanceversion][$NTYPE][4]) . "]
					max_hit:[".( $SD[$NPC->GetLevel()][$NTYPE][5] *  $SZD{$zonesn}[$instanceversion][$NTYPE][5]) . "]
					spec:[". $SD[$NPC->GetLevel()][$NTYPE][21] . "] 
					NTYPE:[". $NTYPE . "] 
					", 15);
			}
			
			if(!$SD[$NPC->GetLevel()][$NTYPE][0]){ quest::gmsay($NPC->GetCleanName() . ": ERROR! Missing Scaling Entry! For level " . $NPC->GetLevel() . " Type: $NTYPE", 13); }
		}
		# Level 255 NPC's are considered quest NPC's and shall not be attacked #
		if($NPC->GetLevel() == 255){ $NPC->ModifyNPCStat("special_attacks", "AHGZ"); } 
	}
	if($ScalingLoaded == 2){ $ScalingLoaded = 3; }
}

sub LoadScaling{
	$connect = plugin::LoadMysql();
	$query = "SELECT
		cust_npc_scaling.level,
		cust_npc_scaling.type,
		cust_npc_scaling.hp,
		cust_npc_scaling.mana,
		cust_npc_scaling.mindmg,
		cust_npc_scaling.maxdmg,
		cust_npc_scaling.attack_speed,
		cust_npc_scaling.AC,
		cust_npc_scaling.STR,
		cust_npc_scaling.STA,
		cust_npc_scaling.DEX,
		cust_npc_scaling.AGI,
		cust_npc_scaling._INT,
		cust_npc_scaling.WIS,
		cust_npc_scaling.CHA,
		cust_npc_scaling.MR,
		cust_npc_scaling.CR,
		cust_npc_scaling.DR,
		cust_npc_scaling.FR,
		cust_npc_scaling.PR,
		cust_npc_scaling.hp_regen,
		cust_npc_scaling.npcspecialattks,
		cust_npc_scaling.spellscale,
		cust_npc_scaling.healscale
		FROM
		cust_npc_scaling
		ORDER BY cust_npc_scaling.level, cust_npc_scaling.type";
	$query_handle = $connect->prepare($query);
	$query_handle->execute();
	$NTYPE = 0;
	while (@row = $query_handle->fetchrow_array()){ $SD[$row[0]][$row[1]] = [@row]; }
}

sub LoadStaticZoneScaling{
	### Load Static Zone Scaling Data ### 
	$connect = plugin::LoadMysql();
	### Default empty values to 1
	for($i = 2; $i < 17; $i++){ 
		for($t = 0; $t < 3; $t++){
			if(!$SZD{$zonesn}[$instanceversion][$t][$i] || $SZD{$zonesn}[$instanceversion][$t][$i] == 0){ 
				$SZD{$zonesn}[$instanceversion][$t][$i] = 1; 
			}
		}
	} 
	
	$query = "SELECT `zonesn`, `version`, `hp`, `mana`, `mindmg`, `maxdmg`, `attack_speed`, `ac`, `healscale`, `spellscale`, `hpregen`, `specialattacks`, `type` FROM `cust_npc_zonescale_static` WHERE `zonesn` = '" . $zonesn . "'"; $query_handle = $connect->prepare($query); $query_handle->execute(); my @SZD;
	while (@row = $query_handle->fetchrow_array()){ 
		$SZD{$row[0]}[$row[1]][$row[12]] = [@row];  
		if($SZD{$zonesn}[$instanceversion][0]){
			$Mod = $SZD{$zonesn}[$instanceversion][2]; $npc->SetEntityVariable("ScaleMod", $Mod);
			quest::gmsay("[Zonecontroller] (Static Data) [Type] : " . $row[12] . " Zone Data: HP=" . ($SZD{$zonesn}[$instanceversion][$row[12]][2] * 100) . "(%%) Mana=" . ($SZD{$zonesn}[$instanceversion][$row[12]][3] * 100) . "(%%) MINDMG=" . ($SZD{$zonesn}[$instanceversion][$row[12]][4] * 100) . "(%%) MAXDMG=" . ($SZD{$zonesn}[$instanceversion][$row[12]][5] * 100) . "(%%) ATK_SPD=" . ($SZD{$zonesn}[$instanceversion][$row[12]][6] * 100) . "(%%) AC=" . ($SZD{$zonesn}[$instanceversion][$row[12]][7] * 100) . "(%%) SPELLSCALE=" . ($SZD{$zonesn}[$instanceversion][$row[12]][8] * 100) . "(%%) HEALSCALE=" . ($SZD{$zonesn}[$instanceversion][$row[12]][9] * 100) . "(%%) HPREGEN=" . ($SZD{$zonesn}[$instanceversion][$row[12]][10] * 100) . "(%%)", 15);
			
		}
	}
}

sub LoadStaticNPCScaling{
	$connect = plugin::LoadMysql();
	$query = "SELECT
		cust_npc_scale_static.`name`,
		cust_npc_scale_static.zonesn,
		cust_npc_scale_static.hp,
		cust_npc_scale_static.mana,
		cust_npc_scale_static.mindmg,
		cust_npc_scale_static.maxdmg,
		cust_npc_scale_static.attack_speed,
		cust_npc_scale_static.AC,
		cust_npc_scale_static.STR,
		cust_npc_scale_static.STA,
		cust_npc_scale_static.DEX,
		cust_npc_scale_static.AGI,
		cust_npc_scale_static._INT,
		cust_npc_scale_static.WIS,
		cust_npc_scale_static.CHA,
		cust_npc_scale_static.MR,
		cust_npc_scale_static.CR,
		cust_npc_scale_static.DR,
		cust_npc_scale_static.FR,
		cust_npc_scale_static.PR,
		cust_npc_scale_static.hp_regen,
		cust_npc_scale_static.spellscale,
		cust_npc_scale_static.healscale,
		cust_npc_scale_static.npcspecialattks,
		cust_npc_scale_static.usediabloloot,
		cust_npc_scale_static.localloottable
		FROM
		cust_npc_scale_static
		WHERE cust_npc_scale_static.zonesn = '" . $_[0] . "' OR cust_npc_scale_static.zonesn = 'global'";
	$query_handle = $connect->prepare($query);
	$query_handle->execute();
	$NTYPE = 0;
	while (@row = $query_handle->fetchrow_array()){ $N_SD{$row[0]} = [@row]; quest::gmsay("[Zonecontroller]: Loading static NPC data for NPC: '" . $row[0] ."'", 15); }
}

sub LoadPetTable{
	$connect = plugin::LoadMysql();
	$query = "SELECT
		pets.npcID,
		pets.type,
		pets.petpower,
		pets.temp,
		pets.petcontrol,
		pets.petnaming,
		pets.monsterflag,
		pets.equipmentset
		FROM
		pets
		";
	$query_handle = $connect->prepare($query);
	$query_handle->execute();
	if($Debug){ quest::gmsay("[Zonecontroller]: Loading pet table...", 15); }
	while (@row = $query_handle->fetchrow_array()){ $PetD[$row[0]] = [@row]; if($Debug){ quest::gmsay("[Zonecontroller]: Loading Pet: '" . $row[0] . " - " . $row[1] . "'", 15); } }
	if($Debug){ quest::gmsay("[Zonecontroller]: Loading pet table complete", 15); }
}

sub LoadPetScaleData{
	$connect = plugin::LoadMysql();
	$query = "SELECT
		cust_pet_scaling_entries.level,
		cust_pet_scaling_entries.hp,
		cust_pet_scaling_entries.mana,
		cust_pet_scaling_entries.mindmg,
		cust_pet_scaling_entries.maxdmg,
		cust_pet_scaling_entries.attack_speed,
		cust_pet_scaling_entries.AC,
		cust_pet_scaling_entries.hp_regen,
		cust_pet_scaling_entries.spellscale,
		cust_pet_scaling_entries.healscale,
		cust_pet_scaling_entries.npcspecialattks
		FROM
		cust_pet_scaling_entries
		order by level
		";
	$query_handle = $connect->prepare($query);
	$query_handle->execute();
	while (@row = $query_handle->fetchrow_array()){ $PetSD[$row[0]] = [@row]; }
	if($Debug){ quest::gmsay("[Zonecontroller]: Loading pet scaling complete", 15); }
}

sub ScaleNPCStatic{
	my $SS = $_[0];
	quest::shout("Found data for NPC " . $SS->GetCleanName() . ' - ' . $_[1]);
	$NTYPE = 0; $NN = "NPC";
	if(substr($SS->GetName(), 0, 1) eq "#" && substr($SS->GetName(), 1, 2) ne "#"){ $NTYPE = 1; $NN = "Named"; }
	if(substr($SS->GetName(), 0, 2) eq "##" && substr($SS->GetName(), 2, 3) ne "#"){ $NTYPE = 2; $NN = "Raid"; }

	$SS->ModifyNPCStat("max_hp", $N_SD{$_[1]}[2]);
	$SS->ModifyNPCStat("max_mana", $N_SD{$_[1]}[3]);
	$SS->ModifyNPCStat("min_hit", $N_SD{$_[1]}[4]);
	$SS->ModifyNPCStat("max_hit", $N_SD{$_[1]}[5]);
	$SS->ModifyNPCStat("attack_speed", $N_SD{$_[1]}[6]);
	$SS->ModifyNPCStat("ac", $N_SD{$_[1]}[7]);
	$SS->ModifyNPCStat("str", $N_SD{$_[1]}[8]);
	$SS->ModifyNPCStat("sta", $N_SD{$_[1]}[9]);
	$SS->ModifyNPCStat("dex", $N_SD{$_[1]}[10]);
	$SS->ModifyNPCStat("agi", $N_SD{$_[1]}[11]);
	$SS->ModifyNPCStat("int", $N_SD{$_[1]}[12]);
	$SS->ModifyNPCStat("wis", $N_SD{$_[1]}[13]);
	$SS->ModifyNPCStat("cha", $N_SD{$_[1]}[14]);
	$SS->ModifyNPCStat("mr", $N_SD{$_[1]}[15]);
	$SS->ModifyNPCStat("cr", $N_SD{$_[1]}[16]);
	$SS->ModifyNPCStat("dr", $N_SD{$_[1]}[17]);
	$SS->ModifyNPCStat("fr", $N_SD{$_[1]}[18]);
	$SS->ModifyNPCStat("pr", $N_SD{$_[1]}[19]);
	$SS->ModifyNPCStat("hp_regen", $N_SD{$_[1]}[20]);
	$SS->ModifyNPCStat("spellscale", $N_SD{$_[1]}[21]);
	$SS->ModifyNPCStat("healscale", $N_SD{$_[1]}[22]);
	$SS->ModifyNPCStat("special_attacks", $N_SD{$_[1]}[23]);
	
	if($N_SD{$_[1]}[24] == 1){ ### Tells Static Entry to use Diablo Loot
		$LID = (200000 + ($NTYPE * 1000) + $SS->GetLevel());
		if($SS->GetLoottableID() != $LID){
			$SS->ModifyNPCStat("loottable_id", (210000 + ($NTYPE * 1000) + $SS->GetLevel())); $SS->AddLootTable();
			$SS->ModifyNPCStat("loottable_id", (200000 + ($NTYPE * 1000) + $SS->GetLevel())); $SS->AddLootTable();  
		}
	}
	
	if($N_SD{$_[1]}[25] > 0){ ### Add an additional Loot table
		$SS->ModifyNPCStat("loottable_id", $N_SD{$_[1]}[25]); $SS->AddLootTable();  
	}
	
	$SS->SetEntityVariable("Scaled", 1); if($Debug){ $SS->SetEntityVariable("ScaledType", 2); }
}

sub RandomRange {

	my $MinRandom = $_[0];
	my $MaxRandom = $_[1];

	my $RandomResult = int(rand(($MaxRandom + 1) - $MinRandom)) + $MinRandom;
	if ($RandomResult > $MaxRandom)
	{
		return $MaxRandom;
	}
	return $RandomResult;

}
Reply With Quote
  #4  
Old 07-27-2013, 11:14 AM
jshows1
Sarnak
 
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
Default

Wow Thanks Akkadius. This sounds like a powerful tool. Real quick, how do you actually implement this? Do I simply create a mob in the zone I want and then put his pl file in that zone folder to get it to work?
Reply With Quote
  #5  
Old 07-27-2013, 01:39 PM
Akkadius's Avatar
Akkadius
Administrator
 
Join Date: Feb 2009
Location: MN
Posts: 2,071
Smile

Quote:
Originally Posted by jshows1 View Post
Wow Thanks Akkadius. This sounds like a powerful tool. Real quick, how do you actually implement this? Do I simply create a mob in the zone I want and then put his pl file in that zone folder to get it to work?
Well it's not a simple implementation, but as you can see in my notes, it is a NPC id of 50, it can be whatever you want it to be.

So how I achieve this automagically, is I insert it into the enterzone of the global_player.pl, if the NPC isn't in the zone when a player zones in, pop him. When he pops, he will go through the scaling routine.

If he isn't already a 'spawn' of the zone, I add him to a spawn regardless of instance version.

Code:
sub EVENT_ENTERZONE {
	if(!$entity_list->GetNPCByNPCTypeID(50)){ 
		quest::spawn2(50, 0, 0, 0, 0, 0, 0); 
		$client->NPCSpawn($entity_list->GetNPCByNPCTypeID(50), "add", 1); 
	} ### Automatic Scaling
}
Another question you might have. Yes, that's fine and dandy, but what about when a NPC spawns and needs to be scaled again?

Well, that's the main reason for me implementing global_npc.pl amongst other reasons.

Here is what I do, I send a signal to the zone controller, first with a unique signal telling the controller that 'HEY! I need to be scaled BEOTCH!' so the zone controller goes, ok yes I got it.

Once again, first he sends signal 21, then he sends the NPC Type ID. So the controller performs an iteration and only scales NPC's with that type ID.

Code:
sub EVENT_SPAWN {
	if($npc->GetEntityVariable("Scaled") != 1){ ### If not flagged as scaled, then scale the NPC
		quest::signalwith(50, 21, 0);
		quest::signalwith(50, $npc->GetNPCTypeID(), 0);
	}
}
The NPC recieves signal 21, sets a entity variable essentially 'flagging' itself for a split second like a catcher in a baseball game that he knows he is about to catch a NPCTypeID as a signal. As soon as he catches the NPCTypeID he releases the entity variable and gets ready for any other scaling requests.

So what did I achieve in the end with all of this?

TONS and TONS of time SAVED by having to scale my zones, I create generic 'ranges' that NPC's SHOULD be at based on their level AND type (Trash, named, raid). If I wanted to up the difficulty I had it set per zone with multipliers as you might be able to rip apart in my code.

All of the table pulls are custom, all of the framework needed to build a system like this is already there after implementing all of the source backend things. Through this thread I am revealing this for the first time.

Once I get my content release done, I will probably end up releasing a good chunk of my work including this one, but I've been so busy in the past years that is all variable.

If you have any questions regarding this type of implementation let me know.
Reply With Quote
  #6  
Old 07-27-2013, 02:13 PM
jshows1
Sarnak
 
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
Default

Akkadius, I for one am very grateful for you sharing this information. I will certainly be looking to use this. I'm sure I speak for the rest of the community when I offer my thanks. I'm sure this will make a lot of people happy to use this code and am looking forward to seeing what you end up implementing in the future.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 06:28 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3