/*##
# NWN Spawn System v2.4.2
# Author: Palor <palor@truefear.net>
# Description:
#  Complete Spawn System, one script.
#
##*
# Installation.
#
# 1. Create an invisible object and attach this script to
#    it's Heartbeat. (You only need the one object for all
#    your spawn points in the area).
# 2. Decide what creature you want to spawn and make a note
#    of it's Tag or create a custom creature.
# 3. Place a Waypoint and edit the properties to look like:
#
# Name: SP_SX01
# Tag : TagOfCreatureYouWantToSpawn
#
# The above would make a single spawn point which would
# always keep one of your creatures spawned there.
#
# NOTE:
# Waypoint and Post works with this script.
# Example:
#  Name: SP_SX01
#  Tag : POST_NW_DOG
#
# The above would keep one dog spawned at the waypoint and
# the creature would return to it after combat.
#
##*
# Changes
#
# 2.4.2
#  - Added TD for Trap Disarm (DT was already taken).
#    - Requested by Erdo
#  - Modified functions to reduce CPU usage and TMIs.
#
# 2.4.1a
#  - Changed X for PI to D, since X already existed :)
#    - Reported by Karthal
#
# 2.4.1
#  - Added X to PI which will destroy the object in the
#    players inventory. - Requested by Karthal
#  - Added SL and ML for StartLevel and MaxLevel. These
#    can be used with RD and PA. - Requested by Jelloman2K
#  - Added PV which will NOT kill a creature if a player
#    is within view. - Requested by Gloomveil
#
# 2.4
#  - Fixed bug with SH. - Reported by Benjamin
#
# 2.3.7e
#  - Fixed bug with TR. - Reported by Kapell
#
# 2.3.7d
#  - Modified GP to ignore SX unless GR is used.
#  - Modified random function of SX.
#  - Modified timer function.
#
# 2.3.7c
#  - Added ID for controlling the spawn from triggers.
#
# 2.3.7b
#  - Modified code structure.
#  - Fixed minor unreported bugs :)
#  - Fixed CPU issues. - Reported by SolarflareX & Maska
#
# 2.3.7a
#  - Fixed several bugs with CH.
#    - Reported by forcrimanysakes
#
# 2.3.7
#  - Removed creature tag requirement in Spawn Points tag.
#    A valid blueprint is still required though.
#  - Modified IG to use roll for random item.
#  - Modified IG to give entire group if no roll is given.
#    - Requested by razar77
#  - Modified GT to accept a roll. - Requested by Wizzle
#
# 2.3.6f
#  - Fixed huge bobo with tagobject.
#    - Suggested by jwvanderbeck
#
# 2.3.6e
#  - Reduced overhead on heartbeats.
#
# 2.3.6d
#  - Worked on TMI errors.
#
# 2.3.6c
#  - Fixed yet another bug with champions.
#    - Reported by Mikey-Flies
#
# 2.3.6b
#  - Fixed champion bug. - Reported by Hotcarl44
#
# 2.3.6a
#  - Added GetIsPC back into TagObject (taken out to test).
#
# 2.3.6
#  - Fixed crash bug.
#  - Modified code to reduce CPU usage.
#
# 2.3.5h
#  - Fixed bug with PI. - Reported by der_drakken
#
# 2.3.5g
#  - Modified main loop so that it always checks the waypoint
#    and not just between timers.
#
# 2.3.5f
#  - Fixed bug with counting dead creatures.
#  - Added RT to reset timer. - Requested by SolarflareX
#
# 2.3.5e
#  - Added PA which will only spawn if a player is in
#    the area.
#  - Modified rolldice function. - Requested by Mikey-Flies
#  - Modified possible bug in object tagging.
#    - Reported by Hotcarl44
#  - Modified RandomWalk to include IsInConversation
#    since a conversation was not showing up as an
#    an action. - Reported by FalloutBoy
#
# 2.3.5d
#  - Added PU for use with jwvanderbeck's Item Manager.
#    When used the creature has a percent chance to pick
#    up any items within 5 meters.
#  - Modified CU function.
#
# 2.3.5c
#  - Modified RW function.
#  - Modified dead creature tagging for compatability
#    with jwvanderbeck's Item Manager
#
# 2.3.5b
#  - Worked on a bug with To Many Instructions
#
# 2.3.5a
#  - Fixed bug with destroying creatures when CC is used.
#    - Reported by Ghost_SD & Hotcarl44
#
# 2.3.5
#  - Added CR which is used with CH and EX. If used it will
#    only spawn the Champion at the remote location.
#  - Added int for KillSpawn for use with controlling the
#    spawn from an outside source.
#
# 2.3.4
#  - Fixed CPU issue with custom Random Walk.
#    - Reported by Damn Near Everyone
#  - Added AI switch for use with jwvanderbeck's Item Manager
#
# 2.3.3
#  - Added IG for Item Group. Requested by Mikey-Flies
#  - Added chance roll to RD for creatures spawning.
#    - Requested by Ghost_SD
#  - Added EX for Remote Spawning. - Requested by Kraxis
#  - Fixed CPU usage with default RW.
#
# 2.3.2
#  - Modified RW to use the same format as CI and CH (1d2)
#    - Suggested by Tovrin
#  - Modified DT and NT to kill creatures no matter what, if
#    a X is used on the switch. - Requested by Visike
#  - Modified RD to kill creatures no matter what, if a X
#    is used on the switch. - Requested by Malkavia
#  - Modified RD to NOT spawn if a player is within a radius
#    and a N is used. - Requested by Gandalf il Bianco
#  - Added PK for use with jwvanderbeck's Item Manager. If
#    set, creatures will only leave loot if killed by a
#    player.
#
# 2.3.1
#  - Fixed bug with Champion not spawning with Groups.
#    - Reported by Qobra
#  - Fixed a bug with Group tag having to be correct case.
#    - Reported by Carey Durbin
#  - Fixed a bug with Spawn Hour and Spawn Day not working
#    if stop time was less than start time.
#    - Reported by InfiniteLoop
#
# 2.3
#  - Changed name to NWN Spawn System.
#
# 2.2.3b
#  - Fixed Group Random random generator.
#    - Reported by Mikey-Flies
#  - Replaced sp_RandomLocation function with one written by
#    Mikey-Flies.
#
# 2.2.3a
#  - Modified RW to include optinal Radius into which to
#    walk to a random spot.
#  - Added AN for use with Random Walk so that when they
#    get to their destination they have a chance on
#    performing an animation.
#  - Modified RH so that the creatures do not by default
#    face the waypoint direction. Use RHF to get that effect.
#    - Requested by Gandalf il Bianco
#  - Modified RF to give a chance for facing a random
#    location every heartbeat. - Requested by jwvanderbeck
#
# 2.2.3
#  - Added further support for jwvanderbeck's Item Manager
#
# 2.2.2f
#  - Fixed a bug with groups spawning badgers the first time.
#    - Reported by mmealman
#  - Modified RH to have all creatures face waypoint direction
#    upon returning. - Requested by mmealman
#
# 2.2.2e
#  - Added PI switch which tells the spawner to spawn or not
#    to spawn if a player has an item in the items list.
#    - Requested by Karthal
#  - Added check for if creature is dead for compatability
#    with Item Manager - Requested by jwvanderbeck
#  - Modified RW to give a 50/50 chance of being used per
#    heartbeat to lessen CPU usage if none of the following
#    are true:
#     -Creature is already walking.
#     -Creature is in combat.
#     -Creature is in a conversation.
#    The old way would do it if none of the above were true 100%
#    of the time.
#
# 2.2.2d
#  - Added CC switch for use with jwvanderbeck's Item Manager.
#  - Added RH, which will instruct the creature to return to
#    it's (Home) waypoint if it is not busy. -
#    - Requested by Gandalf il Bianco
#  - Added CI, which will create an item on the creature spawned.
#    - Requested by Gandalf il Bianco
#
# 2.2.2c
#  - Fixed a bug with Group Random. - Reported by mmealman
#
# 2.2.2b
#  - Fixed bug with Traps. - Reported by keet
#
# 2.2.2
#  - Modified minor functions to lessen CPU usage.
#  - Finished code structure support for Champions and Area Effects
#
# 2.2.1b
#  - Added RF feature which tells the creature to face a
#    random direction after spawning. - Requested by jwvanderbeck
#  - Added AE which is used to add an Area Effect to a location.
#    - Requested by Master_of_Magic
#
# 2.2.1a
#  - Added random spawn for Champions
#
# 2.2.1
#  - Fixed bug with deleting creatures spawned using random
#    option. - Reported by jwvanderbeck
#  - Added support for Champion Spawns using CHnn.
#
# 2.2
#  - Added DS for delaying spawn until are creatures are killed.
#    - Suggested by Michael L. Hoehne
#  - Added SH and SD for spawning only at a certain hour(s) of
#    the day or on a certain day(s) of the month.
#    - Suggested by MokahTGS
#  - Added TP for trap effects. - Suggested by Moose_Dude
#  - Added CP for spawning camp items.
#
# 2.1.9e
#  - Modified a few functions to use less CPU.
#  - Changed how the spawn points keep track of objects
#    due to a new feature being implemented in 2.2
#
# 2.1.9d
#  - Fixed bug with default orc group. - Reported by Qobra
#  - Changed GP to spawn whole group and added GR for using
#    random creatures from the group.
#
# 2.1.9a - c
#  - Modified some code structure to lessen load on CPU.
#  - Fixed bug with RD. - Reported by Qobra
#  - Added GT function to generate treasure on creatures.
#  - Added default Orc group (Group02).
#
# 2.1.9
#  - More code cleanup :)
#  - Added GP for use with spawning groups.
#  - Added default Undead group (Group01).
#
# 2.1.8
#  - Major code changes to reduce CPU usage. Also made
#    modifications that will be used by version 2.2
#
# 2.1.7b
#  - Fixed bug with Random when it was only 2 max :)
#    - Reported by Karthal
#  - Added S switch for SXnn. When used it spawns each
#    create seperate. It will use a delay if specified
#    with TM, but it will not work with random number
#    of creatures.
#    Examples:
#     SX05S      - Will spawn 5 creatures, one at a time.
#     SX05S_TM10 - Will spawn 5 creatures, one at a time
#                  and with a 10 second delay.
#
# 2.1.7a
#  - Fixed a bug with delay not working with radius.
#    - Reported by SebastianCain
#
# 2.1.7
#  - Added NP, which when used with SR, spawns in random
#    locations around the nearest player.
#    - Suggested by Karthal
#  - Added NF, which tells the spawned to not spawn the first
#    batch bypassing the timers. - Suggested by Karthal
#  - Added IC, which when used with RD, tells the spawner
#    not to spawn when a player enters the radius and
#    is already in combat. - Suggested by Karthal
#  - Added SS, which stops the spawn after all creatures
#    are killed. - Suggested by -Mange-
#
# 2.1.6a
#  - Modified RW so that every check it will have the
#    creature random walk if it is not in combat or
#    in a conversation. - Suggested by Qobra
#
# 2.1.6
#  - Modified random counter. - Reported by Dhoff
#  - Added Hours for delay. - Requested by multiple people
#  - Added Effect for when spawner kills creatures.
#  - Added RW (RandomWalk)
#
# 2.1.5b
#  - Fixed a bug with MIN not counting correctly.
#    - Reported by EVERYONE :)
#
# 2.1.5a
#  - Removed where it took 1 off the total of spawns when
#    used with the random option. - Reported by stefulia
#
# 2.1.5
#  - Added effects for spawning.
#
# 2.1.4b
#  - Fixed a problem with WP_ tag where it would chop off
#    the last 2 numbers. - Reported by Noggy
#
# 2.1.4a
#  - Added Spawn Radius (SR), which will spawn in a random
#    location within specified radius. This is now
#    seperate from RD. - Requested by TooKool
#
# 2.1.4
#  - Added ability to spawn any Placeable object. This
#    implemented in order to have it spawn treasure
#    chests, but could be used for any Placeable.
#  - Added random number of creature spawns. (SXnnRyy)
#    Examples:
#     SX02      - Keep 2 creatures spawned.
#     SX01R10   - Spawn a random amount between 1 and 10.
#    Requested by stefulia
#  - Fixed timer bug where minimum would stay seconds
#    even if Minutes was specified. - Reported by jRaskell
#  - Modified timer so that it is not used when a player
#    first enters an area IF a Radius is set. This means
#    that if you have a timer set for 20 minutes, there
#    will always be a creature when the player FIRST
#    enters the Radius. Timer takes effect AFTER they
#    enter the area. - Requested by someone??
#
# 2.1.3
#  - Creatures will now spawn at once the first time
#    it is a VALID time to spawn. This means that
#    when you first load the module, any spawn point
#    that is valid will spawn without a delay the first
#    time. If there is a delay, it will delay each time
#    thereafter. This was requested by multiple people :)
#  - Added Random Spawn Points (RDnnX) within a radius.
#    Example:
#     RD10  = Check if PC is within 10 meters and spawn
#             creatures at spawn point.
#     RD10X = Check if PC is within 10 meters and spawn
#             creatures at random places within that 10
#             meter area.
#    **THE X WAS REMOVED 2.1.5**
# 2.1.2
#  - Added fix for POST_ so that creatures can return
#    back to spawn point. - Reported by stefulia
#  - Added Random option for timer - Suggested by stefulia
#
# 2.1.1
#  - Modified code structure to account for future changes.
#  - Changed how it spawns and how it checks for creatures
#    to account for creatures it spawns. Meaning it will
#    only CHECK for creatures it spawns and it will only
#    kill creatures it spawns :)
#
# 2.1 (The real 2.0)
#  - Rewrote code
#  - Changed DY to DT - Suggested by Iceyflame
#  - Added an optional M for TMnn so you can specify Minutes
#    Example:
#      TM05M = 5 minutes
#      TM05  = 5 seconds
#  - Creatures who only spawn during the day/night will now not
#    be killed by the spawner if they are in combat and the time
#    of day changes.
#  - Changed Radius so that if one is specified and there are
#    no players left in area after a spawn, creatures are killed.
#
# 2.0
#  - Added RD switch (Radius) - Suggested by Iceyflame
#
# 1.2
#  - Added DY & NT switches (Day or Night) - Suggested by Iceyflame
#  - Fixed a bug with it taking of NW_ - Reported by Spike-
#  - Fixed other stuff
#
# 1.1
#  - Added SX (Spawn multiple)
#  - Added TM (Timer)
#
##
#
# Switches
#
# SP_
# Tells the Spawn Control Object that this Waypoint is a Spawn Point.
#
# CHnnRxxx
# Tells the spawner to spawn Champion nn at the spawn point. If R is
# specified and a valid roll is used, it will give the Champion a
# random chance of appearing.
#
# Examples
#
# CH01     - Always spawn Champion 01 when all the creatures are dead.
# CH01R1d4 - The Champion has a 1 in 4 chance of spawning when all the
#            creatures are dead.
# CH01R2d8 - The Champion has a 1 in 16 chance of spawning when all
#            the creatures are dead.
#
#
# Valid rolls are:
#
# nd2
# nd3
# nd4
# nd6
# nd8
# nd10
# nd12
# nd20
# nd100
#
#
# You can find the list around line 268 and it looks like:
#
# SetLocalString(OBJECT_SELF, "champ01", "NW_ORCWIZB");
#
# So to add a Deer Champion you would make a new line:
#
# SetLocalString(OBJECT_SELF, "champ02", "NW_DEER");
#
# SS
# Stops Spawn when all creatures are killed at spawn point.
# NOTE: ALL creatures must be killed. The spawn point will continue
# to spawn until it finds no creatures at the spawn point.
#
# RDnn
# Only spawn creatures if a Player is within "nn" meters.
#
# IC
# Do not spawn when a player enters the radius and is already in combat.
#
# SXnnRyy
# Keep "nn" creatures spawned at this Spawn Point. If R is specified,
# it spawns a random number of creatures between nn and yy.
#
# Examples
#
# SX02      - Keep 2 creatures spawned.
# SX01R10   - Spawn a random amount between 1 and 10.
# NOTE: nn is the MINIMUM you want. If you specify 01 as the minimum,
# it will not spawn anymore until they are ALL dead.
#
#
# CPnn
# Spawn a creature camp.
#
# Example
#
# CP01 - Will place the objects listed in camp01 in random locations
#        around the site.
#
#
# Camp items can be added by including them in the Camp List. The list
# can be found around line 262 of the code and look like:
#
# SetLocalString(OBJECT_SELF, "camp01_tag_1",    "FlyCladCorpseWagon");
# SetLocalString(OBJECT_SELF, "camp01_resref_1", "plc_fcrpsewagon");
#
# To add a second object to Group 01 you would first place the object in
# your area and get the Tag and Blueprint off the object, then delete it.
# Now create a new entry like:
#
# SetLocalString(OBJECT_SELF, "camp01_tag_2",    "MyObjectsTag");
# SetLocalString(OBJECT_SELF, "camp01_resref_2", "myobjectsblueprint");
#
# Replace MyObjectsTag with the tag of your object. Replace the blueprint
# as well.
#
# GP
# Spawn a group of creatures using the default group templates. These can
# be customized if you so desire :) NOTE: The SX tag is not used when
# spawning groups without the GR switch.
#
# Example
#
# Name: SP_GP
# Tag : Group01
#
#
# The Group List can be found around line 243 and looks like:
#
# SetLocalString(OBJECT_SELF, "group01_1", "NW_SKELETON");
# SetLocalString(OBJECT_SELF, "group01_2", "NW_LICH003");
#
# To add new creatures to the groups, just use their Tag. You can
# add as many groups and creatures as you like :)
#
# TR
# Spawns any valid Placeable (used for treasure chests). Please read
# the section in the orginal post for this release on how to make a
# treasure chest if you are not familiar with creating treasure chests.
#
#
#
# -=Item Related Switches=-
#
#
# CI
# Creates an item on creatures spawned.
#
# Examples
#
# CI01 - Creates Item 01 from the Item List and places it on all creatures.
# CI01R1d2 - Each creature has a 50/50 chance of receiving the item.
#
#
# The CI switch uses the same random call as the Champion Spawner. Please
# see list in the description of the Champion switch.
#
# PI
# Tells the spawner to spawn or not to spawn based on whether the player
# has an item. PI must be used with RD.
#
# Examples
#
# PI01  - Player must have Item 01 in their inventory in order for this
#         spawn site to spawn.
# PI01X - Player must NOT have Item 01 in their inventory in order for
#         this spawn site to spawn.
#
#
# GTnn
# Generate treasure. If specified it will generate treasure on the creatures
# that are spawned. This uses the GenerateTreasure function found in the
# default NW_O2_CONINCLUDE file.
#
# Valid types are
#
# GT01 - Generate low treasure.
# GT02 - Generate medium treasure.
# GT03 - Generate high treasure.
# GT04 - Generate boss treasure.
# GT05 - Generate book treasure.
#
#
#
#
# -=Random Switches=-
#
#
# SRnn
# Spawn creatures in a random location within "nn" meters.
#
# NP
# When used with SR, it spawns in random locations near the closest player
# to the spawn point.
#
# GR
# Spawn a random creature from the specified group. When used with the SX
# switch it will spawn up to "nn" random creatures from the specified
# group.
#
# Example
#
# Name: SP_SX03_GP_GR
# Tag : Group01
#
#
#
#
# -=Time Switches=-
#
#
# NT
# Only spawn at night time and kill any creatures left when it turns day.
#
# DT
# Only spawn during the day and kill any creatures left when it turns night.
#
# SHnnTyy
# Spawn only at nn hour of the day. This is in 24 hour format. If T is
# specified it will spawn between the hours of nn and yy.
#
# Examples
#
# SH23    - Spawn only while it is 11pm.
# SH00    - Spawn only while it is midnight.
# SH01    - Spawn only while it is 1am.
# SH01T03 - Spawn only between 1am and 3am.
#
#
# SDnnTyy
# Spawn only on nn day of the month. If T is specified it will only spawn
# between the days of nn and yy.
#
# Examples
#
# SD01    - Spawn only the first day of the month.
# SD01T03 - Spawn only between the first and third days of the month.
#
#
# TMxxRyy
# Delay spawns by "xx" seconds. Specify Minutes by adding the M on the
# end or specify Hours by adding a H. Use R to generate a random number
# between xx and yy.
#
# Examples
#
# TM02     - Delay spawn for 2 seconds.
# TM02M    - Delay spawn for 2 minutes.
# TM02H    - Delay spawn for 2 hours.
# TM10R30  - Random between 10 and 30 seconds.
# TM10R30M - Random between 10 and 30 minutes.
# TM10R30H - Random between 10 and 30 hours.
#
#
# NF
# When used with the timers it tells the spawn system to NOT bypass the timers
# on the very first spawn.
#
# DS
# Wait until all creatures are killed before spawning any more.
#
#
#
# -=Action Switches=-
#
#
# RHF
# RH tells all creatures to return home (to the waypoint) if they are
# not in the middle of something. If an F is used, the creature will
# face the direction of the waypoint when it returns.
#
# RWnn
# If specified, the creature will walk randomly around after spawning.
#
# Examples
#
# RW     - Defaults to a 5 meter radius. The creature will choose a location
#          within the radius and walk to it.
# RW10   - Walk within a 10 meter radius of the spawn point.
#
#
# ANnnLyy
# Used with RW, this switch gives the creature a 50% chance to play an
# animation when they are not walking. Using L will have it loop the
# animation for yy seconds.
#
# Examples
#
# AN01    - The creature will play animation 01.
# AN15L10 - The creature will loop animation 15 for 10 seconds.
#
#
# A list of animations can be found around line 331 of the script.
#
# RF
# This switch gives the creature a 50% chance of facing a random
# direction every heartbeat.
#
#
#
# -=Effect Switches=-
#
#
# AEnnRyy
# This will produce the Area Effect nn throughout a 5 meter radius
# of the spawn point. If R is used it will make the effect throughout
# the radius yy. The location is random.
#
# Examples
#
# AE01    - Will produce Area Effect 01 throughout a 5 meter radius of
#           the spawn point.
# AE01R10 - Will produce Area Effect 01 throughout a 10 meter radius.
#
#
# Effects can be found around line 1215 of the script. This is work in
# progress and I have not had a chance to go through all of the
# effects in NWN :)
#
# VSnn
# Tells the spawner to use effect nn on the location before spawning.
#
# Valid effects are
#
# 01 - VFX_FNF_SUMMON_MONSTER_1
# 02 - VFX_FNF_SUMMON_MONSTER_2
# 03 - VFX_FNF_SUMMON_MONSTER_3
# 04 - VFX_FNF_SUMMON_UNDEAD
# 05 - VFX_FNF_SUNBEAM
# 06 - VFX_FNF_WAIL_O_BANSHEES
# 07 - VFX_FNF_STORM
# 08 - VFX_FNF_STRIKE_HOLY
# 09 - VFX_FNF_IMPLOSION
#
#
# VKnn
# Same as above, except it happens when the spawner kills a creature due
# to it not being a valid spawn time. Example would be if it turned night
# and the creature is suppose to only spawn during the day :)
#
# Effects for spawning can be found around line 677 of the script, effects
# for killing can be found around line 565.
#
# TPnnAyy
# Use trap nn at the spawn point. This works with creature spawns. If A
# is specified and an amulet is listed in the amulet section, whoever is
# wearing it will not be affected.
#
# Examples
#
# TP01    - Use trap 01.
# TP01A01 - Amulet 01 protects against the trap.
# TP01A02 - Amulet 02 protects against the trap.
# Traps are "Damage Effects" with a visual effect applied to the target.
#
#
# Traps can be found around line 1173 and Amulets around line 1193.
#
# If a tag of effect is used for the waypoint, it will only do the effect.
#
#
#
# -=3rd-Party Switches=-
#
#
# Jaga's Area Transition Script
#
# DZ
# Tells the spawner not to zone any creatures spawned from this location.
#
# jwvanderbeck's Item Manager
#
# CC
# This switch is to use creature corpses for loot rather than generic
# loot bags upon creature death.
#
# CU
# Used to determine whether to clean up items on the ground around the
# spawn point.
#
#
# -End of Switch List-
#
##*/
#include "NW_O2_CONINCLUDE"
//#include "NWNSS_Groups"
int iDebug = 0;
void sp_Main(object oNearest);
void sp_DestroyObject(object oNearest, string sTagType);
void sp_CheckVars(object oNearest, string sCurrentName);
void sp_Spawn(object oNearest, string sTemplate, int iSpawnX);
void sp_TagObject(object oNearest, string sTagType, object oNewObject);
void sp_SpawnCamp(object oNearest, location lCurrentWP);
void sp_CheckTrap(object oNearest, object oPC);
void sp_AreaEffect(object oNearest);
void sp_CreateItem(object oNearest, object oLastSpawned);
void sp_PerformActions(object oNearest, object oCurrent);
void sp_GetDiceValue(object oNearest, string sCurrentName, string sNumDice, string sDice);
void sp_GetSpawnSites();
void sp_Debug(string sString);
void sp_CreateObject(object oNearest, string sBluePrint, int iToSpawn);
string sp_CheckSpawnSite(object oNearest);
string sp_SpawnChamp(object oNearest);
string sp_CleanString(object oNearest, string sType);
string sp_GetGroupObject(object oNearest, int iGroupTotal, string sTagType, object oLastSpawned=OBJECT_SELF);
int sp_GetValue(object oNearest, string sSwitch, string sCurrentName);
int sp_CreaturesToSpawn(object oNearest, int iAlive);
int sp_CheckDistance(object oNearest);
int sp_CheckTimeOfDay(object oNearest);
int sp_CheckObjects(object oNearest);
int sp_RollDice(int iNumDice, int iDice, int ReturnValue=FALSE);
int sp_GetIsPCInArea(object oArea, object oNearest);
int sp_GetGroupTotal(object oNearest, string sTagType);
int sp_CheckPlayerInventory(object oNearest, object oPC);
int sp_CheckRules(int iDontSpawn, int iAlive, object oNearest);
int sp_CheckLevel(object oNearest, object oPC);
int sp_PlayerView(object oCreature);
location sp_RandomLocation(object oNearest, int iRandArea);
void sp_ModInit();
void sp_ModInit()
{
/*
# Groups
#
# Undead 1
*/
SetLocalString(OBJECT_SELF, "group01_1", "NW_SKELETON");
SetLocalString(OBJECT_SELF, "group01_2", "NW_GHOUL");
SetLocalString(OBJECT_SELF, "group01_3", "NW_MUMMY");
SetLocalString(OBJECT_SELF, "group01_4", "NW_WRAITH");
//SetLocalString(OBJECT_SELF, "group01_5", "NW_LICH003");
/*
# Orcs 1
*/
SetLocalString(OBJECT_SELF, "group02_1", "NW_ORCA");
SetLocalString(OBJECT_SELF, "group02_2", "NW_ORCB");
SetLocalString(OBJECT_SELF, "group02_3", "NW_OrcChiefA");
SetLocalString(OBJECT_SELF, "group02_4", "NW_ORCCHIEFB");
SetLocalString(OBJECT_SELF, "group02_5", "NW_ORCWIZA");
SetLocalString(OBJECT_SELF, "group02_6", "NW_ORCWIZB");
/*
# Camp Equipment
#
# Corpse Wagon
*/
//SetLocalString(OBJECT_SELF, "camp01_tag_1",    "FlyCladCorpseWagon");
SetLocalString(OBJECT_SELF, "camp01_resref_1", "plc_fcrpsewagon");
/*
# Champions
#
*/
SetLocalString(OBJECT_SELF, "champ01", "NW_ORCWIZB");
/*
# Single Items
#
*/
SetLocalString(OBJECT_SELF, "item01", "NW_IT_BOOK018");
/*
# Group Items
#
*/
SetLocalString(OBJECT_SELF, "item01_1", "NW_IT_BOOK005");
SetLocalString(OBJECT_SELF, "item01_2", "NW_IT_BOOK018");
SetLocalString(OBJECT_SELF, "item01_3", "NW_IT_BOOK022");
/*
# Animations
#
*/
SetLocalInt(OBJECT_SELF, "anim01", ANIMATION_FIREFORGET_BOW);
SetLocalInt(OBJECT_SELF, "anim02", ANIMATION_FIREFORGET_DRINK);
SetLocalInt(OBJECT_SELF, "anim03", ANIMATION_FIREFORGET_GREETING);
SetLocalInt(OBJECT_SELF, "anim04", ANIMATION_FIREFORGET_HEAD_TURN_LEFT);
SetLocalInt(OBJECT_SELF, "anim05", ANIMATION_FIREFORGET_HEAD_TURN_RIGHT);
SetLocalInt(OBJECT_SELF, "anim06", ANIMATION_FIREFORGET_PAUSE_BORED);
SetLocalInt(OBJECT_SELF, "anim07", ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
SetLocalInt(OBJECT_SELF, "anim08", ANIMATION_FIREFORGET_READ);
SetLocalInt(OBJECT_SELF, "anim09", ANIMATION_FIREFORGET_STEAL);
SetLocalInt(OBJECT_SELF, "anim11", ANIMATION_FIREFORGET_TAUNT);
SetLocalInt(OBJECT_SELF, "anim12", ANIMATION_FIREFORGET_VICTORY1);
SetLocalInt(OBJECT_SELF, "anim13", ANIMATION_FIREFORGET_VICTORY2);
SetLocalInt(OBJECT_SELF, "anim14", ANIMATION_FIREFORGET_VICTORY3);
SetLocalInt(OBJECT_SELF, "anim15", ANIMATION_LOOPING_MEDITATE);
/*
#
##*/
SetLocalInt(OBJECT_SELF, "ModInit", 1);
}
void main()
{
  // Setup Group, Champion, Item and Camp vars
  //
  if (!GetLocalInt(OBJECT_SELF, "ModInit")) {
    sp_ModInit();
//    RegisterGroups();
  }
  //////////////////////////////////////////////////////////////////////////////
  // Check if spawn sites are set and if not store their locations.
  //
  if (GetLocalObject(OBJECT_SELF, "SpawnSite1") == OBJECT_INVALID) {
    sp_GetSpawnSites();
  }
  //////////////////////////////////////////////////////////////////////////////
  // If spawn sites exist, start the process.
  //
  int nNth = 1;
  object oNearest = GetLocalObject(OBJECT_SELF, "SpawnSite" + IntToString(nNth));
  if (oNearest == OBJECT_INVALID) {
    return;
  }
  while (oNearest != OBJECT_INVALID)
  {
    // Check spawner and see if a timer is already running and if
    // the spawn site has been destroyed or not.
    //
    // Debug
    if (iDebug == 3) {
      sp_Debug(GetName(oNearest));
    }
    if (!GetLocalInt(oNearest, "SpawnDestroyed")) {
      ActionDoCommand(sp_Main(oNearest));
    }
    nNth++;
    oNearest = GetLocalObject(OBJECT_SELF, "SpawnSite" + IntToString(nNth));
  }
}
// No comments beyond this point :)
void sp_DestroyObject(object oNearest, string sTagType)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_DestroyObject");
  }
  SetLocalInt(oNearest, "DestroyedBySpawner", 1);
  // Debug
  if (iDebug == 1) {
    sp_Debug("Killing Creatures...");
  }
  int iCurObject;
  object oCreatureToKill;
  int iNumObjects;
  if (sTagType == "Camp") {
    iNumObjects = sp_GetGroupTotal(oNearest, "Camp");
  }
  else {
    iNumObjects = GetLocalInt(oNearest, "SpawnAmount") +
                  GetLocalInt(oNearest, "SpawnRandomNumber");
  }
  for (iCurObject = iNumObjects; iCurObject > 0; iCurObject--) {
    oCreatureToKill = GetLocalObject(oNearest, sTagType +
                                               IntToString(iCurObject));
    if (oCreatureToKill != OBJECT_INVALID) {
      if (GetLocalObject(oCreatureToKill, "SpawnedBy") == oNearest) {
        int iKillObjects;
        if (GetLocalInt(oNearest, "PlayerView")) {
          if (sp_PlayerView(oCreatureToKill) != 1) {
            iKillObjects = 1;
          }
        }
        else {
          iKillObjects = 1;
        }
        if ((!GetIsInCombat(oCreatureToKill) ||
             sTagType == "Camp" ||
             GetLocalInt(oNearest, "KillCreatures")) && iKillObjects) {
          // Debug
          if (iDebug == 1) {
            sp_Debug("Killing " + sTagType);
            sp_Debug("Tag - " + GetTag(oCreatureToKill));
          }
          if (GetLocalInt(oNearest, "DeathEffect") && sTagType == "Creature") {
            effect eDeath;
            switch (GetLocalInt(oNearest, "DeathEffect")) {
              case 01: eDeath = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1); break;
              case 02: eDeath = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_2); break;
              case 03: eDeath = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3); break;
              case 04: eDeath = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD); break;
              case 05: eDeath = EffectVisualEffect(VFX_FNF_SUNBEAM); break;
              case 06: eDeath = EffectVisualEffect(VFX_FNF_WAIL_O_BANSHEES); break;
              case 07: eDeath = EffectVisualEffect(VFX_FNF_STORM); break;
              case 08: eDeath = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); break;
              case 09: eDeath = EffectVisualEffect(VFX_FNF_IMPLOSION); break;
                default: eDeath = EffectVisualEffect(VFX_NONE); break;
            }
            ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eDeath,
                                  GetLocation(oCreatureToKill));
          }
          if (GetLocalInt(oNearest, "CORPSE_PERSIST")) {
            AssignCommand(oCreatureToKill, SetIsDestroyable(TRUE, FALSE, FALSE));
          }
          DestroyObject(oCreatureToKill, 0.0);
        }
        else if (GetLocalInt(oNearest, "IsTreasure") && iKillObjects) {
          DestroyObject(oCreatureToKill, 0.0);
        }
      }
    }
  }
}
void sp_CheckVars(object oNearest, string sCurrentName)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckVars");
  }
  // Debug
  if (iDebug == 1) {
    sp_Debug("Checking Variables...");
  }
  SetLocalInt(oNearest, "ID",              sp_GetValue(oNearest, "ID", sCurrentName));
  SetLocalInt(oNearest, "CreatureGroupRandom", sp_GetValue(oNearest, "GR", sCurrentName));
  SetLocalInt(oNearest, "GroupSpawn",      sp_GetValue(oNearest, "GP", sCurrentName));
  SetLocalInt(oNearest, "SpawnTimer",      sp_GetValue(oNearest, "TM", sCurrentName));
  SetLocalInt(oNearest, "SpawnAmount",     sp_GetValue(oNearest, "SX", sCurrentName));
  SetLocalInt(oNearest, "PC_Radius",       sp_GetValue(oNearest, "RD", sCurrentName));
  SetLocalInt(oNearest, "NPC_Radius",      sp_GetValue(oNearest, "SR", sCurrentName));
  SetLocalInt(oNearest, "SpawnDay",        sp_GetValue(oNearest, "DT", sCurrentName));
  SetLocalInt(oNearest, "SpawnNight",      sp_GetValue(oNearest, "NT", sCurrentName));
  SetLocalInt(oNearest, "IsTreasure",      sp_GetValue(oNearest, "TR", sCurrentName));
  SetLocalInt(oNearest, "SpawnEffect",     sp_GetValue(oNearest, "VS", sCurrentName));
  SetLocalInt(oNearest, "DeathEffect",     sp_GetValue(oNearest, "VK", sCurrentName));
  SetLocalInt(oNearest, "RandomWalk",      sp_GetValue(oNearest, "RW", sCurrentName));
  SetLocalInt(oNearest, "NearPlayer",      sp_GetValue(oNearest, "NP", sCurrentName));
  SetLocalInt(oNearest, "InCombat",        sp_GetValue(oNearest, "IC", sCurrentName));
  SetLocalInt(oNearest, "StopSpawn",       sp_GetValue(oNearest, "SS", sCurrentName));
  SetLocalInt(oNearest, "GenTreasure",     sp_GetValue(oNearest, "GT", sCurrentName));
  SetLocalInt(oNearest, "DelaySpawn",      sp_GetValue(oNearest, "DS", sCurrentName));
  SetLocalInt(oNearest, "Camp",            sp_GetValue(oNearest, "CP", sCurrentName));
  SetLocalInt(oNearest, "DaySpawn",        sp_GetValue(oNearest, "SD", sCurrentName));
  SetLocalInt(oNearest, "HourSpawn",       sp_GetValue(oNearest, "SH", sCurrentName));
  SetLocalInt(oNearest, "TrapType",        sp_GetValue(oNearest, "TP", sCurrentName));
  SetLocalInt(oNearest, "ChampSpawn",      sp_GetValue(oNearest, "CH", sCurrentName));
  SetLocalInt(oNearest, "ChampRemote",     sp_GetValue(oNearest, "CR", sCurrentName));
  SetLocalInt(oNearest, "RandomFacing",    sp_GetValue(oNearest, "RF", sCurrentName));
  SetLocalInt(oNearest, "AreaEffect",      sp_GetValue(oNearest, "AE", sCurrentName));
  SetLocalInt(oNearest, "ReturnHome",      sp_GetValue(oNearest, "RH", sCurrentName));
  SetLocalInt(oNearest, "CreateItem",      sp_GetValue(oNearest, "CI", sCurrentName));
  SetLocalInt(oNearest, "ItemGroup",       sp_GetValue(oNearest, "IG", sCurrentName));
  SetLocalInt(oNearest, "PlayerInventory", sp_GetValue(oNearest, "PI", sCurrentName));
  SetLocalInt(oNearest, "PlayAnim",        sp_GetValue(oNearest, "AN", sCurrentName));
  SetLocalInt(oNearest, "SpawnRemote",     sp_GetValue(oNearest, "EX", sCurrentName));
  SetLocalInt(oNearest, "PCInArea",        sp_GetValue(oNearest, "PA", sCurrentName));
  SetLocalInt(oNearest, "ResetTimer",      sp_GetValue(oNearest, "RT", sCurrentName));
  SetLocalInt(oNearest, "StartLevel",      sp_GetValue(oNearest, "SL", sCurrentName));
  SetLocalInt(oNearest, "MaxLevel",        sp_GetValue(oNearest, "ML", sCurrentName));
  SetLocalInt(oNearest, "PlayerView",      sp_GetValue(oNearest, "PV", sCurrentName));
  SetLocalInt(oNearest, "TrapDisarm",      sp_GetValue(oNearest, "TD", sCurrentName));
// Start - Companion Script Switches
  SetLocalInt(oNearest, "CORPSE_PERSIST",  sp_GetValue(oNearest, "CC", sCurrentName));
  SetLocalInt(oNearest, "PickupItems",     sp_GetValue(oNearest, "PU", sCurrentName));
  SetLocalInt(oNearest, "DontZone",        sp_GetValue(oNearest, "DZ", sCurrentName));
  SetLocalInt(oNearest, "PlayerKilled",    sp_GetValue(oNearest, "PK", sCurrentName));
  SetLocalInt(oNearest, "AutoEquip",       sp_GetValue(oNearest, "AI", sCurrentName));
// End
  SetLocalInt(oNearest, "NoFirst",         sp_GetValue(oNearest, "NF", sCurrentName));
  if (GetLocalInt(oNearest, "NoFirst")) {
    SetLocalInt(oNearest, "HasSpawned", 1);
  }
  SetLocalInt(oNearest, "IsSpawner",   1);

  //////////////////////////////////////////////////////////////////////////////
  // If any actions are specified, set a var
  if (GetLocalInt(oNearest, "RandomWalk")   ||
      GetLocalInt(oNearest, "ReturnHome")   ||
      GetLocalInt(oNearest, "RandomFacing") ||
      GetLocalInt(oNearest, "PlayAnim")) {
    SetLocalInt(oNearest, "DoActions", 1);
  }
}
void sp_Spawn(object oNearest, string sTemplate, int iSpawnX)
{

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_Spawn");
  }
  if (!GetLocalInt(oNearest, "IsValid")) {
    // Debug
    if (iDebug == 1)
      sp_Debug("No longer valid. Stopping spawn.");
    SetLocalInt(oNearest, "Spawning",
                GetLocalInt(oNearest, "Spawning") - iSpawnX);
    return;
  }
  // Debug
  if (iDebug == 1) {
    sp_Debug("Spawning...");
  }
  location lCurrentWP = GetLocation(oNearest);
  if (GetLocalInt(oNearest, "SpawnChamp")) {
    sTemplate = GetStringLowerCase(GetLocalString(OBJECT_SELF, sTemplate));
    SetLocalInt(oNearest, "ChampHasSpawned", 1);
    DeleteLocalInt(oNearest, "SpawnChamp");
    // Debug
    if (iDebug == 2) {
      sp_Debug("Spawning Champion " + sTemplate);
    }
  }
  int iGroupTotal;
  int iValid;
  int iTotalX;
  for (iTotalX = 0; iTotalX < iSpawnX; iTotalX++) {
    iValid = 1;
    if (GetLocalInt(oNearest, "GroupSpawn") && !GetLocalInt(oNearest, "ChampHasSpawned")) {
      iGroupTotal = sp_GetGroupTotal(oNearest, "Creature");
      sTemplate = GetStringLowerCase(sp_GetGroupObject(oNearest, iGroupTotal, "Creature"));
      // Debug
      if (iDebug == 2) {
        sp_Debug("Spawning Group Creature " + sTemplate);
      }
    }
    if (GetLocalInt(oNearest, "NPC_Radius") && iValid) {
      if (GetLocalInt(oNearest, "NearPlayer")) {
        object oPlayer = GetNearestCreature(
                            CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC,
                            oNearest);
        if (oPlayer != OBJECT_INVALID) {
          lCurrentWP = sp_RandomLocation(oPlayer,
                                         GetLocalInt(oNearest, "NPC_Radius"));
        }
        else {
          iValid = 0;
          // Debug
          if (iDebug == 1) {
            sp_Debug("Player is not valid...");
          }
        }
      }
      else {
        lCurrentWP = sp_RandomLocation(oNearest, GetLocalInt(oNearest,
                                                             "NPC_Radius"));
      }
    }
    if (GetLocalInt(oNearest, "SpawnRemote")) {
      int iSpawnRemote;
      if (GetLocalInt(oNearest, "ChampRemote")) {
        if (GetLocalInt(oNearest, "ChampHasSpawned")) {
          iSpawnRemote = 1;
        }
      }
      else {
          iSpawnRemote = 1;
      }
      if (iSpawnRemote) {
        lCurrentWP = GetLocation(GetLocalObject(OBJECT_SELF, "RemoteSite" + IntToString(GetLocalInt(oNearest, "SpawnRemote"))));
      }
    }
    object oNewObject;
    if (GetLocalInt(oNearest, "IsTreasure") && iValid) {
      oNewObject = CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lCurrentWP, TRUE);
      SetLocalInt(oNearest, "Spawning",
                  GetLocalInt(oNearest, "Spawning") - 1);
      sp_TagObject(oNearest, "Item", oNewObject);
    }
    else {
      if (GetLocalInt(oNearest, "SpawnEffect") && iValid) {
        effect eSpawn;
        switch (GetLocalInt(oNearest, "SpawnEffect")) {
          case 01: eSpawn = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1); break;
          case 02: eSpawn = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_2); break;
          case 03: eSpawn = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3); break;
          case 04: eSpawn = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD); break;
          case 05: eSpawn = EffectVisualEffect(VFX_FNF_SUNBEAM); break;
          case 06: eSpawn = EffectVisualEffect(VFX_FNF_WAIL_O_BANSHEES); break;
          case 07: eSpawn = EffectVisualEffect(VFX_FNF_STORM); break;
          case 08: eSpawn = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); break;
          case 09: eSpawn = EffectVisualEffect(VFX_FNF_IMPLOSION); break;
          default: eSpawn = EffectVisualEffect(VFX_NONE); break;
        }
        ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eSpawn, lCurrentWP);
      }
      oNewObject = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCurrentWP, TRUE);
      SetLocalInt(oNearest, "Spawning",
                  GetLocalInt(oNearest, "Spawning") - 1);
      sp_TagObject(oNearest, "Creature", oNewObject);
    }
    if (GetLocalInt(oNearest, "Camp") && !GetLocalInt(oNearest, "CampExists")) {
      sp_SpawnCamp(oNearest, lCurrentWP);
    }
  }
}
string sp_CleanString(object oNearest, string sType)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CleanString");
  }
  // Debug
  else if (iDebug == 1) {
    sp_Debug("Cleaning String...");
  }
  string sCurrentTag = GetTag(oNearest);
  if (GetStringLowerCase(GetStringLeft(sCurrentTag, 3)) == "wp_" && sType == "Tag") {
    sCurrentTag = GetStringRight(sCurrentTag, GetStringLength(sCurrentTag) - 3);
    if (StringToInt(GetStringRight(sCurrentTag, 2)) &&
        GetSubString(sCurrentTag, GetStringLength(sCurrentTag) - 3, 1) == "_") {
      sCurrentTag = GetStringLeft(sCurrentTag, GetStringLength(sCurrentTag) - 3);
    }
  }
  else if (GetStringLowerCase(GetStringLeft(sCurrentTag, 3)) == "wn_" && sType == "Tag") {
    sCurrentTag = GetStringRight(sCurrentTag, GetStringLength(sCurrentTag) - 3);
    if (StringToInt(GetStringRight(sCurrentTag, 2)) &&
        GetSubString(sCurrentTag, GetStringLength(sCurrentTag) - 3, 1) == "_") {
      sCurrentTag = GetStringLeft(sCurrentTag, GetStringLength(sCurrentTag) - 3);
    }
  }
  else if (GetStringLowerCase(
           GetStringLeft(sCurrentTag, 5)) == "post_" && sType == "Tag") {
    sCurrentTag = GetStringRight(sCurrentTag, GetStringLength(sCurrentTag) - 5);
  }
  else if (GetStringLowerCase(
           GetStringLeft(sCurrentTag, 6)) == "night_" && sType == "Tag") {
    sCurrentTag = GetStringRight(sCurrentTag, GetStringLength(sCurrentTag) - 5);
  }
  else if (sType == "Blueprint") {
    sCurrentTag = GetLocalString(oNearest, "Tag");
    sCurrentTag = GetStringLowerCase(sCurrentTag);
  }
  return sCurrentTag;
}
int sp_CheckDistance(object oNearest)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckDistance");
  }
  else if (iDebug == 1) {
    sp_Debug("Checking Distance...");
  }
  int iRD = 0;
  int iSpawnDistance = GetLocalInt(oNearest, "PC_Radius");
  if (iSpawnDistance >= 1) {
    object oArea = GetArea(oNearest);
    object oPC = GetFirstPC();
    if (oPC != OBJECT_INVALID) {
      while (oPC != OBJECT_INVALID) {
        if (GetArea(oPC) == oArea) {
          if (GetDistanceBetween(oNearest, oPC) <= IntToFloat(iSpawnDistance)) {
            if (GetLocalInt(oNearest, "TrapType")) {
              sp_CheckTrap(oNearest, oPC);
            }
            if (GetLocalInt(oNearest, "InCombat") && GetIsInCombat(oPC)) {
              if (GetLocalInt(oNearest, "ChampSpawn") &&
                  GetLocalInt(oNearest, "HasSpawned") &&
                 !GetLocalInt(oNearest, "ChampHasSpawned")) {
                 iRD = 1;
              }
              else {
                iRD = 2;
              }
            }
            else {
              iRD = 1;
            }
            if (GetLocalInt(oNearest, "PlayerInventory")) {
              if (!sp_CheckPlayerInventory(oNearest, oPC)) {
                iRD = 2;
              }
            }
            if (GetLocalInt(oNearest, "CheckLevel")) {
              if (!sp_CheckLevel(oNearest, oPC)) {
                iRD = 2;
              }
            }
          }
        }
        oPC = GetNextPC();
      }
    }
  }
  else {
    iRD = 1;
  }
  if (GetLocalInt(oNearest, "RD_DontSpawn")) {
    if (iRD == 1) {
      iRD = 0;
    }
    else {
      iRD = 1;
    }
  }
  if (iRD == 1 && GetLocalInt(oNearest, "RD_NumDice") &&
     !GetLocalInt(oNearest, "RD_DiceRolled")) {
    iRD = sp_RollDice(GetLocalInt(oNearest, "RD_NumDice"),
                      GetLocalInt(oNearest, "RD_Dice"));
    if (iRD == 1) {
      SetLocalInt(oNearest, "RD_DiceRolled", 1);
    }
  }
  else if (iRD == 0 && GetLocalInt(oNearest, "RD_DiceRolled")) {
    SetLocalInt(oNearest, "RD_DiceRolled", 0);
  }
  return iRD;
}
int sp_CheckTimeOfDay(object oNearest)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckTimeOfDay");
  }
  else if (iDebug == 1) {
    sp_Debug("Checking Time of Day...");
  }
  int iValidTime = 0;
  if (GetIsDay() && GetLocalInt(oNearest, "SpawnDay") == 1) {
    iValidTime = 1;
  }
  else if (GetIsNight() && GetLocalInt(oNearest, "SpawnNight") == 1) {
    iValidTime = 1;
  }
  else if (GetLocalInt(oNearest, "DaySpawn")) {
    int iCalDay = GetCalendarDay();
    int iStartDay = GetLocalInt(oNearest, "DaySpawn");
    if (GetLocalInt(oNearest, "TimeThrough")) {
      int iStopDay = GetLocalInt(oNearest, "TimeThrough");
      if (iCalDay >= iStartDay && iCalDay <= iStopDay) {
        iValidTime = 1;
      }
      else if (iStopDay < iStartDay) {
        if (iCalDay >= iStartDay || iCalDay <= iStopDay) {
          iValidTime = 1;
        }
      }
    }
    else if (iCalDay == iStartDay) {
      iValidTime = 1;
    }
  }
  else if (GetLocalInt(oNearest, "HourSpawn")) {
    int iHour = GetTimeHour();
    int iStartHour = GetLocalInt(oNearest, "HourSpawn");
    if (GetLocalInt(oNearest, "StartMidnight")) {
      iStartHour = 0;
    }
    if (GetLocalInt(oNearest, "TimeThrough")) {
      int iStopHour = GetLocalInt(oNearest, "TimeThrough");
      if (GetLocalInt(oNearest, "ThroughMidnight")) {
        iStopHour = 0;
      }
      if (iHour >= iStartHour && iHour <= iStopHour) {
        iValidTime = 1;
      }
      else if (iStopHour < iStartHour) {
        if (iHour >= iStartHour || iHour <= iStopHour) {
          iValidTime = 1;
        }
      }
    }
    else if (iHour == iStartHour) {
      iValidTime = 1;
    }
  }
  return iValidTime;
}
int sp_CheckObjects(object oNearest)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckObjects");
  }
  else if (iDebug == 1) {
    sp_Debug("Counting Creatures...");
  }
  int iMaxXX = GetLocalInt(oNearest, "SpawnAmount");
  if (GetLocalInt(oNearest, "SpawnRandomNumber") > 0) {
    iMaxXX += GetLocalInt(oNearest, "SpawnRandomNumber");
  }
  if (GetLocalInt(oNearest, "GroupSpawn") && !GetLocalInt(oNearest, "CreatureGroupRandom")) {
    iMaxXX = sp_GetGroupTotal(oNearest, "Creature");
  }
  // Debug
  if (iDebug == 2) {
    sp_Debug("Max creatures is " + IntToString(iMaxXX));
  }
  int iAlive = 0;
  int iCountSX_Dead = 0;
  int iCreature;
  object oCurrent;
  for (iCreature = iMaxXX; iCreature > 0; iCreature--) {
    oCurrent = GetLocalObject(oNearest, "Creature" + IntToString(iCreature));
    if (oCurrent != OBJECT_INVALID && !GetIsDead(oCurrent)) {
      iAlive++;
      if (GetLocalInt(oNearest, "DoActions")) {
        ActionDoCommand(sp_PerformActions(oNearest, oCurrent));
      }
    }
    else if (oCurrent != OBJECT_INVALID && GetIsDead(oCurrent)) {
      DeleteLocalObject(oNearest, "Creature" + IntToString(iCreature));
      int iDeadCreatures = GetLocalInt(oNearest, "DeadCreatures") +1;
      if (iDeadCreatures >= 99) {
        iDeadCreatures = 1;
      }
      SetLocalInt(oNearest, "DeadCreatures", iDeadCreatures);
      SetLocalObject(oNearest, "DeadCreature" + IntToString(iDeadCreatures), oCurrent);
    }
    else if (GetLocalInt(oNearest, "IsTreasure")) {
      oCurrent = GetLocalObject(oNearest, "Item" + IntToString(iCreature));
      if (oCurrent != OBJECT_INVALID && GetTag(oCurrent) != "") {
        iAlive++;
      }
      else {
        DeleteLocalObject(oNearest, "Item" + IntToString(iCreature));
      }
    }
  }
  return iAlive;
}
int sp_CreaturesToSpawn(object oNearest, int iAlive)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CreaturesToSpawn");
  }
  else if (iDebug == 1) {
    sp_Debug("Determining Number To Spawn...");
  }
  int iSpawnRandom = GetLocalInt(oNearest, "SpawnRandomNumber");
  int iMin = GetLocalInt(oNearest, "SpawnAmount");
  int iMaxXX = iMin;
  int iCountSX_Dead;
  if (iSpawnRandom > 0) {
    iMaxXX += iSpawnRandom;
  }
  if (GetLocalInt(oNearest, "GroupSpawn") && !GetLocalInt(oNearest, "CreatureGroupRandom")) {
    iMaxXX = sp_GetGroupTotal(oNearest, "Creature");
    return iMaxXX - iAlive;
  }
  iCountSX_Dead = iMaxXX - iAlive;
  if (iCountSX_Dead > 0 && iSpawnRandom > 0) {
    int iAlive = iMaxXX - iCountSX_Dead;
    if (iAlive < iMin) {
      int iRand = iMaxXX - iAlive;
      iCountSX_Dead = Random(iRand)+1;
    }
    else {
      iCountSX_Dead = 0;
    }
  }
  if (GetLocalInt(oNearest, "SpawnSingle") && iCountSX_Dead > 1) {
    return 1;
  }
  else {
    return iCountSX_Dead;
  }
}
int sp_GetValue(object oNearest, string sSwitch, string sCurrentName)
{
  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetValue");
  }
  else if (iDebug == 1) {
    sp_Debug("Getting Variable Values...");
  }
  int iSwitchValue = 0;
  int iIsMinutes = 0;
  int iIsHours = 0;
  if (FindSubString(sCurrentName, sSwitch) > 0) {
    if (sSwitch == "TR" || // Treasure
        sSwitch == "NP" || // Spawn near player
        sSwitch == "NF" || // Skip the timer during module loading
        sSwitch == "IC" || // Do not spawn if in combat
        sSwitch == "SS" || // Stop spawn after all are dead
        sSwitch == "GP" || // Spawn a group
        sSwitch == "GR" || // Spawn random from group
        sSwitch == "RF" || // Face random direction
        sSwitch == "CR" || // Spawn champ remotely
        sSwitch == "PA" || // Spawn only if player is in the area
        sSwitch == "RT" || // Reset timer
        sSwitch == "PV" || // Player in view
        // Third party switches
        sSwitch == "DZ" ||
        sSwitch == "PK" ||
        sSwitch == "CC" ||
        sSwitch == "AI" ||
        sSwitch == "DS") {
      iSwitchValue = 1;
    }
    else {
      int iNumberPos = FindSubString(sCurrentName, sSwitch);
      if (sSwitch == "TM") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "M") {
          iIsMinutes = 1;
        }
        else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "H") {
          iIsHours = 1;
        }
      }
      else if (sSwitch == "SX") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "S") {
          SetLocalInt(oNearest, "SpawnSingle", 1);
        }
      }
      int iNumberLength = GetStringLength(sCurrentName) - iNumberPos;
      string sSwitchValue = GetSubString(sCurrentName, (iNumberPos + 2), 2);
      iSwitchValue = StringToInt(sSwitchValue);
      if (iIsMinutes == 1) {
        iSwitchValue = iSwitchValue * 60;
      }
      else if (iIsHours == 1) {
        iSwitchValue = (iSwitchValue * 60) * 60;
      }
      if ((sSwitch == "SD" || sSwitch == "SH")) {
        if (iSwitchValue == 0) {
          iSwitchValue = 1;
          SetLocalInt(oNearest, "StartMidnight", 1);
        }
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "T") {

          int iTime = StringToInt(GetSubString(sCurrentName, (iNumberPos + 5), 2));
          if (iTime == 0) {
            iTime = 1;
            SetLocalInt(oNearest, "ThroughMidnight", 1);
          }
          SetLocalInt(oNearest, "TimeThrough", iTime);

        }

      }
      else if (sSwitch == "TP" && GetSubString(sCurrentName,
                                         (iNumberPos + 4), 1) == "A") {
        SetLocalInt(oNearest, "TrapAmulet", StringToInt(GetSubString(
                                   sCurrentName, (iNumberPos + 5), 2)));
      }
      else if (sSwitch == "CH") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "CH_NumDice", "CH_Dice");
        }
      }
      else if (sSwitch == "CI") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "CI_NumDice", "CI_Dice");
        }
      }
      else if (sSwitch == "GT") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "GT_NumDice", "GT_Dice");
        }
      }
      else if (sSwitch == "IG") {
        iSwitchValue = 1;
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "IG_NumDice", "IG_Dice");
        }
      }
      else if (sSwitch == "PI") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "X") {
          SetLocalInt(oNearest, "InInventory", 1);
        }
        else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "D") {
          SetLocalInt(oNearest, "DestroyItem", 1);
        }
      }
      else if (sSwitch == "TD") {
        iSwitchValue = 1;
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "TD_NumDice", "TD_Dice");
        }
      }
      else if (sSwitch == "RD") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "X") {
          SetLocalInt(oNearest, "KillCreatures", 1);
        }
        else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "N") {
          SetLocalInt(oNearest, "RD_DontSpawn", 1);
        }
        else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "RD_NumDice", "RD_Dice");
        }
      }
      else if (sSwitch == "NT") {
        iSwitchValue = 1;
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "X") {
          SetLocalInt(oNearest, "KillCreatures", 1);
        }
      }
      else if (sSwitch == "DT") {
        iSwitchValue = 1;
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "X") {
          SetLocalInt(oNearest, "KillCreatures", 1);
        }
      }
      else if (sSwitch == "ML" || sSwitch == "SL") {
        SetLocalInt(oNearest, "CheckLevel", 1);
      }
      else if (sSwitch == "RW") {
        if (iSwitchValue < 1) {
            iSwitchValue = 1;
        }
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength);
          sp_GetDiceValue(oNearest, sStringToParse, "RW_NumDice", "RW_Dice");
        }
      }
      else if (sSwitch == "AN") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "L") {
          SetLocalInt(oNearest, "AnimLoop", StringToInt(GetSubString(
                                   sCurrentName, (iNumberPos + 5), 2)));
        }
      }
      else if (sSwitch == "RH") {
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "F") {
          SetLocalInt(oNearest, "AboutFace", 1);
        }
        iSwitchValue = 1;
      }
      else if (sSwitch == "ID") {
        SetLocalObject(GetModule(),
                      "Spawner" + IntToString(iSwitchValue), oNearest);
      }
      else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
        int iMaxRand = StringToInt(GetSubString(
                                   sCurrentName, (iNumberPos + 5), 2));
        if (sSwitch == "SX") {
          if (iSwitchValue < 1) {
              iSwitchValue = 1;
          }
          else if (iMaxRand < 1) {
            iMaxRand = 1;
          }
          SetLocalInt(oNearest, "SpawnRandomNumber", iMaxRand - iSwitchValue);
        }
        else if (sSwitch == "AE") {
          SetLocalInt(oNearest, "AreaEffectRadius", iMaxRand);
        }
        else {
          if (GetSubString(sCurrentName, (iNumberPos + 7), 1) == "M") {
            iIsMinutes = 1;
            iMaxRand = iMaxRand * 60;
            iSwitchValue = iSwitchValue * 60;
          }
          else if (GetSubString(sCurrentName, (iNumberPos + 7), 1) == "H") {
            iMaxRand = (iMaxRand * 60) * 60;
            iSwitchValue = (iSwitchValue * 60) * 60;
          }
          SetLocalInt(oNearest, "TimerIsRandom", iMaxRand - iSwitchValue);
        }
      }
    }
  }
  else if (sSwitch == "SX") {
    if (GetLocalInt(oNearest, "GroupSpawn") &&
        !GetLocalInt(oNearest, "CreatureGroupRandom")) {
      iSwitchValue = sp_GetGroupTotal(oNearest, "Creature");
    }
    else {
      iSwitchValue = 1;
    }
  }
  return iSwitchValue;
}
int sp_GetGroupTotal(object oNearest, string sTagType) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetGroupTotal");
  }
  else if (iDebug == 1) {
    sp_Debug("Checking group total...");
  }
  int iGroupValid = 0;
  int iGroupTotal = 1;
  string sTemplate;
  if (sTagType == "Creature") {
    // Debug
    if (iDebug == 1) {
      sp_Debug("Checking group Creature...");
    }
    sTemplate = GetStringLowerCase(GetLocalString(oNearest, "Tag"));
  }
  else if (sTagType == "Camp") {
    sTemplate = "camp";
    // Debug
    if (iDebug == 1) {
      sp_Debug("Checking group Camp...");
    }
    if (GetLocalInt(oNearest, "Camp") < 10) {
      sTemplate += "0" + IntToString(GetLocalInt(oNearest, "Camp"));
    }
    else {
      sTemplate += IntToString(GetLocalInt(oNearest, "Camp"));
    }
  }
  else if (sTagType == "Item") {
    sTemplate = "item";
    // Debug
    if (iDebug == 1) {
      sp_Debug("Checking group Item...");
    }
    if (GetLocalInt(oNearest, "CreateItem") < 10) {
      sTemplate += "0" + IntToString(GetLocalInt(oNearest, "CreateItem"));
    }
    else {
      sTemplate += IntToString(GetLocalInt(oNearest, "CreateItem"));
    }
  }
  string sGroup;
  while (iGroupValid != 1) {
    if (sTagType == "Creature") {
      sGroup = GetStringLowerCase(sTemplate) + "_" + IntToString(iGroupTotal);
    }
    if (sTagType == "Item") {
      sGroup = GetStringLowerCase(sTemplate) + "_" + IntToString(iGroupTotal);
    }
    else if (sTagType == "Camp") {
      sGroup = GetStringLowerCase(sTemplate) + "_resref_" +
                                  IntToString(iGroupTotal);
    }
    if (GetLocalString(OBJECT_SELF, sGroup) == "") {
      iGroupValid = 1;
      iGroupTotal--;
    }
    else {
      iGroupTotal++;
    }
  }
  return iGroupTotal;
}
string sp_SpawnChamp(object oNearest) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_SpawnChamp");
  }
  int iChampValid;
  int iChampNumDie = GetLocalInt(oNearest, "CH_NumDice");
  int iChampDie = GetLocalInt(oNearest, "CH_Dice");
  int iDieResult;
  if (!iChampNumDie) {
    iChampValid = 1;
  }
  else {
    iChampValid = sp_RollDice(iChampNumDie, iChampDie);
  }
  string sChamp;
  if (iChampValid) {
    SetLocalInt(oNearest, "SpawnChamp", 1);
    int iChampNum = GetLocalInt(oNearest, "ChampSpawn");
    sChamp = "champ";
    if (iChampNum >= 10) {
      sChamp += IntToString(iChampNum);
    }
    else {
      sChamp += "0" + IntToString(iChampNum);
    }
  }
  return sChamp;
}
string sp_GetGroupObject(object oNearest, int iGroupTotal, string sTagType, object oLastSpawned=OBJECT_SELF) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetGroupObjectt");
  }
  int iSpawnRand;
  string sGroup;
  string sCurrentTag;
  string sTemplate;
  // Check for creature group random
  if (GetLocalInt(oNearest, sTagType + "GroupRandom")) {
    iSpawnRand = Random(iGroupTotal)+1;
    if (sTagType == "Creature") {
      sGroup = GetStringLowerCase(GetLocalString(oNearest, "Tag")) +
                      "_" + IntToString(iSpawnRand);
    }
    sCurrentTag = GetLocalString(OBJECT_SELF, sGroup);
    return sCurrentTag;
  }
  else if (GetLocalInt(oNearest, "IG_Dice")) {
    int iIGDice = GetLocalInt(oNearest, "IG_Dice");
    int iIGNumDice = GetLocalInt(oNearest, "IG_NumDice");
    int iGroupItem = sp_RollDice(iIGNumDice, iIGDice, TRUE);
    sGroup = "item";
    if (GetLocalInt(oNearest, "CreateItem") < 10) {
      sGroup += "0";
    }
    sGroup += IntToString(GetLocalInt(oNearest, "CreateItem")) +
                    "_" + IntToString(iGroupItem);
    sCurrentTag = GetLocalString(OBJECT_SELF, sGroup);
    return sCurrentTag;
  }
  // Not random so get the whole group
  else {
    object oCurrent;
    int iCreature;
    int iGroup;
    for (iGroup = iGroupTotal; iGroup > 0; iGroup--) {
      if (sTagType == "Creature") {
        sGroup = GetStringLowerCase(GetLocalString(oNearest, "Tag")) +
                        "_" + IntToString(iGroup);
      }
      else if (sTagType == "Item") {
        sGroup = "item";
        if (GetLocalInt(oNearest, "CreateItem") < 10) {
          sGroup += "0";
        }
        sGroup += IntToString(GetLocalInt(oNearest, "CreateItem")) +
                        "_" + IntToString(iGroup);
      }
      sCurrentTag = GetLocalString(OBJECT_SELF, sGroup);
      int iValid = 0;
      if (sTagType == "Creature") {
        for (iCreature = iGroupTotal; iCreature >= 1; iCreature--) {
          oCurrent = GetLocalObject(oNearest, sTagType +
                                    IntToString(iCreature));
          if (oCurrent != OBJECT_INVALID) {
            if (sCurrentTag == GetTag(oCurrent)) {
              iValid = 1;
            }
          }
        }
      }
      else if (sTagType == "Item") {
        object oCurItem = GetFirstItemInInventory(oLastSpawned);
        if (sCurrentTag == GetTag(oCurItem)) {
          iValid = 1;
        }
        else {
          if (oCurItem != OBJECT_INVALID) {
            while (oCurItem != OBJECT_INVALID && iValid != 1) {
              oCurItem = GetNextItemInInventory(oLastSpawned);
              if (sCurrentTag == GetTag(oCurItem)) {
                iValid = 1;
              }
            }
          }
        }
      }
      if (iValid != 1) {
        return sCurrentTag;
      }
    }
  }
  if (sTemplate == "") {
    // Debug
    if (iDebug == 1) {
      sp_Debug("Template is blank");
    }
  }
  else if (iDebug == 1) { // Debug
    sp_Debug("Template - " + sTemplate);
  }
  return sCurrentTag;
}
void sp_TagObject(object oNearest, string sTagType, object oNewObject) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_TagObject");
  }
  int iCreature;
  object oCreature;
  object oLastSpawned = oNewObject;
  SetLocalObject(oLastSpawned, "SpawnedBy", oNearest);
  iCreature = 1;
  oCreature = GetLocalObject(oNearest, sTagType +
                             IntToString(iCreature));
  while (oCreature != OBJECT_INVALID) {
    iCreature++;
    oCreature = GetLocalObject(oNearest, sTagType +
                               IntToString(iCreature));
  }
  SetLocalObject(oNearest, sTagType +
                 IntToString(iCreature),
                 oLastSpawned);
  if (GetLocalInt(oNearest, "CORPSE_PERSIST") && sTagType == "Creature") {
    SetLocalInt(oLastSpawned, "CORPSE_PERSIST", 1);
    AssignCommand(oLastSpawned, SetIsDestroyable(FALSE, FALSE, FALSE));
  }
  if (GetLocalInt(oNearest, "GenTreasure") && sTagType == "Creature") {
    int iGTNumDie = GetLocalInt(oNearest, "CH_NumDice");
    int iGTDice = GetLocalInt(oNearest, "CH_Dice");
    int iGTValid;
    if (!iGTNumDie) {
      iGTValid = 1;
    }
    else {
      iGTValid = sp_RollDice(iGTNumDie, iGTDice);
    }
    if (iGTValid) {
      switch (GetLocalInt(oNearest, "GenTreasure")) {
        case 01:GenerateLowTreasure(oLastSpawned);break;
        case 02:GenerateMediumTreasure(oLastSpawned);break;
        case 03:GenerateHighTreasure(oLastSpawned);break;
        case 04:GenerateBossTreasure(oLastSpawned);break;
        case 05:GenerateBookTreasure(oLastSpawned);break;
      }
    }
  }
  if (GetLocalInt(oNearest, "CreateItem")) {
    sp_CreateItem(oNearest, oLastSpawned);
  }
  if (sTagType == "Item" && GetLocalInt(oNearest, "TrapDisarm")) {
    if (GetIsTrapped(oLastSpawned) && GetTrapDisarmable(oLastSpawned)) {
      int iTrapNumDice = GetLocalInt(oNearest, "TD_NumDice");
      int iTrapDice = GetLocalInt(oNearest, "TD_Dice");
      int iTrapValid = sp_RollDice(iTrapNumDice, iTrapDice);
      if (iTrapValid) {
        SetTrapDisabled(oLastSpawned);
      }
    }
  }
  // Debug
  if (iDebug == 1) {
    sp_Debug("Owner of " + GetTag(oLastSpawned) +
                        " is " +
                        GetName(GetLocalObject(
                                oLastSpawned, "SpawnedBy")));
  }
}
void sp_SpawnCamp(object oNearest, location lCurrentWP) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_SpawnCamp");
  }
  location lMyLoc;
  location lObjectLoc;
  int iCampEquip = 0;
  int iItem = 1;
  int iCamp = GetLocalInt(oNearest, "Camp");
  string sCamp;
  if (iCamp < 10) {
    sCamp = "camp0" + IntToString(iCamp);
  }
  else {
    sCamp = "camp" + IntToString(iCamp);
  }
  while (iCampEquip != 1) {
    lMyLoc = lCurrentWP;
    string sItemResRef = GetLocalString(OBJECT_SELF, sCamp + "_resref_" +
                                        IntToString(iItem));
    if (sItemResRef != "") {
      lObjectLoc = GetLocation(GetNearestObjectToLocation(
                               OBJECT_TYPE_PLACEABLE, lCurrentWP));
      if (GetDistanceBetweenLocations(lMyLoc, lObjectLoc) < 2.0 ) {
        int iLocValid ;
        for (iLocValid = 0; iLocValid != 1; iLocValid++) {
          lMyLoc = sp_RandomLocation(oNearest, 1);
          lObjectLoc = GetLocation(GetNearestObjectToLocation(
                                   OBJECT_TYPE_PLACEABLE, lMyLoc));
          if (GetDistanceBetweenLocations(lMyLoc, lObjectLoc) > 2.0 ) {
            iLocValid = 1;
          }
        }
      }
      object oNewObject = CreateObject(OBJECT_TYPE_PLACEABLE, sItemResRef, lMyLoc, TRUE);
      sp_TagObject(oNearest, "Camp", oNewObject);
      iItem++;
    }
    else {
      iCampEquip = 1;
      SetLocalInt(oNearest, "CampExists", 1);
    }
  }
}
void sp_CheckTrap(object oNearest, object oPC)
{

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckTrap");
  }
  else if (iDebug == 1) {
    sp_Debug("Checking Trap...");
  }
  effect eDamage;
  effect eEffect;
  string sDamageCause;
  string sAmulet = "blank";
  if (oPC != OBJECT_INVALID) {
    // Debug
    if (iDebug == 1) {
      sp_Debug("PC is valid...");
    }
    switch (GetLocalInt(oNearest, "TrapType")) {
      case 01:
        eDamage = EffectDamage(d4(),
                  DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_PLUS_FIVE);
        eEffect = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_NATURE);
        sDamageCause = "You have stumbled into a poisonous fog.";
        break;
      case 02:
        eDamage = EffectDamage(d4(),
                  DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_PLUS_FIVE);
        eEffect = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_EVIL);
        sDamageCause = "You have stumbled into a poisonous fog.";
        break;
      default:
        eDamage = EffectDamage(d4(),
                  DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_PLUS_FIVE);
        eEffect = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_NATURE);
        sDamageCause = "You have stumbled into a poisonous fog.";
        break;
    }
    if (GetLocalInt(oNearest, "TrapAmulet")) {
      // Debug
      if (iDebug == 1) {
        sp_Debug("Checking for Amulet...");
      }
      switch (GetLocalInt(oNearest, "TrapAmulet")) {
        case 01: sAmulet = "PalorsAmulet"; break;
        case 02: sAmulet = "PalorsOtherAmulet"; break;
      }
    }
    if (GetTag(GetItemInSlot(INVENTORY_SLOT_NECK, oPC)) != sAmulet) {
     // Debug
      if (iDebug == 1) {
        sp_Debug("PC does not have Amulet...");
      }
      SendMessageToPC(oPC, sDamageCause);
      ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eEffect,
                            GetLocation(oPC));
      ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oPC);
    }
  }
  else {
    // Debug
    if (iDebug == 1) {
      sp_Debug("PC is not valid...");
    }
  }
}
void sp_AreaEffect(object oNearest)
{

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_AreaEffect");
  }
  else if (iDebug == 1) {
    sp_Debug("Setting Area Effect...");
  }
  int iEffectRadius = GetLocalInt(oNearest, "AreaEffectRadius");
  if (!iEffectRadius) {
    iEffectRadius = 5;
  }
  effect eAreaEffect;
  switch (GetLocalInt(oNearest, "AreaEffect")) {
    case 1: eAreaEffect = EffectVisualEffect(VFX_FNF_SMOKE_PUFF);break;
    case 2: eAreaEffect = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_NATURE);break;
    default: eAreaEffect = EffectVisualEffect(VFX_FNF_SMOKE_PUFF);break;
  }
  location lAreaLoc = sp_RandomLocation(oNearest, iEffectRadius);
  ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eAreaEffect, lAreaLoc);
}
int sp_RollDice(int iNumDice, int iDice, int ReturnValue=FALSE) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_RollDice");
  }
  int iDieResult;
  switch (iDice) {
    case 2:   iDieResult = d2(iNumDice);   break;
    case 3:   iDieResult = d3(iNumDice);   break;
    case 4:   iDieResult = d4(iNumDice);   break;
    case 6:   iDieResult = d6(iNumDice);   break;
    case 8:   iDieResult = d8(iNumDice);   break;
    case 10:  iDieResult = d10(iNumDice);  break;
    case 12:  iDieResult = d12(iNumDice);  break;
    case 20:  iDieResult = d20(iNumDice);  break;
    case 100: iDieResult = d100(iNumDice); break;
    default:;break;
  }
  if (ReturnValue) {
    return iDieResult;
  }
  else if (iDieResult == iNumDice) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}
void sp_GetDiceValue(object oNearest, string sCurrentName, string sNumDice, string sDice) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetDiceValue");
  }
  if (FindSubString(sCurrentName, "_") >= 1) {
    sCurrentName = GetStringLeft(sCurrentName, FindSubString(sCurrentName, "_"));
  }
  while (!StringToInt(GetStringLeft(sCurrentName, 1))) {
    sCurrentName = GetStringRight(sCurrentName, GetStringLength(sCurrentName) - 1);
  }
  if (GetSubString(sCurrentName, 2, 1) == "R") {
    sCurrentName = GetStringRight(sCurrentName, GetStringLength(sCurrentName) - 3);
  }
  SetLocalInt(oNearest, sNumDice,
              StringToInt(GetStringLeft(
                          sCurrentName, FindSubString(sCurrentName, "d"))));
  SetLocalInt(oNearest, sDice,
              StringToInt(GetStringRight(
                          sCurrentName, (GetStringLength(sCurrentName) -
                          FindSubString(sCurrentName, "d")) - 1)));
}
int sp_CheckPlayerInventory(object oNearest, object oPC) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckPlayerInventory");
  }
  int iInInventory = GetLocalInt(oNearest, "InInventory");
  int iPlayerHas;
  int iSpawnValid;
  int iItem = GetLocalInt(oNearest, "PlayerInventory");
  string sItem = "item";
  if (iItem < 10) {
    sItem += "0" + IntToString(iItem);
  }
  else {
    sItem += IntToString(iItem);
  }
  string sItemTag = GetLocalString(OBJECT_SELF, sItem);

  object oItem = GetFirstItemInInventory(oPC);
  while (oItem != OBJECT_INVALID && iPlayerHas != 1) {
    if (GetTag(oItem) == sItemTag) {
      iPlayerHas = 1;
      if (GetLocalInt(oNearest, "DestroyItem")) {
        DestroyObject(oItem);
      }
    }
    oItem = GetNextItemInInventory(oPC);
  }
  if (iInInventory && !iPlayerHas) {
    iSpawnValid = 1;
  }
  else if (!iInInventory && iPlayerHas) {
    iSpawnValid = 1;
  }

  return iSpawnValid;
}
location sp_RandomLocation(object oNearest, int iRandArea)
{

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_RandomLocation");
  }
  else if (iDebug == 1) {
    sp_Debug("Choosing Random Location...");
  }
  vector vNewPos = GetPosition(oNearest);
  object oArea = GetArea(oNearest);
  float fX, fY;
  float fMaxY;
  float fRadius = IntToFloat(iRandArea);
  fX = (Random(200)/100.0 - 1.0) * fRadius;
  fMaxY=sqrt(fRadius*fRadius-fX*fX);
  fY = (Random(200)/100.0 - 1.0) * fMaxY;
  vNewPos += Vector(fX, fY, 0.0);
  location lNewLoc = Location(oArea, vNewPos, VectorToAngle(-1.0 * vNewPos));
  return lNewLoc;
}
void sp_PerformActions(object oNearest, object oCurrent) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_PerformActions");
  }
  if (GetLocalInt(oNearest, "RandomWalk") &&
      GetCurrentAction(oCurrent) == 65535 &&
     !IsInConversation(oCurrent)) {
    int iWalkRandom;
    int iNumDice = GetLocalInt(oNearest, "RW_NumDice");
    int iDice = GetLocalInt(oNearest, "RW_Dice");
    if (!iDice) {
      iWalkRandom = 1;
    }
    else {
      iWalkRandom = 0;
      iWalkRandom = sp_RollDice(iNumDice, iDice);
    }
    if (iWalkRandom) {
      int iRandWalk = GetLocalInt(oNearest, "RandomWalk");
      object oCurLoc;
      if (GetLocalInt(oNearest, "SpawnRemote")) {
        oCurLoc = GetLocalObject(OBJECT_SELF, "RemoteSite" + IntToString(GetLocalInt(oNearest, "SpawnRemote")));
      }
      else {
          oCurLoc = oNearest;
      }
      if (iRandWalk > 1 && GetDistanceToObject(oCurLoc) > IntToFloat(iRandWalk)) {
        location lRandWalk = sp_RandomLocation(oCurLoc, iRandWalk);
        AssignCommand(oCurrent, ActionMoveToLocation(lRandWalk));
      }
      else {
        AssignCommand(oCurrent, ActionRandomWalk());
      }
    }
  }
  if (GetLocalInt(oNearest, "PlayAnim")) {
    if (d2() == 1) {
      int iAnim = GetLocalInt(oNearest, "PlayAnim");
      string sAnim = "anim";
      if (iAnim >= 10) {
        sAnim += IntToString(iAnim);
      }
      else {
        sAnim += "0" + IntToString(iAnim);
      }
      int iAnimToPlay = GetLocalInt(OBJECT_SELF, sAnim);
      float fAnimLoop = 1.0;
      if (GetLocalInt(oNearest, "AnimLoop")) {
        fAnimLoop = IntToFloat(GetLocalInt(oNearest, "AnimLoop"));
      }
      AssignCommand(oCurrent, PlayAnimation(iAnimToPlay, 1.0, fAnimLoop));
    }
  }
  if (GetLocalInt(oNearest, "ReturnHome") &&
      GetCurrentAction(oCurrent) == 65535) {
    if (!GetIsInCombat(oCurrent) && !IsInConversation(oCurrent)) {
      if (GetDistanceBetween(oNearest, oCurrent) > 5.0) {
        AssignCommand(oCurrent, ActionMoveToObject(oNearest));
      }
      else if (GetFacing(oNearest) != GetFacing(oCurrent) &&
               GetLocalInt(oNearest, "AboutFace")) {
        AssignCommand(oCurrent, SetFacing(GetFacing(oNearest)));
      }
    }
  }
  if (GetLocalInt(oNearest, "RandomFacing") &&
      GetCurrentAction(oCurrent) == 65535 && d2() == 1) {
    int iRandomDir = Random(270);
    AssignCommand(oCurrent, SetFacing(IntToFloat(iRandomDir)));
  }
}
int sp_GetIsPCInArea(object oArea, object oNearest) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetIsPCInArea");
  }
  int iPCInArea;
  object oPC = GetFirstPC();
  if (oPC != OBJECT_INVALID) {
    while (oPC != OBJECT_INVALID && !iPCInArea) {
      if (GetArea(oPC) == oArea) {
        if (GetLocalInt(oNearest, "CheckLevel")) {
          if (!sp_CheckLevel(oNearest, oPC)) {
            iPCInArea = 1;
          }
        }
        else {
          iPCInArea = 1;
        }
      }
      oPC = GetNextPC();
    }
  }
  return iPCInArea;
}
int sp_CheckRules(int iDontSpawn, int iAlive, object oNearest) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckRules");
  }
  else if (iDebug == 2) {
    sp_Debug("Found " + IntToString(iAlive) + " alive.");
  }
  //
  // Check for time of day.
  // Check how many creatures are at the spawn point.
  //
  int iIsValid = 1;
  //
  // Check for time of day.
  //
  if (GetLocalInt(oNearest, "SpawnDay") ||
      GetLocalInt(oNearest, "SpawnNight") ||
      GetLocalInt(oNearest, "DaySpawn") ||
      GetLocalInt(oNearest, "HourSpawn")) {
    if (sp_CheckTimeOfDay(oNearest) != 1) {
      iIsValid = 0;
      if (iAlive) {
        sp_DestroyObject(oNearest, "Creature");
        if (GetLocalInt(oNearest, "IsTreasure")) {
          sp_DestroyObject(oNearest, "Item");
        }
        if (GetLocalInt(oNearest, "Camp")) {
          sp_DestroyObject(oNearest, "Camp");
          SetLocalInt(oNearest, "CampExists", 0);
        }
      }
    }
  }
  // Check for PC in radius (if a radius is used).
  //
  if (GetLocalInt(oNearest, "PC_Radius")) {
    int iRD = sp_CheckDistance(oNearest);
    if (iRD != 1) {
      iIsValid = 0;
      if (iRD != 1 && iAlive) {
        sp_DestroyObject(oNearest, "Creature");
        if (GetLocalInt(oNearest, "IsTreasure")) {
          sp_DestroyObject(oNearest, "Item");
        }
        if (GetLocalInt(oNearest, "Camp")) {
          sp_DestroyObject(oNearest, "Camp");
          SetLocalInt(oNearest, "CampExists", 0);
        }
      }
    }
  }
  //
  // Check for PC in Area
  if (GetLocalInt(oNearest, "PCInArea")) {
    int iPCInArea = sp_GetIsPCInArea(GetArea(OBJECT_SELF), oNearest);
    if (!iPCInArea) {
      if (GetLocalInt(oNearest, "ResetTimer")) {
        SetLocalInt(oNearest, "HasSpawned", 0);
      }
      iIsValid = 0;
      if (iAlive) {
        sp_DestroyObject(oNearest, "Creature");
        if (GetLocalInt(oNearest, "IsTreasure")) {
          sp_DestroyObject(oNearest, "Item");
        }
        if (GetLocalInt(oNearest, "Camp") && GetLocalInt(oNearest, "CampExists")) {
          sp_DestroyObject(oNearest, "Camp");
          SetLocalInt(oNearest, "CampExists", 0);
        }
      }
    }
  }
  //
  // Check for Area effect
  if (iIsValid && GetLocalInt(oNearest, "AreaEffect")) {
    sp_AreaEffect(oNearest);
  }
  // Check if the spawn is suppose to wait till all
  // creatures are dead.
  //
  int iToSpawn;
  if (GetLocalInt(oNearest, "DelaySpawn") && iAlive >= 1) {
    iIsValid = 0;
  }
  //
  // Check for missing creatures.
  //
  if (!GetLocalInt(oNearest, "SpawnRandomNumber") && !iDontSpawn) {
    if (iAlive == GetLocalInt(oNearest, "SpawnAmount")) {
      iIsValid = 0;
    }
  }
  if (iIsValid && !GetLocalInt(oNearest, "SpawnChamp") && !iDontSpawn &&
      (GetLocalInt(oNearest, "Spawning") < GetLocalInt(oNearest, "SpawnAmount"))) {
    iToSpawn = sp_CreaturesToSpawn(oNearest, iAlive);
    if (iToSpawn == 0) {
      iIsValid = 0;
    }
    else if (iToSpawn == GetLocalInt(oNearest, "SpawnAmount") &&
                         GetLocalInt(oNearest, "HasSpawned") &&
                         GetLocalInt(oNearest, "StopSpawn")) {
      SetLocalInt(oNearest, "SpawnDestroyed", 1);
      iIsValid = 0;
    }
    // Debug
    if (iDebug == 1) {
      sp_Debug("Missing " + IntToString(iToSpawn) +
                          " at " + GetTag(oNearest));
    }
  }
  //
  if (GetLocalInt(oNearest, "KillSpawn")) {
    iIsValid = 0;
    AssignCommand(OBJECT_SELF, sp_DestroyObject(oNearest, "Creature"));
    if (GetLocalInt(oNearest, "IsTreasure")) {
      AssignCommand(OBJECT_SELF, sp_DestroyObject(oNearest, "Item"));
    }
    SetLocalInt(oNearest, "ChampHasSpawned", 0);
    SetLocalInt(oNearest, "SpawnDestroyed", 1);
  }
  //
  // Checking spawn in queue
  if (iToSpawn > GetLocalInt(oNearest, "Spawning")) {
    iToSpawn = iToSpawn - GetLocalInt(oNearest, "Spawning");
    if (iToSpawn <= 0) {
      iIsValid = 0;
      if (GetLocalInt(oNearest, "ToSpawn")) {
        DeleteLocalInt(oNearest, "ToSpawn");
      }
    }
    else {
      SetLocalInt(oNearest, "ToSpawn", iToSpawn);
    }
  }
  else if (!GetLocalInt(oNearest, "Spawning")) {
    iIsValid = 0;
    if (GetLocalInt(oNearest, "ToSpawn")) {
      DeleteLocalInt(oNearest, "ToSpawn");
    }
  }
  return iIsValid;
}
void sp_CreateItem(object oNearest, object oLastSpawned) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CreateItem");
  }
  else if (iDebug == 1) {
    sp_Debug("Checking Item...");
  }
  string sItem = "item";
  int iCreateItem;
  int iDice = GetLocalInt(oNearest, "CI_Dice");
  int iNumDice = GetLocalInt(oNearest, "CI_NumDice");
  int sItemNumber = GetLocalInt(oNearest, "CreateItem");
  if (!GetLocalInt(oNearest, "ItemGroup")) {
    if (sItemNumber >= 10) {
      sItem += IntToString(sItemNumber);
    }
    else {
      sItem += "0" + IntToString(sItemNumber);
    }
    sItem = GetLocalString(OBJECT_SELF, sItem);
  }
  if (!iDice) {
    iCreateItem = 1;
  }
  else {
    iCreateItem = 0;
    iCreateItem = sp_RollDice(iNumDice, iDice);
  }
  if (iCreateItem) {
    // Debug
    if (iDebug == 1)
      sp_Debug("Creating Item " + sItem);
    if (GetLocalInt(oNearest, "ItemGroup") && !GetLocalInt(oNearest, "IG_Dice")) {
      int iTotalItems = sp_GetGroupTotal(oNearest, "Item");
      int iItems;
      for (iItems = 0; iItems < iTotalItems; iItems++) {
        sItem = sp_GetGroupObject(oNearest, iTotalItems, "Item", oLastSpawned);
        CreateItemOnObject(sItem, oLastSpawned);
      }
    }
    else if (GetLocalInt(oNearest, "IG_Dice")) {
      int iTotalItems = sp_GetGroupTotal(oNearest, "Item");
      sItem = sp_GetGroupObject(oNearest, iTotalItems, "Item", oLastSpawned);
      CreateItemOnObject(sItem, oLastSpawned);
    }
    else {
      CreateItemOnObject(sItem, oLastSpawned);
    }
  }
}
void sp_GetSpawnSites() {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_GetSpawnSites");
  }
  object oNearest;
  int iSS;
  int iSpawnSite = 1;
  oNearest = GetFirstObjectInArea(GetArea(OBJECT_SELF));
  while (oNearest != OBJECT_INVALID)
  {
    if (GetObjectType(oNearest) == 32 && !GetLocalInt(oNearest, "SpawnSite")) {
      string sName = GetName(oNearest);
      if (GetStringLeft(sName, 3) == "SP_") {
        SetLocalObject(OBJECT_SELF, "SpawnSite" +
                       IntToString(iSpawnSite), oNearest);
        iSpawnSite++;
        SetLocalInt(oNearest, "SpawnSite", 1);
      }
      else if (GetStringLeft(sName, 3) == "XX_") {
        iSS = StringToInt(GetSubString(sName, FindSubString(sName, "_") + 1, 2));
        SetLocalObject(OBJECT_SELF, "RemoteSite" +
                       IntToString(iSS), oNearest);
        SetLocalInt(oNearest, "SpawnSite", 1);
      }
      else if (GetStringLeft(sName, 2) == "XX") {
        iSS = StringToInt(GetSubString(sName, 3, 2));
        SetLocalObject(OBJECT_SELF, "RemoteSite" +
                       IntToString(iSS), oNearest);
        SetLocalInt(oNearest, "SpawnSite", 1);
      }
    }
    oNearest = GetNextObjectInArea(GetArea(OBJECT_SELF));
  }
}
string sp_CheckSpawnSite(object oNearest) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckSpawnSite");
  }
  if (iDebug == 2) {
    sp_Debug(GetName(oNearest));
  }
  // Get the Tag.
  //
  string sBluePrint;
  if (GetLocalString(oNearest, "Tag") == "") {
    SetLocalString(oNearest, "Tag", sp_CleanString(oNearest, "Tag"));
  }
  // Get the Blueprint.
  //
  if (GetLocalString(oNearest, "Blueprint") == "") {
    SetLocalString(oNearest, "Blueprint",
                   sp_CleanString(oNearest, "Blueprint"));
    sBluePrint = GetLocalString(oNearest, "Blueprint");
    // Debug
    if (iDebug == 1) {
      sp_Debug("Blueprint = " + sBluePrint);
    }
  }
  else {
    sBluePrint = GetLocalString(oNearest, "Blueprint");
  }
  // Check if variables are already set.
  //
  if (!GetLocalInt(oNearest, "IsSpawner")) {
    sp_CheckVars(oNearest, GetName(oNearest));
  }
  // Start of Main Procedure.
  //
  // We will use this to see if we should spawn or not.
  int iDontSpawn;
  if (sBluePrint == "" || sBluePrint == "effect") {
    iDontSpawn = 1;
  }
  int iAlive;
  if (!iDontSpawn) {
    iAlive = sp_CheckObjects(oNearest);
  }
  int iIsValid = sp_CheckRules(iDontSpawn, iAlive, oNearest);
  // Check for Champion
  //
  if (iIsValid && !iDontSpawn && !iAlive &&
      GetLocalInt(oNearest, "ChampSpawn") &&
      GetLocalInt(oNearest, "HasSpawned") &&
     !GetLocalInt(oNearest, "ChampHasSpawned") &&
     !GetLocalInt(oNearest, "DestroyedBySpawner") &&
     !GetLocalInt(oNearest, "Spawning")) {
    string sChamp = sp_SpawnChamp(oNearest);
    if (sChamp != "") {
      SetLocalInt(oNearest, "ToSpawn", 1);
      sBluePrint = sChamp;
    }
  }
  else if (iIsValid && GetLocalInt(oNearest, "ChampHasSpawned")) {
    DeleteLocalInt(oNearest, "ChampHasSpawned");
  }
  // Debug
  if (iDebug == 2) {
    sp_Debug("Blueprint is " + sBluePrint);
  }
  if (iDontSpawn) {
    iIsValid = 0;
    if (GetLocalInt(oNearest, "IsValid")) {
      DeleteLocalInt(oNearest, "IsValid");
    }
  }
  SetLocalInt(oNearest, "IsValid", iIsValid);
  return sBluePrint;
}
void sp_CreateObject(object oNearest, string sBluePrint, int iToSpawn) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CreateObject");
  }
  DeleteLocalInt(oNearest, "DestroyedBySpawner");
  float fSpawnTime;
  SetLocalInt(oNearest, "IsSpawning", 1);
  int iSpawnTime;
  string sSpawnTimer;
  string sTimerRandom;
  int iDoSpawnChamp = GetLocalInt(oNearest, "SpawnChamp");
  if (GetLocalInt(oNearest, "SpawnTimer") && !iDoSpawnChamp) {
    sSpawnTimer = IntToString(GetLocalInt(oNearest, "SpawnTimer"));
  }
  if (GetLocalInt(oNearest, "TimerIsRandom") && !iDoSpawnChamp) {
    sTimerRandom = IntToString(GetLocalInt(oNearest, "TimerIsRandom"));
  }
  if (GetLocalInt(oNearest, "HasSpawned")) {
    // Debug
    if (iDebug == 1) {
      sp_Debug("Timer " + sSpawnTimer +
                          " Random " +
                          sTimerRandom);
    }
    if (GetLocalInt(oNearest, "TimerIsRandom")  && !iDoSpawnChamp) {
      iSpawnTime = StringToInt(sSpawnTimer) +
                               Random(StringToInt(sTimerRandom));
      fSpawnTime = IntToFloat(iSpawnTime);
    }
    else if (!iDoSpawnChamp) {
      fSpawnTime = StringToFloat(sSpawnTimer);
    }
  }
  else {
    SetLocalInt(oNearest, "HasSpawned", 1);
  }
  // Debug
  if (iDebug == 1) {
    sp_Debug("Spawning " + sBluePrint +
                        " delayed for " + IntToString(FloatToInt(fSpawnTime)));
  }
  SetLocalInt(oNearest, "Spawning",
              GetLocalInt(oNearest, "Spawning") + iToSpawn);
  DeleteLocalInt(oNearest, "ToSpawn");
  if (fSpawnTime >= 1.0) {
    AssignCommand(OBJECT_SELF, DelayCommand( fSpawnTime,
                  sp_Spawn(oNearest, sBluePrint, iToSpawn)));
  }
  else {
      sp_Spawn(oNearest, sBluePrint, iToSpawn);
  }
}
int sp_CheckLevel(object oNearest, object oPC) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_CheckLevel");
  }
  int iMinLevel = GetLocalInt(oNearest, "StartLevel");
  int iMaxLevel = GetLocalInt(oNearest, "MaxLevel");
  int iPCLevel1 = GetLevelByPosition(1, oPC);
  int iPCLevel2 = GetLevelByPosition(2, oPC);
  int iPCLevel3 = GetLevelByPosition(3, oPC);
  int iValid;
  if (iMinLevel) {
    if (iPCLevel1 >= iMinLevel ||
        iPCLevel2 >= iMinLevel ||
        iPCLevel3 >= iMinLevel) {
      iValid = 1;
    }
  }
  if (iMaxLevel) {
    if (iMaxLevel >= iPCLevel1 &&
        iMaxLevel >= iPCLevel2 &&
        iMaxLevel >= iPCLevel3) {
      iValid = 1;
    }
  }
  return iValid;
}
int sp_PlayerView(object oCreature) {

  // Debug
  if (iDebug == 3) {
    sp_Debug("sp_PlayerView");
  }
  object oPC = GetFirstPC();
  while (oPC != OBJECT_INVALID) {
    if (GetObjectSeen(oCreature, oPC)) {
      return 1;
    }
    oPC = GetNextPC();
  }
  return 0;
}
void sp_Debug(string sString) {
  SendMessageToPC(GetFirstPC(), sString);
}
void sp_Main(object oNearest) {

  int iIsValid;
  int iToSpawn;
  string sBluePrint = sp_CheckSpawnSite(oNearest);
  if (sBluePrint == "") {
    iIsValid = 0;
    SetLocalInt(oNearest, "IsValid", 0);
  }
  else {
    iIsValid = GetLocalInt(oNearest, "IsValid");
    iToSpawn = GetLocalInt(oNearest, "ToSpawn");
  }
  // If everything is ok, spawn.
  //
  if (iIsValid && iToSpawn) {
    AssignCommand(OBJECT_SELF, sp_CreateObject(oNearest, sBluePrint, iToSpawn));
  }
}

