Ok, I solved your second problem at least, the script (with the instructions) is the following:
//Random patrol spawner by Clangeddin (2018)
//This script has to be slotted in the Heartbeat of the AREA where the npcs are intended to spawn.
//To be properly set up for it to work the following has to be done:
// - There have to be at least 2 waypoints with proper tags.
// The tags of the waypoints have to be "wp_xxxx_y"
// Where xxxx is the tag of the area and Y is a number from 1 (0 will not work) to the number of tags.
// The y number has to be consecutive (ie 1,2,3,4) ecc... if there's no 2, nothing will happen.
// - The area needs to have set certain local variables (all integers) on it:
// RANDOM_NUM: The number of npcs that will spawn. This one is mandatory and has to be 1 or higher.
// RANDOM_GROUP (optional): The number of the group where the type of npc is picked from.
// See below in GetCreatureByGroup function. If no value is set, the default will be picked.
// RANDOM_RUN (optional). If set to 1, the spawns will run, otherwise they will just walk.
// RANDOM_LOOP (optional). If set to 1, the patrols will go back to wp_xxxx_1 after they have reached wp_xxxx_max.
// If not set, the patrols will exit the map (be destroyed) after theyr each the last waypoint.
const string RANDOM_VAR = "RANDOM_SPAWNS";
//USE RESREF, NOT THE TAG (although in many cases they are identical).
string GetCreatureByGroup(int nGROUP)
int nROLL = d100(1);
if (nROLL > 66) return "N_Graycloak_01_ref";
else if (nROLL > 33) return "N_Graycloak_02_ref";
else return "N_Graycloak_03_ref";
//Insert more cases down below to make different groups
//Make sure to assign the correct variables to the area
//The Default values are just here for testing purposes
//If you have lots of areas that are supposed to use this group
//it might be a good idea to modify this value and use it instead
if (nROLL > 66) return "n_greycloak";
else if (nROLL > 33) return "n_royalguard";
else return "n_watchman";
void MoveToNextWP(object oAREA, object oNPC, string sAREA, int nTOTAL, int nCURRENT, int nRUN, int nLOOP)
if (GetCurrentAction(oNPC) == ACTION_MOVETOPOINT)
DelayCommand(0.5, MoveToNextWP(oAREA, oNPC, sAREA, nTOTAL, nCURRENT, nRUN, nLOOP));
if (nCURRENT > nTOTAL)
if (nLOOP == TRUE) DelayCommand(0.5, MoveToNextWP(oAREA, oNPC, sAREA, nTOTAL, 1, nRUN, nLOOP));
int nSPAWN = GetLocalInt(oAREA, RANDOM_VAR);
SetLocalInt(oAREA, RANDOM_VAR, nSPAWN - 1);
AssignCommand(oNPC, SetIsDestroyable(TRUE, FALSE));
DestroyObject(oNPC, 0.0f, FALSE);
int nNEXT = nCURRENT;
object oCURRENT = GetWaypointByTag("wp_" + sAREA + "_" + IntToString(nCURRENT));
if (GetDistanceBetween(oNPC, oCURRENT) > 1.0f) AssignCommand(oNPC, ActionForceMoveToObject(oCURRENT, nRUN));
nNEXT = nCURRENT + 1;
object oNEXT = GetWaypointByTag("wp_" + sAREA + "_" + IntToString(nNEXT));
AssignCommand(oNPC, ActionForceMoveToObject(oNEXT, nRUN));
DelayCommand(0.5, MoveToNextWP(oAREA, oNPC, sAREA, nTOTAL, nNEXT, nRUN, nLOOP));
void SpawnRandomGroup(object oAREA, string sAREA, location lSPAWN, int nGROUP, int nNUM, int nTOTAL, int nRUN, int nLOOP)
while (nNUM > 0)
oNPC = CreateObject(OBJECT_TYPE_CREATURE, GetCreatureByGroup(nGROUP), lSPAWN);
if (oNPC != OBJECT_INVALID)
MoveToNextWP(oAREA, oNPC, sAREA, nTOTAL, 1, nRUN, nLOOP);
nSPAWN = nSPAWN + 1;
nNUM = nNUM - 1;
if (nSPAWN < 1) return;
int nAREA = GetLocalInt(oAREA, RANDOM_VAR);
SetLocalInt(oAREA, RANDOM_VAR, nAREA + nSPAWN);
object oAREA = OBJECT_SELF;
if (GetLocalInt(oAREA, RANDOM_VAR) > 0) return;
int nNUM = GetLocalInt(oAREA, "RANDOM_NUM");
if (nNUM < 1) return;
string sAREA = GetTag(oAREA);
int nTOTAL = 0;
object oSPAWN = GetWaypointByTag("wp_" + sAREA + "_1");
object oWAY = oSPAWN;
while (oWAY != OBJECT_INVALID)
nTOTAL = nTOTAL + 1;
oWAY = GetWaypointByTag("wp_" + sAREA + "_" + IntToString(nTOTAL + 1));
if (nTOTAL < 2) return;
int nLOOP = GetLocalInt(oAREA, "RANDOM_LOOP");
int nGROUP = GetLocalInt(oAREA, "RANDOM_GROUP");
int nRUN = GetLocalInt(oAREA, "RANDOM_RUN");
if (nRUN != TRUE) nRUN = FALSE;
location lSPAWN = GetLocation(oSPAWN);
SpawnRandomGroup(oAREA, sAREA, lSPAWN, nGROUP, nNUM, nTOTAL, nRUN, nLOOP);
You can see its working implementation in this very small module I just made to test it.