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);
}