From 278c4cfefdcba4b78b165965e8a1d25685dd7f4d Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Wed, 25 Oct 2023 23:34:09 +0200 Subject: [PATCH] Added CritterSpawn Congestion Fix 1.52 by Excinerus --- Enderal Credits.txt | 1 + scripts/critterspawn.pex | Bin 5055 -> 8154 bytes source/scripts/critterspawn.psc | 384 ++++++++++++++++++++------------ 3 files changed, 244 insertions(+), 141 deletions(-) diff --git a/Enderal Credits.txt b/Enderal Credits.txt index 41ebb10f..dad497f6 100644 --- a/Enderal Credits.txt +++ b/Enderal Credits.txt @@ -649,6 +649,7 @@ CommonLibSSE-NG by Ryan McKenzie, powerofthree, Charmed Baryon, and others Better Dialogue Controls by ecirbaf Unofficial Enderal Port (fs.dll) by Hishutup and FelesNoctis Shorter Grass by Fhaarkas +CritterSpawn Congestion Fix by Excinerus Ghost Item Bug Fix for SkyUI by EdmanSA Drunk Sinking Head Idle Fix by KnightRangersGuild Book Covers Skyrim by DanielCoffey diff --git a/scripts/critterspawn.pex b/scripts/critterspawn.pex index 0b108151d57f986677a961d050c516c85dfdf769..fe876c746f8fcc439001882c560ae45f8a629cc0 100644 GIT binary patch literal 8154 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 E2XY%nA^-pY diff --git a/source/scripts/critterspawn.psc b/source/scripts/critterspawn.psc index ae4e8595..302860e7 100644 --- a/source/scripts/critterspawn.psc +++ b/source/scripts/critterspawn.psc @@ -1,5 +1,4 @@ -scriptName CritterSpawn extends ObjectReference -{MODIFIED BY STEVE40 and USKP} +scriptName CritterSpawn extends ObjectReference import Critter import Utility @@ -42,189 +41,292 @@ float property fLeashOverride auto bool property bSpawnInPrecipitation auto {Should this critter spawn in rain/snow? DEFAULT: FALSE} + +Bool property bAllowRespawn = true auto +Bool property bReducedRespawn = true auto ;---------------------------------------------- ; Constants (shouldn't need to modify these) ;---------------------------------------------- -float fCheckPlayerDistanceTime = 2.0 ;---------------------------------------------- ; Variables to keep track of spawned critters ;---------------------------------------------- -int property iCurrentCritterCount = 0 auto hidden -bool bLooping -bool bPrintDebug = FALSE ; should usually be set to false. -int recursions - -; Do initial stuff when my 3D has loaded up. -EVENT OnCellAttach() ; USKP 1.3.3 - Changed from OnLoad() to be more reliable, especially in interior cells. - ; 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 = 0 - endif - - ; set our control bool to start the loop - bLooping = TRUE - - while bLooping - if bPrintDebug - recursions += 1 -; debug.trace("spawner " + self + " while loop #" + recursions) + + + +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 property fCheckPlayerDistanceTime = 2.0 auto hidden +int property iSpawnedCritterCount = 0 auto hidden +int property iDeadCritterCount = 0 auto hidden +int property iRespawnDelay = 6 auto hidden + + +bool property isSpawning = false auto hidden +bool property shouldTryAgain = false auto hidden +actor property PlayerRef auto + + +bool bPrintDebug = FALSE +Cell _ParentCell + +Cell property ParentCell + Cell function get() + if !_ParentCell + _ParentCell = self.GetParentCell() endif + return _ParentCell + endFunction +endproperty + + +bool Function VanillaLoopBreak() + + if (bLooping || iCurrentCritterCount>0) + ; breaking OnCellAttach runaway loop in baked vanilla functions + bLooping = false + ; breaking SpawnInitialCritterBatch runaway loop in baked vanilla and uskp functions + iCurrentCritterCount = 0 - if !shouldSpawn() - ; 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 - endif - endWhile - + Debug.Trace("CritterSpawn : Runaway Spawner warning :" + self); + return true + endif + return false +EndFunction +Function SpawnInitialCritterBatch() + VanillaLoopBreak() +endFunction + + + + + +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 + iDeadCritterCount = 0 + isSpawning = false + + shouldTryAgain = true + + if shouldSpawn() + SpawnABatchOfCritters() + elseif shouldTryAgain + ;randomize the update time so we're less likely to have synchronized spawners asking for updates + RegisterForSingleUpdate(fCheckPlayerDistanceTime * Randomfloat(1.0,1.5)) + + endif endEVENT -EVENT onUnload() - ; when our 3D unloads, stop looping until loaded again. - bLooping = FALSE -; ; debug.TraceConditional("spawner " + self + " unloading due to onUnload() EVENT.", bPrintDebug) +event OnUpdate() + + VanillaLoopBreak() + if (iSpawnedCritterCount < iMaxCritterCount*10) + if shouldSpawn() + SpawnABatchOfCritters() + elseif shouldTryAgain + RegisterForSingleUpdate(fCheckPlayerDistanceTime + extraWaitTime ) + endif + endif +endEvent + + + + +EVENT onUnload() + shouldTryAgain = false + UnregisterForUpdate() endEVENT EVENT onCellDetach() - bLooping = FALSE -; ; debug.TraceConditional("spawner " + self + " unloading due to onCellDetach() EVENT.", bPrintDebug) + shouldTryAgain = false + UnregisterForUpdate() endEVENT -Function SpawnInitialCritterBatch() - ; How many do we need to spawn? - int icrittersToSpawn = iMaxCritterCount - iCurrentCritterCount +Function SpawnABatchOfCritters() + VanillaLoopBreak() + ; Important note here, even if the instance locking this thread gets dumped midway leaving the thread locked + ; this wouldn't leave threads wait()ing in the stacks, they'll just keep reregistering every iSpawnedCritterCount + ; and just won't spawn anything, the whole thing will be fixed when the spawner is unloaded and loaded again - ; Create that many critters - int i = 0; - while (i < icrittersToSpawn) - ; Create one critter at a time - SpawnCritter() - - ; Wait a bit before the next spawn - ;Wait(fFastSpawnInterval) + if (! isSpawning && iSpawnedCritterCount < iMaxCritterCount*10) + isSpawning = true + + + ;if not using the Fixed critter script there's no way to prevent over-reported critter deaths, but we'll cap spawns anyway to the max value + if (iDeadCritterCount > iSpawnedCritterCount) + iDeadCritterCount = iSpawnedCritterCount + endIf + + + int spawnAttempts = iMaxCritterCount - iSpawnedCritterCount + iDeadCritterCount - ; Next - i = i + 1 - endWhile + ;limiting amount of respawns by distance + if (iDeadCritterCount>0 && bReducedRespawn) + spawnAttempts = 1 + endif + + while (spawnAttempts) + if (SpawnCritterAtRef(self)) + iSpawnedCritterCount += 1 + endif + spawnAttempts -=1 + endWhile + + isSpawning = false + if (iMaxCritterCount - iSpawnedCritterCount + iDeadCritterCount) + ;we couldn't spawn enough critters, or the player is currently killing them : try a bit later + QueueAdditionalSpawns() + endIf + endIf endFunction -; Called by critters when they die -Event OnCritterDied() - ; Decrement current critter count, next time OnUpdate - ; gets called, we'll spawn a new one - if iCurrentCritterCount > 0 - iCurrentCritterCount -= 1; faster [USKP 2.0.1] - elseif iCurrentCritterCount < 0 - ; iCurrentCritterCount must be in the negatives. Something is up, but for now increment towards zero - iCurrentCritterCount = 0; earler saves with failed deletions [USKP 2.0.1] + +function QueueAdditionalSpawns() + VanillaLoopBreak() + if (!isSpawning && iSpawnedCritterCount < iMaxCritterCount*10) + UnregisterForUpdate() + RegisterForSingleUpdate(1+ (1+iSpawnedCritterCount) * iRespawnDelay ) endif -endEvent +endFunction -; Spawns one Critter -bool Function SpawnCritter() - if (iCurrentCritterCount < iMaxCritterCount) && (iCurrentCritterCount >= 0) - ; Go ahead with the actual spawn - return SpawnCritterAtRef(self) -;/// [USKP 2.0.1] moved into SpawnCritterAtRef - ; Increment count - iCurrentCritterCount = iCurrentCritterCount + 1 - return true -///; - elseif iCurrentCritterCount < 0 -; debug.trace("("+self+") has invalid iCurrentCritterCount of "+iCurrentCritterCount+", abort!") - ; turn off loop - bLooping = FALSE +; Called by critters when they die +Event OnCritterDied() + VanillaLoopBreak() + if iDeadCritterCount < iSpawnedCritterCount + iDeadCritterCount += 1 else -; debug.trace("("+self+") SpawnCritter() failed because iCurrentCritterCount is "+iCurrentCritterCount) -; ;debug.trace("("+self+") iMaxCritterCount: "+iMaxCritterCount) + ;iDeadCritterCount is overflowing for some reason + ;increase the spawned Critter count instead so the queue keeps getting postponed until the script shuts down + iSpawnedCritterCount = iDeadCritterCount + Debug.Trace("CritterSpawn : iDeadCritterCount overflowing " + iDeadCritterCount); + endif - - return False ; [USKP 1.3.1] to help eliminate log spam and potential long term save bloating. -endFunction + ; a critter died, if we're not currently spawning, try to refresh the queue or push back the delay + QueueAdditionalSpawns() +endEvent -; Spawns one Critter at a specific location -; returns true on success [USKP 2.0.1] -; bool Function SpawnCritterAtRef(ObjectReference arSpawnRef) - ; Pick a random critter type - Activator critterType = CritterTypes.GetAt(RandomInt(0, CritterTypes.GetSize() - 1)) as Activator - if critterType == none - ; STEVE40+USKP extra check + if VanillaLoopBreak() return false endif - - ; Create the critter and cast it to the critter base class - ObjectReference critterRef = arSpawnRef.PlaceAtMe(critterType, 1, false, true) - Critter thecritter = critterRef as Critter + ; Pick a random critter type + Activator critterType = CritterTypes.GetAt(RandomInt(0, CritterTypes.GetSize() - 1)) as Activator - if thecritter == none - ; STEVE40+USKP extra check + 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 ); return false endif + + return false + +endFunction - ; Set initial variables on the critter -; ; Debug.TraceConditional("Spawner " + self + " is creating Critter " + thecritter, bPrintDebug); - thecritter.SetInitialSpawnerProperties(fLeashLength, fLeashHeight, fLeashDepth, fMaxPlayerDistance + fLeashLength, self) - ; Increment count [USKP 2.0.1] - iCurrentCritterCount += 1 - return true -endFunction +float Function GetPlayerDistance() ; Caches player reference too -; Utility method that returns the player's distance -float Function GetPlayerDistance() - return Game.GetPlayer().GetDistance(self) + if VanillaLoopBreak() + return fMaxPlayerDistance + 1 + endif + if !PlayerRef + PlayerRef = Game.GetPlayer() + endif + return PlayerRef.GetDistance(self) endFunction -; Utility method that tells the spawner whether it should spawn critters bool 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 thrown - ; GetParentCell check first helps avoid error log in exteriors? [USKP 2.0.1] - if self.GetParentCell() && self.is3dLoaded() - if !(GetPlayerDistance() <= fMaxPlayerDistance) + if VanillaLoopBreak() + return false + endif + if (iSpawnedCritterCount >= iMaxCritterCount*10) + shouldTryAgain = false + return false + endif + + if (!bAllowRespawn && (iSpawnedCritterCount >= iMaxCritterCount)) + shouldTryAgain = false + return false + endif + + if ParentCell != none + float distance = GetPlayerDistance() + if !self.is3dLoaded() || ( distance > fMaxPlayerDistance) + extraWaitTime = 2.0 return false + else + if (bReducedRespawn && iDeadCritterCount > 0 && distance <= fMaxPlayerDistance*0.75 ) + if (Randomfloat(1 )>0.99 && !PlayerRef.HasLos(self)) + extraWaitTime = 0.0 + else + extraWaitTime = 2.0 + return false + endif + else + extraWaitTime = 0.0 + endif 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 IsActiveTime() && CustomCheck() + else + shouldTryAgain = false return false endif endFunction -bool Function IsActiveTime() - bool binTimeRange = false +function SetExtraWaitTime(float t) + extraWaitTime = t +endFunction +function ClearExtraWaitTime() + extraWaitTime = 0 +endFunction - ; BUGFIX BY STEVE40 - I found one case in Hearthfire where Bethesda didn't set the GameHour property on the script, causing it to spam the logs - if GameHour == none - bLooping = False ; kill the script - return false - endIf - if (fEndSpawnTime >= fStartSpawnTime) - binTimeRange = (GameHour.GetValue() >= fStartSpawnTime) && (GameHour.GetValue() < fEndSpawnTime) - else - binTimeRange = (GameHour.GetValue() >= fStartSpawnTime) || (GameHour.GetValue() < fEndSpawnTime) - endIf - return binTimeRange && ((Weather.GetCurrentWeather() == none) || \ - (Weather.GetCurrentWeather().GetClassification() < 2) || \ - (bSpawnInPrecipitation == TRUE)) +;Custom method to override in custom scripts +bool Function CustomCheck() + return true +endFunction + +bool Function IsActiveTime() + if VanillaLoopBreak() + return false + endif + bool binTimeRange = (fEndSpawnTime != fStartSpawnTime) ;dont bother reading or checking for Gamehour if not timeframe is set + + if (binTimeRange) + if GameHour + float GameHourf = GameHour.GetValue() + if (fEndSpawnTime >= fStartSpawnTime) + binTimeRange = (GameHourf >= fStartSpawnTime) && (GameHourf < fEndSpawnTime) + else + binTimeRange = (GameHourf >= fStartSpawnTime) || (GameHourf < fEndSpawnTime) + endIf + else + shouldTryAgain = false ;spawner not set up properly, stop it + Debug.Trace("CritterSpawn :" + self + " spawner not set up properly, has a timerane while missing the GameHourf property"); + + return false + endif + endif + + if (binTimeRange) + if (bSpawnInPrecipitation) ;dont check for weather condition if not needed + return true; + else + Weather W = Weather.GetCurrentWeather() + return !W || (W.GetClassification() < 2) + endif + endif + + return false endFunction