//***************************************************************** // UO Style Spawn System v2.1.5 // Author: Palor <palor@truefear.net> // Description: // This script enables you to place "spawners" that can make // a location keep X number of creatures spawned, with delays // and even just at night or during the day. // // Features: // -Spawn up to 99 creatures. - optional // -Spawn a random amount of creatures between 1 and 99. - optional // -Use a delay of up to 99 minutes between each spawn. - optional // -Use random delay between 0 to 99 minutes. - optional // -Spawn just at night or during the day. - optional // -Spawn only if a PC is within up to 99 meters. - optional // -Spawn creatures in random locations within a specified radius // of the spawn point. - optional // -Spawn treasure chests or any other valid Placeable. // -Use Effects during spawning // //############################################################### // 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: WP_TagOfCreatureYouWantToSpawn // // The above would make a single spawn point which would // always keep one of your creatures spawned there. // //############################################################### // Switches // // Switches are used in the Name of the Spawn Point (Waypoint) // and control how the Spawn Point spawns. // Example: // Name: SP_SX05_TM10_NT_RD05 // // Will keep 5 creatures spawned only at night, only if // a player is within 5 meters and will wait 10 seconds // before spawning and will wait 10 seconds after each // creature is killed before spawning a new one. // // Current Switches: // // SP_ Tells the Spawn Control Object that this Waypoint is // a Spawn Point. // // SRnn Spawn creatures in a random location within "nn" meters. // // RDnn Only spawn creatures if a Player is within "nn" meters. // // 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. // // 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. // // 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. // // TMxxRyyM Delay spawns by "xx" seconds. Specify Minutes by adding // the M on the end. If R is used, it chooses a random // number between xx and yy. // Examples: // TM02 - Delay spawn for 2 seconds. // TM02M - Delay spawn for 2 minutes. // TM10R30 - Random between 10 and 30 seconds. // TM10R30M - Random between 10 and 30 minutes. // // 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 // //############################################################### // Changes // // 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) // //############################################################### int iDebug = 0; void sp_KillCreatures(object oNearest, string sCurrentTag); void sp_CheckVars(object oNearest, string sCurrentName); void sp_Spawn(object oNearest, string sTemplate, int iSpawnX); string sp_CleanString(object oNearest, string sType); int sp_GetValue(object oNearest, string sSwitch, string sCurrentName); int sp_CheckCreatures(object oNearest, string sCurrentTag); int sp_CheckDistance(object oNearest, int iSpawnDistance); int sp_CheckTimeOfDay(object oNearest); location sp_RandomLocation(object oNearest, int iRandArea); void main() { int nNth = 1; object oNearest = GetNearestObject( 32, OBJECT_SELF, nNth ); while (GetIsObjectValid(oNearest) ) { // Check this is a spawner and if a timer is already running. if (!GetLocalInt(oNearest, "IsSpawning") && GetStringLeft(GetName(oNearest), 3) == "SP_") { // Check if variables are already set. if (!GetLocalInt(oNearest, "IsSpawner")) { sp_CheckVars(oNearest, GetName(oNearest)); } // Start of Main Procedure. //////////////////////////////////////////////////////////// // Get the Tag. if (GetLocalString(oNearest, "Tag") == "") { SetLocalString(oNearest, "Tag", sp_CleanString(oNearest, "Tag")); } if (GetLocalString(oNearest, "Blueprint") == "") { SetLocalString(oNearest, "Blueprint", sp_CleanString(oNearest, "Blueprint")); if (iDebug) // Debug message SendMessageToAllDMs("Blueprint = " + GetLocalString(oNearest, "Blueprint")); } // We will use this to see if we should spawn or not. int iIsValid = 1; // Check for time of day. if (sp_CheckTimeOfDay(oNearest) != 1) { if (iDebug) // Debug message SendMessageToAllDMs("Time of day is not valid for " + GetTag(oNearest)); iIsValid = 0; sp_KillCreatures(oNearest, GetLocalString(oNearest, "Tag")); } // Check for missing creatures and kill if Time of Day is wrong. int iToSpawn = sp_CheckCreatures(oNearest, GetLocalString(oNearest, "Tag")); if (iToSpawn == 0) { iIsValid = 0; } else if (iDebug) { SendMessageToAllDMs("Missing " + IntToString(iToSpawn) + " at " + GetTag(oNearest)); } // Check for PC in radius (if a radius is used). if (sp_CheckDistance(oNearest, GetLocalInt(oNearest, "PC_Radius")) != 1) { iIsValid = 0; sp_KillCreatures(oNearest, GetLocalString(oNearest, "Tag")); } // Setup spawn. if (iIsValid) { SetLocalInt(oNearest, "IsSpawning", 1); int iSpawnTime; float fSpawnTime; if (GetLocalInt(oNearest, "HasSpawned")) { if (iDebug) { // Debug message SendMessageToAllDMs("Timer " + IntToString(GetLocalInt(oNearest, "SpawnTimer")) + " Random " + IntToString(GetLocalInt(oNearest, "TimerIsRandom"))); } iSpawnTime = GetLocalInt(oNearest, "SpawnTimer") + Random(GetLocalInt(oNearest, "TimerIsRandom")); fSpawnTime = IntToFloat(iSpawnTime); } else { SetLocalInt(oNearest, "HasSpawned", 1); fSpawnTime = 0.0; } if (GetLocalInt(oNearest, "PC_Entered") == 1) { SetLocalInt(oNearest, "PC_Entered", 2); fSpawnTime = 0.0; } if (iDebug) // Debug message SendMessageToAllDMs("Spawning " + GetLocalString(oNearest, "Blueprint") + " delayed for " + IntToString(iSpawnTime)); AssignCommand(OBJECT_SELF, DelayCommand( fSpawnTime, sp_Spawn(oNearest, GetLocalString(oNearest, "Blueprint"), iToSpawn))); AssignCommand(OBJECT_SELF, DelayCommand( fSpawnTime, SetLocalInt(oNearest, "IsSpawning", 0))); } //////////////////////////////////////////////////////////// // End of Main Procedure } nNth++; oNearest = GetNearestObject( 32, OBJECT_SELF, nNth ); } } // No comments beyond this point :) void sp_KillCreatures(object oNearest, string sCurrentTag) { int iCurObject = 1; object oCreatureToKill = GetNearestObjectByTag(sCurrentTag, OBJECT_SELF, iCurObject); while (GetIsObjectValid(oCreatureToKill)) { if (!GetIsInCombat(oCreatureToKill) && GetObjectType(oCreatureToKill) != 32 && GetLocalString(oCreatureToKill, "SpawnedBy") == ObjectToString(oNearest)) { if (iDebug) { // Debug message SendMessageToAllDMs("Killing - " + GetTag(oCreatureToKill)); } // Comment the following two lines and uncomment the 3rd // to get rid of the death effect when it kills a creature // it spawned. effect eDeath = EffectDeath(TRUE, TRUE); ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oCreatureToKill); //DestroyObject(oCreatureToKill, 0.0); } iCurObject++; oCreatureToKill = GetNearestObjectByTag(sCurrentTag, OBJECT_SELF, iCurObject); } } void sp_CheckVars(object oNearest, string 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, "IsSpawner", 1); } void sp_Spawn(object oNearest, string sTemplate, int iSpawnX) { location lCurrentWP = GetLocation(oNearest); int iTotalX = 0; if (GetLocalInt(oNearest, "SpawnRandomNumber")) { iSpawnX = Random(GetLocalInt(oNearest, "SpawnRandomNumber")) + iSpawnX - 1; } while (iTotalX < iSpawnX) { if (iDebug) { // Debug message SendMessageToAllDMs("Using template - " + sTemplate); } if (GetLocalInt(oNearest, "NPC_Radius")) { lCurrentWP = sp_RandomLocation(oNearest, GetLocalInt(oNearest, "NPC_Radius")); } if (GetLocalInt(oNearest, "IsTreasure")) { CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lCurrentWP, TRUE); } else { if (GetLocalInt(oNearest, "SpawnEffect")) { 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); } CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lCurrentWP, TRUE); } int iNearest = 1; int iCountNearest = 0; object oLastSpawned = GetNearestObjectByTag( GetLocalString(oNearest, "Tag"), oNearest, iNearest); while (iCountNearest != 1) { if (GetLocalString(oLastSpawned, "SpawnedBy") == "") { SetLocalString(oLastSpawned, "SpawnedBy", ObjectToString(oNearest)); if (iDebug) { // Debug message SendMessageToAllDMs("Object string - " + GetLocalString( oLastSpawned, "SpawnedBy")); } iCountNearest = 1; } iNearest++; oLastSpawned = GetNearestObjectByTag( GetLocalString(oNearest, "Tag"), oNearest, iNearest); if (!GetIsObjectValid(oLastSpawned)) { iCountNearest = 1; } } iTotalX++; } } string sp_CleanString(object oNearest, string sType) { 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, 5)) == "post_" && 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, int iSpawnDistance) { int iRD = 0; if (iSpawnDistance >= 1) { object oPC = GetFirstPC(); if (GetIsObjectValid(oPC)) { while (GetIsObjectValid(oPC)) { if (GetDistanceBetween(oNearest, oPC) <= (IntToFloat(iSpawnDistance))) { if (!GetLocalInt(oNearest, "PC_Entered")) { SetLocalInt(oNearest, "PC_Entered", 1); } iRD = 1; } oPC = GetNextPC(); } } if (iRD != 1) { SetLocalInt(oNearest, "PC_Entered", 0); } } else { iRD = 1; } return iRD; } int sp_CheckTimeOfDay(object oNearest) { int iValidTime = 0; if (GetIsDay() && GetLocalInt(oNearest, "SpawnDay") == 1) { iValidTime = 1; } else if (GetIsNight() && GetLocalInt(oNearest, "SpawnNight") == 1) { iValidTime = 1; } else if (GetLocalInt(oNearest, "SpawnNight") == 0 && GetLocalInt(oNearest, "SpawnDay") == 0) { iValidTime = 1; } return iValidTime; } int sp_CheckCreatures(object oNearest, string sCurrentTag) { int iMaxXX = GetLocalInt(oNearest, "SpawnAmount"); int iCountSX = 1; int iCountSX_Dead = 0; object oCurrent = GetNearestObjectByTag(sCurrentTag, OBJECT_SELF, iCountSX); while (iCountSX <= iMaxXX) { if (GetIsObjectValid(oCurrent) == FALSE) { iCountSX_Dead++; } else if (GetObjectType(oCurrent) == 32) { iMaxXX++; } else if (GetLocalString(oCurrent, "SpawnedBy") != ObjectToString(oNearest) && iDebug) { // Debug message SendMessageToAllDMs("Creature " + GetTag(oCurrent) + " = "+ GetLocalString(oCurrent, "SpawnedBy")); SendMessageToAllDMs("Spawn Point " + GetTag(oNearest) + " = " + ObjectToString(oNearest)); iMaxXX++; } iCountSX++; oCurrent = GetNearestObjectByTag(sCurrentTag, OBJECT_SELF, iCountSX); } return iCountSX_Dead; } int sp_GetValue(object oNearest, string sSwitch, string sCurrentName) { int iSwitchValue = 0; int iIsMinutes = 0; if (FindSubString(sCurrentName, sSwitch) > 0) { if (sSwitch == "DT") { iSwitchValue = 1; } else if (sSwitch == "NT") { iSwitchValue = 1; } else if (sSwitch == "TR") { iSwitchValue = 1; } else { int iNumberPos = FindSubString(sCurrentName, sSwitch); if (sSwitch == "TM") { if (GetSubString(sCurrentName, (iNumberPos + 4), 1) == "M") { iIsMinutes = 1; } } int iNumberLength = GetStringLength(sCurrentName) - iNumberPos; string sSwitchValue = GetSubString(sCurrentName, (iNumberPos + 2), 2); iSwitchValue = StringToInt(sSwitchValue); if (iIsMinutes == 1) { iSwitchValue = iSwitchValue * 60; } 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 < 2) { iMaxRand = 2; } SetLocalInt(oNearest, "SpawnRandomNumber", iMaxRand - iSwitchValue); } else { if (GetSubString(sCurrentName, (iNumberPos + 7), 1) == "M") { iIsMinutes = 1; iMaxRand = iMaxRand * 60; iSwitchValue = iSwitchValue * 60; } SetLocalInt(oNearest, "TimerIsRandom", iMaxRand - iSwitchValue); } } } } else if (sSwitch == "SX") { iSwitchValue = 1; } return iSwitchValue; } location sp_RandomLocation(object oNearest, int iRandArea) { vector vNewPos = GetPosition(oNearest); object oArea = GetArea(oNearest); float fX, fY; float fRadius = IntToFloat(iRandArea); int iValid = FALSE; while (iValid != TRUE) { fX = (Random(200)/100.0 - 1.0) * fRadius; fY = (Random(200)/100.0 - 1.0) * fRadius; if (fX * fX + fY * fY <= fRadius * fRadius) { iValid = TRUE; } } vNewPos += Vector(fX, fY, 0.0); location lNewLoc = Location(oArea, vNewPos, VectorToAngle(-1.0 * vNewPos)); return lNewLoc; }