projecteq-projecteqquests/plugins/formation_tools.pl
2015-03-14 16:18:39 -04:00

1054 lines
No EOL
31 KiB
Perl

#Usage: plugin::FollowFormLeader([LeaderMob, OnGrid = 0, MoveToX = 0, MoveToY = 0, NoSpeedBuffs = false]);
# This script will move all of 1 NPC Type ID to the relative Guard position that they were the first time this plugin was run
# LeaderMob is an optional field where a specific leader NPC can be specified (default is the NPC running the script)
# OnGrid is am optional field where 1 will use NPC waypoints if they are on a grid, and 0 (default) will use guard or set XYZ
# MoveToX/MoveToY/MoveToZ are optional fields for setting a destination X Y and Z instead of using guard points or way points to determine the destination.
# At this time, the MoveToZ field is basically ignored, as the ground Z is calculated based on the leader NPC's Z.
# NoSpeedBuffs is an optional field for all speedbuffs to be removed during pathing if enabled
sub FollowFormLeader {
my $npc = plugin::val('$npc');
my $entity_list = plugin::val('$entity_list');
my $Mob = $_[0];
my $OnGrid = $_[1];
my $MoveToX = $_[2];
my $MoveToY = $_[3];
my $NoSpeedBuffs = $_[4];
if (!$Mob) { $Mob = $npc; }
if (!$MoveToX && !$MoveToY && !$MoveToZ)
{
if (!$OnGrid)
{
$MoveToX = $Mob->GetGuardPointX();
$MoveToY = $Mob->GetGuardPointY();
}
else
{
$MoveToX = $Mob->GetWaypointX();
$MoveToY = $Mob->GetWaypointY();
}
}
my $NewHeading = $Mob->CalculateHeadingToTarget($MoveToX, $MoveToY);
if ($NoSpeedBuffs)
{
$Mob->BuffFadeByEffect(3); # Prevent Speed buffs from messing up the group of NPCs
}
my $LeaderID = $Mob->GetID();
my @npclist = $entity_list->GetNPCList();
foreach $ent (@npclist)
{
if($ent->EntityVariableExists(52) && $ent->GetEntityVariable(52) == $LeaderID)
{
#plugin::Debug("Got NPC");
if ($NoSpeedBuffs)
{
$ent->BuffFadeByEffect(3); # Prevent Speed buffs from messing up the group of NPCs
}
# Set the Initial Relative Position and Heading variables
if(!$ent->EntityVariableExists(50))
{
# If this doesn't exist, then neither of them do
my $SquadX = $ent->GetX();
my $SquadY = $ent->GetY();
my $SquadHeading = $Mob->CalculateHeadingToTarget($SquadX, $SquadY);
my $CurHeading = $Mob->GetHeading();
my $CurDist = plugin::DistNoZ($Mob, $ent);
my $HeadingDiff = 0;
if ($CurHeading > $SquadHeading)
{
$HeadingDiff = $CurHeading - $SquadHeading;
}
else
{
$HeadingDiff = $CurHeading + 256 - $SquadHeading;
}
$ent->SetEntityVariable(50, $CurDist); # Original Distance from Leader
$ent->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
}
my $SquadDist = $ent->GetEntityVariable(50);
my $HDiff = $ent->GetEntityVariable(51);
#plugin::Debug("Got Variables");
my $NewSquadHeading = 0;
if ($NewHeading - $HDiff > 0)
{
$NewSquadHeading = $NewHeading - $HDiff;
}
else
{
$NewSquadHeading = 256 + $NewHeading - $HDiff;
}
#plugin::Debug("Before Calc");
my @DestArray = plugin::CalcDestFromHeadingXY($NewSquadHeading, $SquadDist, $MoveToX, $MoveToY, 10, $Mob);
my $NewX = $DestArray[0];
my $NewY = $DestArray[1];
my $NewZ = $DestArray[2];
#plugin::Debug("After Calc");
$ent->MoveTo($NewX, $NewY, $NewZ, $NewHeading, 1);
#plugin::Debug("Moving NPC");
}
}
}
#Usage: plugin::MoveInFormation(NPCID, [$npc = 0, OnGrid = 0, MoveToX = 0, MoveToY = 0, NoSpeedBuffs = false]);
# This script will move all of 1 NPC Type ID to the relative Guard position that they were the first time this plugin was run
# NPCID is the NPC ID of the NPCs you want to move in the formation (moves all of this NPC ID in the whole zone)
# $npc is an optional field where a specific leader NPC can be specified (default is the NPC running the script)
# OnGrid is am optional field where 1 will use NPC waypoints if they are on a grid, and 0 (default) will use guard or set XYZ
# MoveToX/MoveToY/MoveToZ are optional fields for setting a destination X Y and Z instead of using guard points or way points to determine the destination.
# At this time, the MoveToZ field is basically ignored, as the ground Z is calculated based on the leader NPC's Z.
# NoSpeedBuffs is an optional field for all speedbuffs to be removed during pathing if enabled
sub MoveInFormation {
my $npc = plugin::val('$npc');
my $entity_list = plugin::val('$entity_list');
my $SquadNPCID = $_[0];
my $Mob = $_[1];
my $OnGrid = $_[2];
my $MoveToX = $_[3];
my $MoveToY = $_[4];
my $NoSpeedBuffs = $_[5];
if (!$Mob) { $Mob = $npc; }
if (!$MoveToX && !$MoveToY && !$MoveToZ)
{
if (!$OnGrid)
{
$MoveToX = $Mob->GetGuardPointX();
$MoveToY = $Mob->GetGuardPointY();
#$MoveToZ = $Mob->GetGuardPointZ();
#plugin::Debug("Using Guard Locs");
}
else
{
#GetWaypointX(), GetWaypointY(), GetWaypointZ(), GetWaypointH(), GetWaypointPause(), and GetWaypointID().
#$Mob->CalculateNewWaypoint();
#my $WPID = int($Mob->GetWaypointID());
#my $WPPause = int($Mob->GetWaypointPause());
#my $CurX = int($Mob->GetX());
#my $CurY = int($Mob->GetY());
$MoveToX = $Mob->GetWaypointX();
$MoveToY = $Mob->GetWaypointY();
#$MoveToZ = $Mob->GetWaypointZ();
#plugin::Debug("Using WP:$WPID Pause:$WPPause WPX:$MoveToX WPY:$MoveToY CurX:$CurX CurY:$CurY");
}
}
my $NewHeading = $Mob->CalculateHeadingToTarget($MoveToX, $MoveToY);
if ($NoSpeedBuffs)
{
$Mob->BuffFadeByEffect(3); # Prevent Speed buffs from messing up the group of NPCs
}
my @npclist = $entity_list->GetNPCList();
foreach $ent (@npclist)
{
if($ent->GetNPCTypeID() == $SquadNPCID)
{
#plugin::Debug("Got NPC");
if ($NoSpeedBuffs)
{
$ent->BuffFadeByEffect(3); # Prevent Speed buffs from messing up the group of NPCs
}
# Set the Initial Relative Position and Heading variables
if(!$ent->EntityVariableExists(50))
{
# If this doesn't exist, then neither of them do
my $SquadX = $ent->GetX();
my $SquadY = $ent->GetY();
my $SquadHeading = $Mob->CalculateHeadingToTarget($SquadX, $SquadY);
my $CurHeading = $Mob->GetHeading();
my $CurDist = plugin::DistNoZ($Mob, $ent);
my $HeadingDiff = 0;
if ($CurHeading > $SquadHeading)
{
$HeadingDiff = $CurHeading - $SquadHeading;
}
else
{
$HeadingDiff = $CurHeading + 256 - $SquadHeading;
}
$ent->SetEntityVariable(50, $CurDist); # Original Distance from Leader
$ent->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
}
my $SquadDist = $ent->GetEntityVariable(50);
my $HDiff = $ent->GetEntityVariable(51);
#plugin::Debug("Got Variables");
my $NewSquadHeading = 0;
if ($NewHeading - $HDiff > 0)
{
$NewSquadHeading = $NewHeading - $HDiff;
}
else
{
$NewSquadHeading = 256 + $NewHeading - $HDiff;
}
#plugin::Debug("Before Calc");
my @DestArray = plugin::CalcDestFromHeadingXY($NewSquadHeading, $SquadDist, $MoveToX, $MoveToY, 10, $Mob);
my $NewX = $DestArray[0];
my $NewY = $DestArray[1];
my $NewZ = $DestArray[2];
#plugin::Debug("After Calc");
$ent->MoveTo($NewX, $NewY, $NewZ, $NewHeading, 1);
#plugin::Debug("Moving NPC");
}
}
}
#Usage: plugin::MoveToFormation(NPCID, LeaderMob, Distance, Columns, Rows, [LeadDist, MaxZDiff]);
# NPCID is the NPC Type ID of the NPCs you want to form the squad formation with
# LeaderMob is the Client or NPC you want to spawn the formation behind (use invisible and/or temp NPC to show no leader)
# Distance is the distance each member of the formation will be from each other on both axis
# Columns is the number of columns you want in the formation
# Rows is the number of rows you want in the formation
# LeadDist is the distance you want the formation to be created behind the leader
# MaxZDiff is the maximum height above the leader that the best Z will be searched for
sub MoveToFormation {
my $NPCID = $_[0];
my $Mob = $_[1];
my $Distance = $_[2];
my $Columns = $_[3];
my $Rows = $_[4];
my $LeadDist = $_[5];
my $MaxZDiff = $_[6];
if (!$MaxZDiff)
{
$MaxZDiff = 15;
}
if (!$LeadDist)
{
$LeadDist = $Distance;
}
my $npc = plugin::val('$npc');
my $client = plugin::val('$client');
my $entity_list = plugin::val('$entity_list');
if (!$Mob) {
if ($npc->IsNPC())
{
$Mob = $npc;
}
elsif ($client->IsClient())
{
$Mob = $client;
}
else
{
return; # No Client or NPC found
}
}
my $SpawnX = $Mob->GetX();
my $SpawnY = $Mob->GetY();
my $SpawnZ = $Mob->GetZ();
my $LeaderHeading = $Mob->GetHeading();
$SpawnX = $SpawnX + $LeadDist;
$SpawnY = $SpawnY - (($Columns - 1) * $Distance / 2);
my $NewX = $SpawnX;
my $NewY = $SpawnY;
my @npclist = $entity_list->GetNPCList();
my @SquadNPCs = (); # Creates a cleared array
foreach $ent (@npclist)
{
if($ent->GetNPCTypeID() == $NPCID) {
#plugin::Debug("Found a matching NPC");
push(@SquadNPCs, $ent->GetID()); # Adds an element to the end of the array
#plugin::Debug("Added the NPC to the list");
}
}
my $ArrayEntry = 0;
for ($ColNum = 0; $ColNum < $Columns; $ColNum++)
{
#plugin::Debug("Column $ColNum");
for ($RowNum = 0; $RowNum < $Rows; $RowNum++)
{
#plugin::Debug("Row $RowNum");
my $SquadHeading = $Mob->CalculateHeadingToTarget($NewX, $NewY);
my $CurDist = plugin::DistNoZToCoords($Mob, $NewX, $NewY);
my $DestH = 0;
if ($LeaderHeading > ($SquadHeading - 64))
{
$DestH = $LeaderHeading - $SquadHeading - 64;
}
else
{
$DestH = 256 + $LeaderHeading - $SquadHeading - 64;
}
my @DestArray = plugin::CalcDestFromHeading($DestH, $CurDist, $Mob, $MaxZDiff);
my $DestX = $DestArray[0];
my $DestY = $DestArray[1];
my $DestZ = $DestArray[2];
my $ArrayNPC = $SquadNPCs[$ArrayEntry];
my $CurNPC = $entity_list->GetNPCByID($ArrayNPC);
$CurNPC->MoveTo($DestX, $DestY, $DestZ, $LeaderHeading, 1);
my $NewSquadHeading = $Mob->CalculateHeadingToTarget($DestX, $DestY);
my $HeadingDiff = 0;
if ($LeaderHeading > $NewSquadHeading) {
$HeadingDiff = $LeaderHeading - $NewSquadHeading;
}
else {
$HeadingDiff = $LeaderHeading + 256 - $NewSquadHeading;
}
$CurNPC->SetEntityVariable(50, $CurDist); # Original Distance from Leader
$CurNPC->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
$NewX = $NewX + $Distance;
$ArrayEntry++;
}
$NewY = $NewY + $Distance;
$NewX = $SpawnX;
}
}
#Usage: plugin::RandomFormRoam(MaxVariance, MaxZVariance, LoSMobSize);
# NPCID is the NPC Type ID of the NPCs you want to form the squad formation with
# MaxVariance - Sets the max X/Y variance to travel
# MaxZVariance - Sets the max Z variance to travel. This field is optional and default is 15.
# LoSMobSize - Sets the size of the mob LoS check. This field is optional and default is 5.
# The LoS check basically looks from your NPC to an imaginary NPC of the LoSMobSize size to see if LoS exists
sub RandomFormRoam {
my $npc = plugin::val('$npc');
my $entity_list = plugin::val('$entity_list');
#my $NPCID = $_[0];
my $MaxVariance = $_[0];
my $MaxZVariance = $_[1];
my $LoSMobSize = $_[2];
#Set the Max Z Variance to 15 if no 3rd argument is set
if(!$MaxZVariance)
{
$MaxZVariance = 15;
}
#Set the LoS Check Mob Size to 5 if no 4th argument is set
if(!$LoSMobSize)
{
$LoSMobSize = 5;
}
# Don't try to roam if already engaged in combat!
if ($npc->IsEngaged() != 1)
{
#Get needed Locs
my $CurX = $npc->GetX();
my $CurY = $npc->GetY();
#my $CurZ = $npc->GetZ(); #Not currently required by this plugin
my $OrigX = $npc->GetSpawnPointX();
my $OrigY = $npc->GetSpawnPointY();
my $OrigZ = $npc->GetSpawnPointZ();
my $GuardX = $npc->GetGuardPointX();
my $GuardY = $npc->GetGuardPointY();
if ($CurX == $GuardX && $CurY == $GuardY)
{
#If the NPC has finished walking to the previous given Loc
#plugin::Debug("My Guard X is $GuardX and Y is $GuardY");
#Get a random X and Y within the set range
my $RandomX = int(rand($MaxVariance - 1)) + 1;
my $RandomY = int(rand($MaxVariance - 1)) + 1;
my $PosX = $OrigX + $RandomX;
my $PosY = $OrigY + $RandomY;
my $NegX = $OrigX - $RandomX;
my $NegY = $OrigY - $RandomY;
my $NewX = quest::ChooseRandom($PosX, $NegX);
my $NewY = quest::ChooseRandom($PosY, $NegY);
#Check for LoS and Z issues before moving to the new Loc
my $NewZ = $npc->FindGroundZ($NewX,$NewY, 5) + 1; #Add 1 to the new Z to prevent hopping issue when they arrive
if ($NewZ > -999999 && $OrigZ > ($NewZ - $MaxZVariance + 1) && $OrigZ < ($NewZ + $MaxZVariance - 1))
{
my $NewH = $npc->CalculateHeadingToTarget($NewX, $NewY);
my $LoS_Check = $npc->CheckLoSToLoc($NewX, $NewY, $NewZ, $LoSMobSize);
#Check LoS to the new random Loc
if ($LoS_Check)
{
quest::moveto($NewX, $NewY, $NewZ, $NewH, 1);
#plugin::MoveInFormation($NPCID, $npc, 0, $NewX, $NewY);
plugin::FollowFormLeader($npc, 0, $NewX, $NewY);
}
else
{
# If the NPC is not at its spawn point, return to it to prevent getting stuck
if ($OrigX != $CurX || $OrigY != $CurY)
{
quest::moveto($OrigX, $OrigY, $OrigZ, $NewH, 1);
plugin::FollowFormLeader($npc, 0, $NewX, $NewY);
}
}
}
}
}
}
#Usage: plugin::SpawnInFormation(NPCID, LeaderMob, Distance, Columns, Rows, [LeadDist, MaxZDiff]);
# NPCID is the NPC Type ID of the NPCs you want to form the squad formation with
# LeaderMob is the Client or NPC you want to spawn the formation behind (use invisible and/or temp NPC to show no leader)
# Distance is the distance each member of the formation will be from each other on both axis
# Columns is the number of columns you want in the formation
# Rows is the number of rows you want in the formation
# LeadDist is the distance you want the formation to be created behind the leader
# MaxZDiff is the maximum height above the leader that the best Z will be searched for
sub SpawnInFormation {
my $NPCID = $_[0];
my $Mob = $_[1];
my $Distance = $_[2];
my $Columns = $_[3];
my $Rows = $_[4];
my $LeadDist = $_[5];
my $MaxZDiff = $_[6];
if (!$MaxZDiff)
{
$MaxZDiff = 15;
}
if (!$LeadDist)
{
$LeadDist = $Distance;
}
my $npc = plugin::val('$npc');
my $client = plugin::val('$client');
my $entity_list = plugin::val('$entity_list');
if (!$Mob) {
if ($npc->IsNPC())
{
$Mob = $npc;
}
elsif ($client->IsClient())
{
$Mob = $client;
}
else
{
return; # No Client or NPC found
}
}
my $SpawnX = $Mob->GetX();
my $SpawnY = $Mob->GetY();
my $SpawnZ = $Mob->GetZ();
my $LeaderHeading = $Mob->GetHeading();
$SpawnX = $SpawnX + $LeadDist;
$SpawnY = $SpawnY - (($Columns - 1) * $Distance / 2);
my $NewX = $SpawnX;
my $NewY = $SpawnY;
for ($ColNum = 0; $ColNum < $Columns; $ColNum++)
{
#plugin::Debug("Column $ColNum");
for ($RowNum = 0; $RowNum < $Rows; $RowNum++)
{
#plugin::Debug("Row $RowNum");
my $SquadHeading = $Mob->CalculateHeadingToTarget($NewX, $NewY);
my $CurDist = plugin::DistNoZToCoords($Mob, $NewX, $NewY);
my $DestH = 0;
if ($LeaderHeading > ($SquadHeading - 64))
{
$DestH = $LeaderHeading - $SquadHeading - 64;
}
else
{
$DestH = 256 + $LeaderHeading - $SquadHeading - 64;
}
my @DestArray = plugin::CalcDestFromHeading($DestH, $CurDist, $Mob, $MaxZDiff);
my $DestX = $DestArray[0];
my $DestY = $DestArray[1];
my $DestZ = $DestArray[2];
my $EntityID = quest::spawn2($NPCID, 0, 0, $DestX, $DestY, $DestZ, $LeaderHeading);
my $CurNPC = $entity_list->GetNPCByID($EntityID);
my $NewSquadHeading = $Mob->CalculateHeadingToTarget($DestX, $DestY);
my $HeadingDiff = 0;
if ($LeaderHeading > $NewSquadHeading)
{
$HeadingDiff = $LeaderHeading - $NewSquadHeading;
}
else
{
$HeadingDiff = $LeaderHeading + 256 - $NewSquadHeading;
}
my $LeaderID = $Mob->GetID();
$CurNPC->SetEntityVariable(50, $CurDist); # Original Distance from Leader
$CurNPC->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
$CurNPC->SetEntityVariable(52, $LeaderID); # Save the entity ID of the leader NPC
$NewX = $NewX + $Distance;
}
$NewY = $NewY + $Distance;
$NewX = $SpawnX;
}
}
#Usage: plugin::SpawnInFormationXY(NPCID, X, Y, Z, Distance, Columns, Rows, Heading, MaxZDiff);
# NPCID is the NPC Type ID of the NPCs you want to form the squad formation with
# X/Y/Z are the coords of the first NPC to spawn that the others are spawned based on it's location
# Distance is the distance each member of the formation will be from each other on both axis
# Columns is the number of columns you want in the formation
# Rows is the number of rows you want in the formation
# Heading is heading for the spawns to face
# MaxZDiff is the maximum height above the leader that the best Z will be searched for
sub SpawnInFormationXY {
my $NPCID = $_[0];
my $SpawnX = $_[1];
my $SpawnY = $_[2];
my $SpawnZ = $_[3];
my $Distance = $_[4];
my $Columns = $_[5];
my $Rows = $_[6];
my $Heading = $_[7];
my $MaxZDiff = $_[8];
if (!$Heading)
{
$Heading = 0;
}
if (!$MaxZDiff)
{
$MaxZDiff = 15;
}
my $entity_list = plugin::val('$entity_list');
$SpawnX = $SpawnX + $Distance;
$SpawnY = $SpawnY - (($Columns - 1) * $Distance / 2);
# Spawn the first NPC
quest::spawn2($NPCID, 0, 0, $SpawnX, $SpawnY, $SpawnZ, $Heading);
#plugin::Debug("Spawned First NPC");
# Get the first NPC
my $MainNPC = $entity_list->GetNPCByNPCTypeID($NPCID);
#plugin::Debug("Got First NPC");
my $NewX = $SpawnX;
my $NewY = $SpawnY;
#my $RowNum = 1; # Prevent Respawn over the first NPC
$NewX = $NewX + $Distance;
if ($MainNPC)
{
for ($ColNum = 0; $ColNum < $Columns; $ColNum++)
{
#plugin::Debug("Column $ColNum");
for ($RowNum = 0; $RowNum < $Rows; $RowNum++)
{
# Prevent Respawn over the first NPC
if ($RowNum > 0 || $ColNum > 0)
{
#plugin::Debug("Row $RowNum");
my $NewZ = $MainNPC->FindGroundZ($NewX, $NewY, $MaxZDiff);
quest::spawn2($NPCID, 0, 0, $NewX, $NewY, $NewZ, $Heading);
$NewX = $NewX + $Distance;
}
}
$NewY = $NewY + $Distance;
$NewX = $SpawnX;
}
}
}
#Usage: plugin::SpawnMixedFormation(LeaderMob, Distance, Columns, Rows, LeadDist=Distance, MaxZDiff=15, NPCID, NPCID...);
# Works just like SpawnInFormation, accept this allows mixing multiple NPCs into the formation
# Can add as many NPCIDs as wanted by appending to the end of the arguments
# NPCID is the NPC Type ID of the NPCs you want to form the squad formation with
# LeaderMob is the Client or NPC you want to spawn the formation behind (use invisible and/or temp NPC to show no leader)
# Distance is the distance each member of the formation will be from each other on both axis
# Columns is the number of columns you want in the formation
# Rows is the number of rows you want in the formation
# LeadDist is the distance you want the formation to be created behind the leader
# MaxZDiff is the maximum height above the leader that the best Z will be searched for
sub SpawnMixedFormation {
my $Mob = $_[0];
my $Distance = $_[1];
my $Columns = $_[2];
my $Rows = $_[3];
my $LeadDist = $_[4];
my $MaxZDiff = $_[5];
if (!$MaxZDiff)
{
$MaxZDiff = 15;
}
if (!$LeadDist)
{
$LeadDist = $Distance;
}
if(!$_[6])
{
plugin::Debug("At least 1 NPCID is required for SpawnMixedFormation plugin!");
}
my $LastArg = 6;
while($_[$LastArg])
{
$LastArg++;
}
$LastArg--;
my $npc = plugin::val('$npc');
my $client = plugin::val('$client');
my $entity_list = plugin::val('$entity_list');
if (!$Mob) {
if ($npc->IsNPC())
{
$Mob = $npc;
}
elsif ($client->IsClient())
{
$Mob = $client;
}
else
{
return; # No Client or NPC found
}
}
my $SpawnX = $Mob->GetX();
my $SpawnY = $Mob->GetY();
my $SpawnZ = $Mob->GetZ();
my $LeaderHeading = $Mob->GetHeading();
$SpawnX = $SpawnX + $LeadDist;
$SpawnY = $SpawnY - (($Columns - 1) * $Distance / 2);
my $NewX = $SpawnX;
my $NewY = $SpawnY;
for ($ColNum = 0; $ColNum < $Columns; $ColNum++)
{
#plugin::Debug("Column $ColNum");
for ($RowNum = 0; $RowNum < $Rows; $RowNum++)
{
#plugin::Debug("Row $RowNum");
my $SquadHeading = $Mob->CalculateHeadingToTarget($NewX, $NewY);
my $CurDist = plugin::DistNoZToCoords($Mob, $NewX, $NewY);
my $DestH = 0;
if ($LeaderHeading > ($SquadHeading - 64))
{
$DestH = $LeaderHeading - $SquadHeading - 64;
}
else
{
$DestH = 256 + $LeaderHeading - $SquadHeading - 64;
}
my @DestArray = plugin::CalcDestFromHeading($DestH, $CurDist, $Mob, $MaxZDiff);
my $DestX = $DestArray[0];
my $DestY = $DestArray[1];
my $DestZ = $DestArray[2];
my $RandomNPCIDArg = plugin::RandomRange(6, $LastArg);
my $NPCID = $_[$RandomNPCIDArg];
my $EntityID = quest::spawn2($NPCID, 0, 0, $DestX, $DestY, $DestZ, $LeaderHeading);
my $CurNPC = $entity_list->GetNPCByID($EntityID);
my $NewSquadHeading = $Mob->CalculateHeadingToTarget($DestX, $DestY);
my $HeadingDiff = 0;
if ($LeaderHeading > $NewSquadHeading) {
$HeadingDiff = $LeaderHeading - $NewSquadHeading;
}
else {
$HeadingDiff = $LeaderHeading + 256 - $NewSquadHeading;
}
my $LeaderID = $Mob->GetID();
$CurNPC->SetEntityVariable(50, $CurDist); # Original Distance from Leader
$CurNPC->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
$CurNPC->SetEntityVariable(52, $LeaderID); # Save the entity ID of the leader NPC
$NewX = $NewX + $Distance;
}
$NewY = $NewY + $Distance;
$NewX = $SpawnX;
}
}
#Usage: plugin::FollowInFormation(NPCID, Mob, [MaxZDiff]);
# This script will move all of 1 NPC Type ID to the relative Guard position that they were the first time this plugin was run
# NPCID is the NPC ID of the NPCs you want to move in the formation (moves all of this NPC ID in the whole zone)
# Mob is the leader NPC that you want the NPCs to follow in formation
# MaxZDiff is an optional field (default of 30) to set the Max Z the NPCs will check to find the ground Z location
sub FollowInFormation {
my $entity_list = plugin::val('$entity_list');
my $SquadNPCID = $_[0];
my $Mob = $_[1];
my $MaxZDiff = $_[2];
my $MoveToX = $Mob->GetX();
my $MoveToY = $Mob->GetY();
if(!$MaxZDiff) { $MaxZDiff = 15; }
#my $NewHeading = $Mob->CalculateHeadingToTarget($MoveToX, $MoveToY);
my $NewHeading = $Mob->GetHeading();
my $RunSpeed = 0;
my @npclist = $entity_list->GetNPCList();
foreach $ent (@npclist)
{
if($ent->GetNPCTypeID() == $SquadNPCID) {
# Set the Initial Relative Position and Heading variables
if(!$ent->EntityVariableExists(50)) { # If this doesn't exist, then neither of them do
my $SquadX = $ent->GetX();
my $SquadY = $ent->GetY();
my $SquadHeading = $Mob->CalculateHeadingToTarget($SquadX, $SquadY);
my $CurHeading = $Mob->GetHeading();
my $StartDist = plugin::DistNoZ($Mob, $ent);
my $HeadingDiff = 0;
if ($CurHeading > $SquadHeading) {
$HeadingDiff = $CurHeading - $SquadHeading;
}
else {
$HeadingDiff = $CurHeading + 256 - $SquadHeading;
}
$ent->SetEntityVariable(50, $StartDist); # Original Distance from Leader
$ent->SetEntityVariable(51, $HeadingDiff); # Original Heading Difference from Leader
}
my $SquadDist = $ent->GetEntityVariable(50);
my $HDiff = $ent->GetEntityVariable(51);
#plugin::Debug("Got Variables");
#Set Run Speed depending on distance from the leader
if(!$RunSpeed)
{
my $CurDist = plugin::DistNoZ($Mob, $ent);
if ($CurDist > ($SquadDist * 10))
{
$RunSpeed = 5;
}
elsif($CurDist > ($SquadDist * 5))
{
$RunSpeed = 3;
}
else
{
$RunSpeed = 1.25;
}
}
$ent->ModifyNPCStat("runspeed", $RunSpeed);
my $NewSquadHeading = 0;
if ($NewHeading - $HDiff > 0) {
$NewSquadHeading = $NewHeading - $HDiff;
}
else {
$NewSquadHeading = 256 + $NewHeading - $HDiff;
}
#plugin::Debug("Before Calc");
#Usage: my @DestArray = plugin::CalcDestFromHeadingXY(Heading, Distance, X, Y, [MaxZDiff, Mob]);
my @DestArray = plugin::CalcDestFromHeadingXY($NewSquadHeading, $SquadDist, $MoveToX, $MoveToY, $MaxZDiff, $Mob);
my $NewX = $DestArray[0];
my $NewY = $DestArray[1];
my $NewZ = $DestArray[2];
#plugin::Debug("After Calc");
$ent->MoveTo($NewX, $NewY, $NewZ, $NewHeading, 1);
#plugin::Debug("Moving NPC");
}
}
}
#Usage: plugin::SquadAttackTarget(SquadNPCID, TargetID);
# Squad1NPCID is the NPC ID of the Squad attacking the other
# TargetID is the Entity ID of the Target being attacked
sub SquadAttackTarget {
my $entity_list = plugin::val('$entity_list');
my $SquadNPCID = $_[0];
my $TargetID = $_[1];
my $TargetNPC = $entity_list->GetNPCByID($TargetID); # Gets the NPC from the array
if ($TargetNPC)
{
if($TargetNPC->EntityVariableExists(50)) # If this exists, the target is part of a squad
{
# Have the squad attack the other squad
my $TargetNPCID = $TargetNPC->GetNPCTypeID();
plugin::SquadAttackSquad($SquadNPCID, $TargetNPCID);
}
else
{
my @npclist = $entity_list->GetNPCList();
foreach $ent (@npclist)
{
# Have the squad attack the single target
if($ent->GetNPCTypeID() == $SquadNPCID) {
$ent->AddToHateList($TargetNPC, 1);
}
}
}
}
}
#Usage: plugin::SquadAttackSquad(Squad1NPCID, Squad2NPCID);
# Squad1NPCID is the NPC ID of the Squad attacking the other squad
# Squad2NPCID is the NPC ID of the Squad being attacked
sub SquadAttackSquad {
my $entity_list = plugin::val('$entity_list');
my $SquadNPCID1 = $_[0];
my $SquadNPCID2 = $_[1];
my @npclist = $entity_list->GetNPCList();
my @Squad1 = (); # Creates a cleared array
my @Squad2 = (); # Creates a cleared array
foreach $ent (@npclist)
{
if($ent->GetNPCTypeID() == $SquadNPCID1) {
push(@Squad1, $ent->GetID()); # Adds an element to the end of the array
}
if($ent->GetNPCTypeID() == $SquadNPCID2) {
push(@Squad2, $ent->GetID()); # Adds an element to the end of the array
}
}
my $ArraySize1 = @Squad1;
my $ArraySize2 = @Squad2;
#plugin::Debug("Squad1: $ArraySize1, Squad2: $ArraySize2");
foreach $EntID (@Squad1)
{
if ($EntID)
{
my $GotSquadNPC = $entity_list->GetNPCByID($EntID); # Gets the NPC from the array
my $RandNPC = plugin::RandomRange(0, $ArraySize2); # Pick one of the chosen NPCs
my $GotNPC2 = $entity_list->GetNPCByID($Squad2[$RandNPC]); # Gets the NPC from the array
#AddToHateList(other, hate= 0, damage= 0, iYellForHelp= true, bFrenzy= false, iBuffTic= false)
if ($GotNPC2 && $GotNPC2->IsNPC())
{
$GotSquadNPC->AddToHateList($GotNPC2, 1, 0, 0);
}
else
{
my $GotNPCDefault = $entity_list->GetNPCByID($Squad2[0]); # Gets the NPC from the array
$GotSquadNPC->AddToHateList($GotNPCDefault, 1, 0, 0);
}
}
}
}
return 1; #This line is required at the end of every plugin file in order to use it
#Usage: my @DestArray = plugin::CalcDestFromHeading(Heading, Distance, [Mob, MaxZDiff]);
# This plugin calculates the destination X and Y loc based on heading and distance
# Heading is the heading you want to calculate destination X Y Z from
# Distance is the distance you want for the destination X and Y from the source
# Mob is an optional field to allow any mob to be set, but $npc is default
# MaxZDiff is the max height difference from the source mob's Z you want to calculate the destination from.
#
# The output array can be used as follows:
# my $DestX = $DestArray[0];
# my $DestY = $DestArray[1];
# my $DestZ = $DestArray[2];
sub CalcDestFromHeading {
my $npc = plugin::val('$npc');
my $Heading = $_[0];
my $Distance = $_[1];
my $Mob = $_[2];
my $MaxZDiff = $_[3];
if (!$Distance) { return; }
if (!$Mob) { $Mob = $npc; }
if (!$MaxZDiff) { $MaxZDiff = 50; }
my $ReverseHeading = 256 - $Heading;
my $ConvertAngle = $ReverseHeading * 1.40625;
if ($ConvertAngle <= 270) {
$ConvertAngle = $ConvertAngle + 90;
}
else {
$ConvertAngle = $ConvertAngle - 270;
}
my $Radian = $ConvertAngle * (3.1415927 / 180);
my $CircleX = $Distance * cos($Radian);
my $CircleY = $Distance * sin($Radian);
my $DestX = $CircleX + $Mob->GetX();
my $DestY = $CircleY + $Mob->GetY();
my $DestZ = $Mob->FindGroundZ($DestX, $DestY, $MaxZDiff);
my @DestArray = ($DestX, $DestY, $DestZ);
return @DestArray;
}
#Usage: my @DestArray = plugin::CalcDestFromHeadingXY(Heading, Distance, X, Y, [MaxZDiff, Mob]);
# This plugin calculates the destination X and Y loc based on heading and distance
# Heading is the heading you want to calculate destination X Y Z from
# Distance is the distance you want for the destination X and Y from the source
# X is the X loc that the destination will be calculated from
# Y is the Y loc that the destination will be calculated from
# MaxZDiff is the max height difference from the source mob's Z you want to calculate the destination from.
#
# The output array can be used as follows:
# my $DestX = $DestArray[0];
# my $DestY = $DestArray[1];
# my $DestZ = $DestArray[2];
sub CalcDestFromHeadingXY {
my $npc = plugin::val('$npc');
my $client = plugin::val('$client');
my $Heading = $_[0];
my $Distance = $_[1];
my $MobX = $_[2];
my $MobY = $_[3];
my $MaxZDiff = $_[4];
my $Mob = $_[5];
if (!$Mob) {
if ($npc->IsNPC())
{
$Mob = $npc;
}
elsif ($client->IsClient())
{
$Mob = $client;
}
else
{
return; # No Client or NPC found
}
}
if (!$Distance) { return; }
if (!$MaxZDiff) { $MaxZDiff = 50; }
my $ReverseHeading = 256 - $Heading;
my $ConvertAngle = $ReverseHeading * 1.40625;
if ($ConvertAngle <= 270) {
$ConvertAngle = $ConvertAngle + 90;
}
else {
$ConvertAngle = $ConvertAngle - 270;
}
my $PI = 3.141592653589793238;
my $Radian = $ConvertAngle * ($PI / 180);
my $CircleX = $Distance * cos($Radian);
my $CircleY = $Distance * sin($Radian);
my $DestX = $CircleX + $MobX;
my $DestY = $CircleY + $MobY;
my $DestZ = $Mob->FindGroundZ($DestX, $DestY, $MaxZDiff);
my @DestArray = ($DestX, $DestY, $DestZ);
return @DestArray;
}
#Usage: plugin::GetReverseHeading($mob);
# Returns the heading of the opposite direction the mob is facing
sub GetReverseHeading {
my $Mob = $_[0];
my $CurHeading = $Mob->GetHeading();
my $ReverseHeading = 128 + $CurHeading;
if ($ReverseHeading >= 256) {
$ReverseHeading = $ReverseHeading - 256;
}
return $ReverseHeading;
}
#Usage: my $Degrees = plugin::ConvertHeadingToDegrees(Heading);
# Converts 0-256 headings into 0 to 360 degrees
sub ConvertHeadingToDegrees {
my $Heading = $_[0];
my $ReverseHeading = 256 - $Heading;
my $ConvertAngle = $ReverseHeading * 1.40625;
if ($ConvertAngle <= 270) {
$ConvertAngle = $ConvertAngle + 90;
}
else {
$ConvertAngle = $ConvertAngle - 270;
}
return $ConvertAngle;
}
#Usage: plugin::Debug("Message", Color, Mob);
# "Message" is a required field and is the message you want to show up in the debug
# Color is an optional field and if not set will default to a pink/purple color
# Mob is an optional field if you want to export a particular mob to debug from (used for getting the name)
# Example 1: plugin::Debug("Event Started", 7);
# Example 2: plugin::Debug("Event Started");
sub Debug {
my $npc = plugin::val('$npc');
my $client = plugin::val('$client');
my $MyMessage = $_[0];
my $TextColor = $_[1];
if (!$TextColor)
{
$TextColor = 326; #Set the Text Color for the Message (this one is bright purple)
}
my $Mob = $_[2];
if (!$Mob)
{
if ($npc)
{
# NPC Quest
$Mob = $npc;
}
elsif ($client)
{
# Player Quest
$Mob = $client;
}
}
my $MobName = "NO_NAME";
if ($Mob) {
#Get the clean name of the Mob sending the message
$MobName = $Mob->GetCleanName();
}
#Send a message in purple (default) to GMs in the Zone only
quest::gmsay("$MobName Debugs: $MyMessage", $TextColor);
}
return 1; #This line is required at the end of every plugin file in order to use it