#include "NW_O2_CONINCLUDE"

/*##
# NWN Spawn System v2.4.4c
# Author: Palor <palor@truefear.net>
# Description:
#  Complete Spawn System, one script.
##*/
//Version Numbers. Do NOT change these
void sp_VersionInfo();
void sp_VersionInfo()
{
  SetLocalInt(GetModule(), "NWNSS_Version", 244);
}
///////////////////////////
// GROUP_INCLUDE
//#include "NWNSS_Groups"
//int iDebug = 0;
int ACS_Version = GetLocalInt(GetModule(), "ACS_Version");
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()
{
        /*______________________
        # Champions
        # ______________________
        */
        SetLocalString(OBJECT_SELF, "champ01", "NW_SKELWARR01");

        /*______________________
        # Items
        # ______________________
        */
        SetLocalString(OBJECT_SELF, "item01", "itemresref");
        /*
        # Groups
        #
        # Undead 1
        */
        SetLocalString(OBJECT_SELF, "group01_1", "skeleton001");
        SetLocalString(OBJECT_SELF, "group01_2", "skeleton001");
        SetLocalString(OBJECT_SELF, "group01_3", "NW_SKELWARR01");
        SetLocalString(OBJECT_SELF, "group01_4", "NW_SKELWARR01");
        SetLocalString(OBJECT_SELF, "group01_5", "NW_WRAITH");
        /*
        # 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");
        /*
        # 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()
{
    if(GetLocalInt(GetArea(OBJECT_SELF), "playerCount"))  // Only run the spanwer if there's someone in the area
    {
//        SpeakString("Spanwer in " + GetName(GetArea(OBJECT_SELF)) + " is ACTIVE", TALKVOLUME_SHOUT);

        // Start the heartbeat
        // Setup Group, Champion, Item and Camp vars
        //
        if (!GetLocalInt(OBJECT_SELF, "ModInit"))
        {
            sp_VersionInfo();
            sp_ModInit();
           // GROUP_INCLUDE
           //    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 for Area Control System
            // 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));
        }
    }
    else
    {
//        SpeakString("Spanwer in " + GetName(GetArea(OBJECT_SELF)) + " is DORMANT", TALKVOLUME_SHOUT);
    }
}
// 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));
          }
          if (GetLocalInt(oNearest, "DespawnRemote")) {
            location lDespawnSite = GetLocation(GetLocalObject(
                                    OBJECT_SELF, "RemoteSite" +
                                    IntToString(GetLocalInt(
                                    oNearest, "DespawnRemote"))));
            if (GetCurrentAction(oCreatureToKill) != 36) {
              AssignCommand(oCreatureToKill, ClearAllActions());
              AssignCommand(oCreatureToKill, ActionMoveToLocation(lDespawnSite));
            }
            if (GetDistanceBetweenLocations(GetLocation(
                                            oCreatureToKill), lDespawnSite) <= 2.0) {
              DestroyObject(oCreatureToKill, 0.0);
            }
          }
          else {
            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, "DespawnRemote",   sp_GetValue(oNearest, "DX", 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));
  SetLocalInt(oNearest, "KillTime",        sp_GetValue(oNearest, "KT", sCurrentName));
  SetLocalInt(oNearest, "ForceAttack",     sp_GetValue(oNearest, "FA", 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));
  SetLocalInt(oNearest, "SitChair",        sp_GetValue(oNearest, "SC", 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, "ForceAttack") ||
      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);
      }
      // !Mod Modified to keep creatures from dropping from the sky
      oNewObject = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCurrentWP, FALSE);
      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;
              }
            }
          }
        }
        if (iRD == 1 && GetLocalInt(oNearest, "ForceAttack") &&
            oPC != OBJECT_INVALID) {
          SetLocalObject(oNearest, "NearestPC", oPC);
        }
        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 = 1;
  //
  // Check Day and Night
  if (GetIsDay() && GetLocalInt(oNearest, "SpawnDay")) {
    iValidTime = 1;
  }
  else if (GetIsNight() && GetLocalInt(oNearest, "SpawnNight")) {
    iValidTime = 1;
  }
  else if (GetLocalInt(oNearest, "SpawnDay") ||
           GetLocalInt(oNearest, "SpawnNight")) {
    iValidTime = 0;
  }
  //
  // Check day specific
  if (GetLocalInt(oNearest, "DaySpawn")) {
    int iCalDay = GetCalendarDay();
    int iStartDay = GetLocalInt(oNearest, "DaySpawn");
    if (GetLocalInt(oNearest, "DayTimeThrough")) {
      int iStopDay = GetLocalInt(oNearest, "DayTimeThrough");
      if (iCalDay < iStartDay && iCalDay > iStopDay) {
        iValidTime = 0;
      }
      else if (iStopDay > iStartDay) {
        if (iCalDay < iStartDay || iCalDay > iStopDay) {
          iValidTime = 0;
        }
      }
    }
    else if (iCalDay != iStartDay) {
      iValidTime = 0;
    }
  }
  //
  // Check hour specific
  if (GetLocalInt(oNearest, "HourSpawn")) {
    int iHour = GetTimeHour();
    int iStartHour = GetLocalInt(oNearest, "HourSpawn");
    if (GetLocalInt(oNearest, "StartMidnight")) {
      iStartHour = 0;
    }
    if (GetLocalInt(oNearest, "HourTimeThrough")) {
      int iStopHour = GetLocalInt(oNearest, "HourTimeThrough");
      if (GetLocalInt(oNearest, "ThroughMidnight")) {
        iStopHour = 0;
      }
      if (iHour < iStartHour && iHour > iStopHour) {
        iValidTime = 0;
      }
      else if (iStopHour > iStartHour) {
        if (iHour < iStartHour || iHour > iStopHour) {
          iValidTime = 0;
        }
      }
    }
    else if (iHour != iStartHour) {
      iValidTime = 0;
    }
  }
  if (GetLocalInt(oNearest, "KillTime")) {
    if (GetLocalString(oNearest, "KillTime") == "Hour") {
      if (GetLocalInt(oNearest, "KillTime") == GetTimeHour()) {
        iValidTime = 0;
      }
    }
    else if (GetLocalString(oNearest, "KillTime") == "Day") {
      if (GetLocalInt(oNearest, "KillTime") == GetCalendarDay()) {
        iValidTime = 0;
      }
    }
  }
  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;
  int iDeadCreatures = 1;
  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")) {
// TEST
//        ActionDoCommand(sp_PerformActions(oNearest, oCurrent));
        sp_PerformActions(oNearest, oCurrent);
      }
    }
    else if (oCurrent != OBJECT_INVALID && GetIsDead(oCurrent)) {
      DeleteLocalObject(oNearest, "Creature" + IntToString(iCreature));
      SetLocalObject(oNearest, "DeadCreature" +
                     IntToString(iDeadCreatures), oCurrent);
      iDeadCreatures++;
    }
    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
        sSwitch == "FA" || // Force Attack
        // 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);
          }
          if (sSwitch == "SD") {
            SetLocalInt(oNearest, "DayTimeThrough", iTime);
          }
          else if (sSwitch == "SH") {
            SetLocalInt(oNearest, "HourTimeThrough", 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") {
          // !MOD changed to parse correctly
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 5);
          sp_GetDiceValue(oNearest, sStringToParse, "CH_NumDice", "CH_Dice");
        }
      }
      else if (sSwitch == "CI") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
          // !MOD changed to parse correctly
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 5);
          sp_GetDiceValue(oNearest, sStringToParse, "CI_NumDice", "CI_Dice");
        }
      }
      else if (sSwitch == "GT") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
         // !MOD changed to parse correctly
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 5);
          sp_GetDiceValue(oNearest, sStringToParse, "GT_NumDice", "GT_Dice");
        }
      }
      else if (sSwitch == "IG") {
        iSwitchValue = 1;
        if (GetSubString(sCurrentName, (iNumberPos + 2), 1) == "R") {
          SetLocalInt(oNearest, "IG_Random", 1);
        }
      }
      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") {
          // !MOD changed to parse correctly
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 3);
          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") {
          // !MOD changed to parse correctly
          string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 5);
          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 + 2), 1) != "_") {
          if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "R") {
            // !MOD changed to parse correctly
            string sStringToParse = GetStringRight(sCurrentName, iNumberLength - 5);
            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 (sSwitch == "KT") {
        if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "H") {
          SetLocalString(oNearest, "KillTime", "Hour");
        }
        else if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "D") {
          SetLocalString(oNearest, "KillTime", "Day");
        }
      }
      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_Random")) {
    int iGroupItem = Random(iGroupTotal) + 1;
    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;
  SetLocalObject(oNewObject, "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),
                 oNewObject);
  if (GetLocalInt(oNearest, "CORPSE_PERSIST") && sTagType == "Creature") {
    SetLocalInt(oNewObject, "CORPSE_PERSIST", 1);
    AssignCommand(oNewObject, 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(oNewObject);break;
        case 02:GenerateMediumTreasure(oNewObject);break;
        case 03:GenerateHighTreasure(oNewObject);break;
        case 04:GenerateBossTreasure(oNewObject);break;
        case 05:GenerateBookTreasure(oNewObject);break;
      }
    }
  }
  if (GetLocalInt(oNearest, "CreateItem")) {
    sp_CreateItem(oNearest, oNewObject);
  }
  if (sTagType == "Item" && GetLocalInt(oNearest, "TrapDisarm")) {
    if (GetIsTrapped(oNewObject) && GetTrapDisarmable(oNewObject)) {
      int iTrapNumDice = GetLocalInt(oNearest, "TD_NumDice");
      int iTrapDice = GetLocalInt(oNearest, "TD_Dice");
      int iTrapValid = sp_RollDice(iTrapNumDice, iTrapDice);
      if (iTrapValid) {
        SetTrapDisabled(oNewObject);
      }
    }
  }
  if (GetLocalInt(oNearest, "ForceAttack")) {
    object oPC = GetLocalObject(oNearest, "NearestPC");
    if (oPC != OBJECT_INVALID) {
      AssignCommand(oNewObject, ClearAllActions());
      AssignCommand(oNewObject, ActionAttack(oPC));
    }
  }
  // Debug
  //if (iDebug == 1) {
  //  sp_Debug("Owner of " + GetTag(oNewObject) +
  //                      " is " +
  //                      GetName(GetLocalObject(
  //                              oNewObject, "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);
}
// !MOD sp_RollDice modified to work correctly
int sp_RollDice(int iNumDice, int iDice, int ReturnValue=FALSE)
{
  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");
  //}  FindSubString
  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"))));


  string s = 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, "ReturnHome") && GetLocalInt(oNearest, "RandomWalk")) {
    if (GetDistanceBetweenLocations(GetLocation(oCurrent),
        GetLocation(GetLocalObject(oCurrent, "SpawnedBy"))) > 3.0) {
      if (d2() == 2) {
        if (GetDistanceBetween(oNearest, oCurrent) > 5.0) {
          AssignCommand(oCurrent, ClearAllActions());
          SetLocalInt(oCurrent, "DoReturnHome", 1);
        }
      }
    }
  }
  int iReturnHome = GetLocalInt(oCurrent, "DoReturnHome");
  if (GetLocalInt(oNearest, "RandomWalk") &&
      GetCurrentAction(oCurrent) == 65535 &&
     !IsInConversation(oCurrent) && !iReturnHome) {
    int iWalkRandom = 1;
    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") && ACS_Version < 220) {
    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 (iReturnHome) {
        AssignCommand(oCurrent, DeleteLocalInt(oCurrent, "DoReturnHome"));
      }
    }
  }
  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") ||
      GetLocalInt(oNearest, "KillTime") ) {
    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_Random")) {
      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_Random")) {
      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));
  }
}