SCRIPT TITLE: inc_guards

SUMMARY: Functions to create a permanently respawning guard system for one or more areas. Useful for persistent worlds, or large city areas which should have a near-inexhaustible supply of guards. The script will maintain a near-constant population of a preset number of guards, the number being able to be set on an area-by-area basis.

HOW TO USE THIS SCRIPT:

1. This script is designed for use as an include, so must be saved *without* compiling.

2. This script is taken from my upcoming module, Darkness of the Sun. As I use the Baldur's Gate style ARxxxx system for my area tag names, these functions are designed making the assumption that area tags will be in this format. This means that when using this script, it is strongly suggested that you also use this system for naming area tags. It has not been tested with any other system of area tag names, so you have been warned.

3. To install this script on an area, do the following:

a) Create a SINGLE waypoint in the area, with the tag AreaTag_GuardSpawnPoint, where AreaTag should, of course, be replaced by the area's actual tag. There should be exactly one of these waypoints per area. Script behavior is undefined if more than one such waypoint is present. If the spawn waypoint is not present, the spawning function will do nothing. I usually place this spawnpoint in front of a door, to give the impression that guards are coming out of a building.

b) Unless you have adjusted the chance of female guards to either 0% or 100% (defaults to 33%; see below for how to adjust this), or unless you *want* badgers, you will need to create two guard templates for each area using the script. These should have blueprint ResRefs of guardXXXXm and guardXXXXf, where XXXX are the four digits at the end of your area tag. It is designed for male and female guards, but you could have these be any two types of creature you wanted, of course (though they will still use the "m" and "f" designators to distinguish their ResRefs).

c) Either by modifying the OnDeath script, or by setting the OnSpawn script to give a user defined event on death and modifying the OnUserDefined script accordingly, these guard templates must be set to call GuardDied(OBJECT_SELF) upon death of the guard. Don't forget to #include this script, or it won't compile.

d) Unless you want your guards to just stand around the guardhouse door doing nothing, you will want to set up a set of waypoints for creatures of their tag.

e) Somewhere, probably in the OnModuleLoad script in the module properties, set the local variable MAXGUARDS for the area. This should be set to whatever number of guards you desire to be patrolling this area under normal circumstances.

f) Include this include file, and add a call to CheckGuards() to the OnHeartbeat script for the area. CheckGuards() needs no parameters and returns nothing, and works correctly only if called by an area object.

4. If you have performed the instructions in step 3 correctly, guards should gradually spawn into the area and begin patrolling, until the number of guards you set in the local variable MAXGUARDS are present. At this point no more guards will spawn until and unless one or more dies. If any guards die, more will spawn until MAXGUARDS is again reached.

5. The following parameters are adjustable:

Delay between spawns; also the delay following a guard's death before a new one spawns: To change this, find the line in CheckGuards() which sets the local int GSPDELAY, and change the number (which will be measured in area heartbeats). Note that since guards are all spawned off the same one or two blueprints (and thus all share the same two (at most) Tags), if you set this too low, you will find that the guards will patrol -very- close together (since they are walking the same set of waypoints). This cannot currently be changed on an area-by-area basis. To allow such is not difficult - it would simply be necessary to have an additional LocalInt initialized per area, and to reset GSPDELAY to that instead of a fixed constant.

Percentage of female guards: To change this, find the line in SpawnAreaGuard() which has "if (FemaleRoll <= 33)" and change the 33 to some other number (between 0 and 100). Note that if you set it to 0 or 100 you will need only one gender template. Also note that this changes the gender percentage for *ALL* areas that use the script (it is not currently possible to set a gender percentage on an area-by-area basis, though it is relatively simple to edit the script to do this - simply have another LocalInt initialized for each area and compare the FemaleRoll to that instead of to the fixed number).

Number of guards per area: Is set on an area-by-area basis by changing the local variable MAXGUARDS. As mentioned above, this variable MUST be set in order for the script to work (otherwise, CheckGuards() will see that there are zero out of zero guards present, and naturally will exit, seeing that the guard count is already adequate - thus the script will do nothing). If it is increased during gameplay, more guards will begin to spawn to fill the deficit; however, if it is reduced, existing surplus guards will remain until death removes them from play.

6. The script is designed for area patrol guards who do not leave the area once spawned (unless killed). Script behavior is undefined if guards are permitted (through waypoint placement or otherwise) to leave the area they are spawned in.




// Functions for area guards
// Function declarations for sanity
int SpawnAreaGuard ( object theArea);
void CheckGuards();
void GuardDied ( object Guard);
// Make sure there are a certain number of guards around.
// If there isn't, spawn a new one at the area's guard
// spawn point.
// This function only works correctly if called by area
// objects. The area must previously have had the local
// integers NUMGUARDS and MAXGUARDS set (presumably in
// the module load script). Otherwise, both variables
// will be assumed zero, and no guards will be spawned.
void CheckGuards()
{
// number of guards active right now in this area
int numGuards = GetLocalInt ( OBJECT_SELF, "NUMGUARDS");
// number of guards there *should* be in the area
int maxGuards = GetLocalInt ( OBJECT_SELF, "MAXGUARDS");
// if we're adequately patrolled, return
if (numGuards >= maxGuards)
return;
// otherwise, check the spawn delay
int spawnDelay = GetLocalInt ( OBJECT_SELF, "GSPDELAY");
// are we ready to spawn a guard?
if (spawnDelay > 0)
{
// nope, we aren't ready. reduce the Spawn Delay
// by one, and return
spawnDelay = spawnDelay - 1;
SetLocalInt ( OBJECT_SELF, "GSPDELAY", spawnDelay);
return;
}
int spawn = SpawnAreaGuard ( OBJECT_SELF);
// if failed, don't continue!
if (spawn == -1)
return;
// and increment the number of guards in area
numGuards = numGuards + 1;
SetLocalInt ( OBJECT_SELF, "NUMGUARDS", numGuards);
// reset the spawn delay
SetLocalInt ( OBJECT_SELF, "GSPDELAY", 10);
}
// Spawn a guard in an area, using a spawn point of the
// format (AreaTag) _GuardSpawnPoint.
// This function should also -only- be used on area
// objects. The area's tag should be in the format
// ARxxxx, where xxxx is a four-digit number. Also,
// two guard templates should exist with ResRef names
// of guardxxxxm and guardxxxxf, where xxxx is the same
// four digit number as per the area.
// Returns -1 on error (bad area tag or missing spawn
// point), 0 otherwise.
int SpawnAreaGuard ( object theArea)
{
string areaNumber = GetStringRight ( GetTag(theArea), 4);
string guardTemplate;
string areaTag = GetTag(theArea);
// Bad area tag
if (areaNumber == "")
return -1;
// Determine sex of guard to spawn
int FemaleRoll = Random(100) + 1;
if (FemaleRoll <= 33) // chance of female: 33%
{
guardTemplate = "guard" + areaNumber + "f";
}
else
{
guardTemplate = "guard" + areaNumber + "m";
}
// Get spawn spot
object SpawnWaypoint = GetWaypointByTag (areaTag + "_GuardSpawnPoint");
// if invalid, bail
if (SpawnWaypoint == OBJECT_INVALID)
return -1;
location SpawnSpot = GetLocation (SpawnWaypoint);
CreateObject (OBJECT_TYPE_CREATURE, guardTemplate, SpawnSpot, FALSE);
return 0;
}

// Call this on guard death, to reduce area's guard count
void GuardDied(object Guard)
{
object Area = GetArea(Guard);
int GuardMax = GetLocalInt(Area, "MAXGUARDS");
// this area does not use the auto-spawn guard
// system
if (GuardMax == 0)
return;
// otherwise, update the guard count
int GuardCount = GetLocalInt(Area, "NUMGUARDS");
GuardCount = GuardCount - 1;
SetLocalInt(Area, "NUMGUARDS", GuardCount);
}