diff --git a/scripts/critterspawn.pex b/scripts/critterspawn.pex index 554680231..dc2c9697c 100644 Binary files a/scripts/critterspawn.pex and b/scripts/critterspawn.pex differ diff --git a/source/scripts/critterspawn.psc b/source/scripts/critterspawn.psc index cbee6d74f..69c7535e3 100644 --- a/source/scripts/critterspawn.psc +++ b/source/scripts/critterspawn.psc @@ -57,6 +57,10 @@ Bool property bReducedRespawn = true auto int property iCurrentCritterCount = 0 auto hidden ; not used, set to -1 to break vanilla spawner while loops bool bLooping = false ; reintroduced to catch baked runaway loops float extraWaitTime = 0.0 ; extra time in seconds before trying again +float fDetachGameTime = 0.0 ; game time (days) of the last cell detach +; critter.psc delays the kill of a detached critter by 0.31831 game hours and cancels it if the cell attaches again earlier, +; so the live count is carried over on such a quick re-attach +float fOldCrittersDeadAfter = 0.02 float property fCheckPlayerDistanceTime = 2.0 auto hidden @@ -108,7 +112,12 @@ EVENT OnCellAttach() VanillaLoopBreak() ; the spawner will attach to a cell, this can be the cell we just teleported to (door) or one that loaded in the distance ; shouldSpawn() will check if the spawner should start spawning critters, wait a bit or do nothing - iSpawnedCritterCount = 0 + ; critters of the previous attach survive a short detach, keep counting them + int liveCritters = iSpawnedCritterCount - iDeadCritterCount + if liveCritters < 0 || liveCritters > iMaxCritterCount || Utility.GetCurrentGameTime() - fDetachGameTime > fOldCrittersDeadAfter + liveCritters = 0 + endif + iSpawnedCritterCount = liveCritters iDeadCritterCount = 0 isSpawning = false @@ -146,6 +155,7 @@ endEVENT EVENT onCellDetach() shouldTryAgain = false UnregisterForUpdate() + fDetachGameTime = Utility.GetCurrentGameTime() endEVENT Function SpawnABatchOfCritters() @@ -171,15 +181,20 @@ Function SpawnABatchOfCritters() spawnAttempts = 1 endif + bool spawnFailed = spawnAttempts > 0 while (spawnAttempts>0) if (SpawnCritterAtRef(self)) iSpawnedCritterCount += 1 + spawnFailed = false endif spawnAttempts -=1 endWhile isSpawning = false - if (iMaxCritterCount - iSpawnedCritterCount + iDeadCritterCount>0) + if spawnFailed + ;no critter could be placed, CritterTypes is broken: do not retry until the next cell attach or critter death + shouldTryAgain = false + elseif (iMaxCritterCount - iSpawnedCritterCount + iDeadCritterCount>0) ;we couldn't spawn enough critters, or the player is currently killing them : try a bit later QueueAdditionalSpawns() endIf @@ -216,22 +231,35 @@ bool Function SpawnCritterAtRef(ObjectReference arSpawnRef) if VanillaLoopBreak() return false endif - ; Pick a random critter type - Activator critterType = CritterTypes.GetAt(RandomInt(0, CritterTypes.GetSize() - 1)) as Activator - - if critterType - Critter critty = arSpawnRef.PlaceAtMe(critterType, 1, false, true) as Critter - if critty - critty.SetInitialSpawnerProperties(fLeashLength, fLeashHeight, fLeashDepth, fMaxPlayerDistance + fLeashLength, self) - return true - endif - else - Debug.Trace("CritterSpawn :" + arSpawnRef + " attempted to spawn a bad critter type, check the contents of " + CritterTypes ); + int typeCount = 0 + if CritterTypes + typeCount = CritterTypes.GetSize() + endif + if typeCount == 0 + Debug.Trace("CritterSpawn :" + self + " has an empty CritterTypes list") return false endif - - return false - + + ; Pick a random critter type + Activator critterType = CritterTypes.GetAt(RandomInt(0, typeCount - 1)) as Activator + if !critterType + Debug.Trace("CritterSpawn :" + arSpawnRef + " attempted to spawn a bad critter type, check the contents of " + CritterTypes) + return false + endif + + ObjectReference critterRef = arSpawnRef.PlaceAtMe(critterType, 1, false, true) + Critter critty = critterRef as Critter + if !critty + ; the activator has no Critter script, remove the orphan reference + Debug.Trace("CritterSpawn :" + critterType + " has no Critter script, check the contents of " + CritterTypes) + if critterRef + critterRef.Delete() + endif + return false + endif + + critty.SetInitialSpawnerProperties(fLeashLength, fLeashHeight, fLeashDepth, fMaxPlayerDistance + fLeashLength, self) + return true endFunction