|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Quests::Completed This is where Completed quests are. |
|
|
|
07-26-2013, 10:26 AM
|
Sarnak
|
|
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
|
|
Dynamic instancing by level range
This community has been overwhelmingly helpful in assisting me with getting my server up and running. When I first started I had a plethora of questions (will have more I'm sure!) But you guys have been patient and extremely positive.
Now it's time for me to give back to the community. A friend and I have been trying to find a way to create instances that would change level ranges based on the quest requestor's level. Yes this is similar to what LDON's do. With this PL file, however, you canmodify it to make instances of any zone.
In my example below, we've used Crushbone. With this, all you would have to do is populate each version of the instance with the appropriate level of mobs. This, of course, can be any range you want. You determine.
I apologize if this is not as neat as some PL files. We've just recently begun to learn how PL files work and most of the ones we've made have been simply trial nad error to see what works. So it may not be as neat as some, but it works perfectly.
Code:
sub EVENT_SAY {
my $EnterInst = quest::saylink("Enter the Instance", 2);
my $Destroy = quest::saylink("destroy", 2);
my $worse = quest::saylink("worse", 2);
my $evil = quest::saylink("evil", 2);
my $InstId = quest::GetInstanceID("crushbone", $version);
if($text=~/hail/i){
quest::say("Thank Tunare you're here, $name! Something dreadful is taking place inside Castle Crushbone. Yes, yes, I realize that the whole place is dreadful to begin with. But it's much [$worse] now.");
}
if($text=~/worse/i){
quest::say("Some new evil has moved in and set up shop, destroying everything inside. Where the Crushbone Orcs were destructive and chaotic, this new [$evil] is much more of a threat to all of Tunare's creatures.");
}
if($text=~/evil/i){
quest::say("if you are willing to assist us, I will need to ensure your safe passage. The new evil inside Crushbone has not yet reached our dimension. However, he has infilftrated alternate diminsions. If we can defeat him in these other planes of existence, perhaps we can prevent him from finding our current diminsion. Just say when you are ready and I can open a portal enabling you to [$EnterInst]. Also, if you are done fighting, or wish to start over, I can [$Destroy] any old instances that may be lingering. ");
}
if ($text =~/Enter the Instance/i) {
my $j;
for($j=1; $j<=6; $j++) {
my $version = $j;
my $InstId = quest::GetInstanceID("crushbone", $version);
if ($InstId > 0) {
quest::MovePCInstance(58, $InstId, 166, -586, 3.13);
}
elsif ((quest::GetInstanceID("crushbone", $ulevel + 1)) > 0) {
quest::MovePCInstance(58, quest::GetInstanceID("crushbone", $ulevel + 1), 166, -586, 3.13);
}
elsif ((quest::GetInstanceID("crushbone", $ulevel - 1)) > 0) {
quest::MovePCInstance(58, quest::GetInstanceID("crushbone", $ulevel - 1), 166, -586, 3.13);
}
elsif (($ulevel >= (($version * 10) - 9)) && ($ulevel <= ($version * 10))) {
my $i_id = quest::CreateInstance("crushbone", $version, 21600);
quest::AssignGroupToInstance($i_id);
quest::AssignToInstance($i_id);
quest::MovePCInstance(58, $i_id, 166, -586, 3.13);
}
else {
}
}
}
if($text=~/destroy/i){
my $i;
for($i=0; $i<=6; $i++) {
quest::DestroyInstance(quest::GetInstanceID("crushbone", $i));
}
}
}
We also had to find a way to have the player zone back out of the instance. For this, there may be a better way, but we created an NPC that acts like a zone line. Below is his PL file. Basically we put an NPC where we want the zone line, changed him to an invisible man, made his name ___ so it does not show up, and simply made him untargetable and made it to where players can't aggro or damage him. Again, there may be an easier way, but this works perfectly for us.
Code:
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
quest::set_proximity($x - 70, $x + 70, $y - 70, $y + 70);
}
sub EVENT_ENTER {
$client->Message(315, "Greater Faydark");
quest::movepc(54,-56.33,2552.77,18.97,125);
}
sub EVENT_EXIT {
quest::set_proximity($x - 70, $x + 70, $y - 70, $y + 70);
}
If anyone has any questions, I'd be more than happy to answer them. Again, thank you guys kindly for helping me get started. I hope this can help even a few people.
|
|
|
|
07-26-2013, 12:34 PM
|
Hill Giant
|
|
Join Date: Jul 2012
Location: Oklahoma
Posts: 222
|
|
Nice.... I can see this getting some good use.
|
07-26-2013, 04:51 PM
|
|
Demi-God
|
|
Join Date: May 2007
Location: b
Posts: 1,447
|
|
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.
|
07-27-2013, 12:29 AM
|
Sarnak
|
|
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
|
|
Quote:
Originally Posted by Secrets
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!
|
|
|
|
07-27-2013, 01:18 AM
|
|
Administrator
|
|
Join Date: Feb 2009
Location: MN
Posts: 2,072
|
|
Quote:
Originally Posted by jshows1
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;
}
|
|
|
|
07-27-2013, 11:14 AM
|
Sarnak
|
|
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
|
|
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?
|
|
|
|
07-27-2013, 01:39 PM
|
|
Administrator
|
|
Join Date: Feb 2009
Location: MN
Posts: 2,072
|
|
Quote:
Originally Posted by jshows1
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.
|
|
|
|
07-27-2013, 02:13 PM
|
Sarnak
|
|
Join Date: Jun 2009
Location: Baton Rouge,LA
Posts: 79
|
|
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.
|
03-06-2023, 11:31 AM
|
Hill Giant
|
|
Join Date: Feb 2008
Posts: 189
|
|
Just a quick note that dimension is spelled correctly the first time, but the two times after that, it is spelled incorrectly.
##EDIT -- I just noticed the date on this post. Sorry for the necro.
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 07:53 PM.
|
|
|
|
|
|
|
|
|
|
|
|
|