The fix is in the SpawnCritter() function. In the original script the else condition did not have a return value. This was a problem because the function expects a boolean returned. Instead Bethesda did some odd thing by setting a variable instead of simply returning false as well. So i inserted a 'return false'. This seemed to have fixed the endless loop the script was stuck in.
Hopefully it eliminates the bloat..initial testing has been positive. Did not see log spamming using a recent save. If anyone wants to test let me know how it turns out, please use a 'test' save if you do.
scriptName CritterSpawn extends ObjectReferenceimport Critterimport Utility;----------------------------------------------; Properties to be set for this Critter spawn;----------------------------------------------; The type of critter (base object) to createFormList property CritterTypes auto{ The base object to create references of to spawn critters}; The distance from this spawner that Moths are allowed to befloat property fLeashLength = 500.0 auto{ The distance that moths are allowed to be from this spawner}float property fLeashHeight = 50.0 auto{ The distance that dragonflies are allowed to be from above spawner}float property fLeashDepth = 50.0 auto{ The distance that fish are allowed to be from below spawner}float property fMaxPlayerDistance = 2000.0 auto{ The distance from the player before the Spawner stops spawning critters}int property iMaxCritterCount = 10 auto{ The maximum number of critters this spawner can generate}float property fFastSpawnInterval = 0.1 auto{ When spawning critters, the interval between spawns}float property fSlowSpawnInterval = 5.0 auto{ When spawning critters, the interval between spawns}GlobalVariable property GameHour auto{ Make this point to the GameHour global }float property fStartSpawnTime = 6.0 auto{ The Time after which this spawner can be active}float property fEndSpawnTime = 11.0 auto{ The Time before which this spawner can be active}float property fLeashOverride auto{Optional: Manually set roaming radius for critters spawned}bool property bSpawnInPrecipitation auto{Should this critter spawn in rain/snow? DEFAULT: FALSE};----------------------------------------------; Constants (shouldn't need to modify these);----------------------------------------------float fCheckPlayerDistanceTime = 2.0;----------------------------------------------; Variables to keep track of spawned critters;----------------------------------------------int property iCurrentCritterCount = 0 auto hiddenbool bLoopingbool bPrintDebug = FALSE ; should usually be set to false.int recursions; Do initial stuff when my 3D has loaded up.EVENT OnLoad(); The Spawner will register for update and periodically check whether the player is close or not; - JOEL REFACTOR - Going to use onLoad() & onUnload() instead of states?; GotoState("WaitingForPlayer"); - JOEL REFACTOR - also no longer need to update; RegisterForSingleUpdate(fCheckPlayerDistanceTime)if bPrintDebug == TRUE; debug.trace("spawner " + self + " loaded.") recursions = 0endif; set our control bool to start the loopbLooping = TRUEwhile bLooping == TRUE if bPrintDebug == TRUE recursions += 1; debug.trace("spawner " + self + " while loop #" + recursions) endif if shouldSpawn() == FALSE ; wait a bit, then see if the player is close again. ; Removing this to eliminate some TPLOG spam; ; debug.TraceConditional("player not yet near spawner " + self, bPrintDebug) utility.wait(fCheckPlayerDistanceTime) else; ; debug.TraceConditional("spawner " + self + " ready to spawn!!!", bPrintDebug) ; player must be nearby - spawn our initial critters ; don't follow up as we no longer wish to re-generate new critters until the player leaves entirely spawnInitialCritterBatch() bLooping = FALSE endifendWhileendEVENTEVENT onUnload(); when our 3D unloads, stop looping until loaded again.bLooping = FALSE; ; debug.TraceConditional("spawner " + self + " unloading due to onUnload() EVENT.", bPrintDebug)endEVENTEVENT onCellDetach()bLooping = FALSE; ; debug.TraceConditional("spawner " + self + " unloading due to onCellDetach() EVENT.", bPrintDebug)endEVENTFunction SpawnInitialCritterBatch(); How many do we need to spawn?int icrittersToSpawn = iMaxCritterCount - iCurrentCritterCount; Create that many crittersint i = 0;while (i &--#60; icrittersToSpawn) ; Create one critter at a time SpawnCritter() ; Wait a bit before the next spawn ;Wait(fFastSpawnInterval) ; Next i = i + 1endWhileendFunction; Called by critters when they dieEvent OnCritterDied(); Decrement current critter count, next time OnUpdate; gets called, we'll spawn a new oneif iCurrentCritterCount &--#62; 0 iCurrentCritterCount = iCurrentCritterCount - 1elseif iCurrentCritterCount == 0 ; don't do anything if already @ zeroelse ; iCurrentCritterCount must be in the negatives. Something is up, but for now increment towards zero iCurrentCritterCount = iCurrentCritterCount + 1endifendEvent; Spawns one Critterbool Function SpawnCritter()if (iCurrentCritterCount &--#60; iMaxCritterCount) && (iCurrentCritterCount &--#62;= 0) ; Go ahead with the actual spawn SpawnCritterAtRef(self) ; Increment count iCurrentCritterCount = iCurrentCritterCount + 1 return trueelseif iCurrentCritterCount &--#60; 0; debug.trace("("+self+") has invalid iCurrentCritterCount of "+iCurrentCritterCount+", abort!") ; turn off loop bLooping = FALSE return falseelse; debug.trace("("+self+") SpawnCritter() failed because iCurrentCritterCount is "+iCurrentCritterCount); ;debug.trace("("+self+") iMaxCritterCount: "+iMaxCritterCount)endifendFunction; Spawns one Critter at a specific locationFunction SpawnCritterAtRef(ObjectReference arSpawnRef); Pick a random critter typeActivator critterType = CritterTypes.GetAt(RandomInt(0, CritterTypes.GetSize() - 1)) as Activator; Create the critter and cast it to the critter base classObjectReference critterRef = arSpawnRef.PlaceAtMe(critterType, 1, false, true)Critter thecritter = critterRef as Critter; Set initial variables on the critter; ; Debug.TraceConditional("Spawner " + self + " is creating Critter " + thecritter, bPrintDebug);thecritter.SetInitialSpawnerProperties(fLeashLength, fLeashHeight, fLeashDepth, fMaxPlayerDistance + fLeashLength, self)endFunction; Utility method that returns the player's distancefloat Function GetPlayerDistance()return Game.GetPlayer().GetDistance(self)endFunction; Utility method that tells the spawner whether it should spawn crittersbool Function ShouldSpawn();DREW - Added an extra safety measure for if the object is stuck in the spawn check loop while the 3d is not loaded; NOTE - is3dLoaded dumps an error when the 3d is not loaded, but the function still returns false which should; set bLooping to false and jump out of the bLooping While, at which point no additional errors will be thrownif self.is3dLoaded() if !(GetPlayerDistance() &--#60;= fMaxPlayerDistance) return false endIf ; Otherwise, base value on time of day (no handling of wrap around though...) return IsActiveTime()else ;if the 3d is not loaded jump out of the looped state. Just an extra safety measure.; ;debug.Trace(self + ": should be setting bLooping to False") bLooping = FALSE return falseendifendFunctionbool Function IsActiveTime()bool binTimeRange = falseif (fEndSpawnTime &--#62;= fStartSpawnTime) binTimeRange = (GameHour.GetValue() &--#62;= fStartSpawnTime) && (GameHour.GetValue() &--#60; fEndSpawnTime)else binTimeRange = (GameHour.GetValue() &--#62;= fStartSpawnTime) || (GameHour.GetValue() &--#60; fEndSpawnTime)endIfreturn binTimeRange && ((Weather.GetCurrentWeather() == none) || \ (Weather.GetCurrentWeather().GetClassification() &--#60; 2) || \ (bSpawnInPrecipitation == TRUE))endFunction