enderalse/source/scripts/critterspawn.psc

333 lines
9.3 KiB
Plaintext
Raw Normal View History

scriptName CritterSpawn extends ObjectReference
2021-10-05 22:22:24 +00:00
import Critter
import Utility
;----------------------------------------------
; Properties to be set for this Critter spawn
;----------------------------------------------
; The type of critter (base object) to create
FormList property CritterTypes auto
{ The base object to create references of to spawn critters}
; The distance from this spawner that Moths are allowed to be
float 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}
Bool property bAllowRespawn = true auto
Bool property bReducedRespawn = true auto
2021-10-05 22:22:24 +00:00
;----------------------------------------------
; Constants (shouldn't need to modify these)
;----------------------------------------------
;----------------------------------------------
; Variables to keep track of spawned critters
;----------------------------------------------
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()
2021-10-05 22:22:24 +00:00
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
2021-10-05 22:22:24 +00:00
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
2021-10-05 22:22:24 +00:00
endEVENT
event OnUpdate()
VanillaLoopBreak()
if (iSpawnedCritterCount < iMaxCritterCount*10)
if shouldSpawn()
SpawnABatchOfCritters()
elseif shouldTryAgain
RegisterForSingleUpdate(fCheckPlayerDistanceTime + extraWaitTime )
endif
endif
endEvent
EVENT onUnload()
shouldTryAgain = false
UnregisterForUpdate()
2021-10-05 22:22:24 +00:00
endEVENT
EVENT onCellDetach()
shouldTryAgain = false
UnregisterForUpdate()
2021-10-05 22:22:24 +00:00
endEVENT
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
2021-10-05 22:22:24 +00:00
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
2021-10-05 22:22:24 +00:00
;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
2021-10-05 22:22:24 +00:00
endFunction
function QueueAdditionalSpawns()
VanillaLoopBreak()
if (!isSpawning && iSpawnedCritterCount < iMaxCritterCount*10)
UnregisterForUpdate()
RegisterForSingleUpdate(1+ (1+iSpawnedCritterCount) * iRespawnDelay )
2021-10-05 22:22:24 +00:00
endif
endFunction
2021-10-05 22:22:24 +00:00
; Called by critters when they die
Event OnCritterDied()
VanillaLoopBreak()
if iDeadCritterCount < iSpawnedCritterCount
iDeadCritterCount += 1
2021-10-05 22:22:24 +00:00
else
;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);
2021-10-05 22:22:24 +00:00
endif
; a critter died, if we're not currently spawning, try to refresh the queue or push back the delay
QueueAdditionalSpawns()
endEvent
2021-10-05 22:22:24 +00:00
bool Function SpawnCritterAtRef(ObjectReference arSpawnRef)
if VanillaLoopBreak()
2021-10-05 22:22:24 +00:00
return false
endif
; Pick a random critter type
Activator critterType = CritterTypes.GetAt(RandomInt(0, CritterTypes.GetSize() - 1)) as Activator
2021-10-05 22:22:24 +00:00
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 );
2021-10-05 22:22:24 +00:00
return false
endif
return false
endFunction
2021-10-05 22:22:24 +00:00
float Function GetPlayerDistance() ; Caches player reference too
2021-10-05 22:22:24 +00:00
if VanillaLoopBreak()
return fMaxPlayerDistance + 1
endif
if !PlayerRef
PlayerRef = Game.GetPlayer()
endif
return PlayerRef.GetDistance(self)
2021-10-05 22:22:24 +00:00
endFunction
bool Function ShouldSpawn()
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
2021-10-05 22:22:24 +00:00
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
2021-10-05 22:22:24 +00:00
endIf
return IsActiveTime() && CustomCheck()
else
shouldTryAgain = false
2021-10-05 22:22:24 +00:00
return false
endif
endFunction
function SetExtraWaitTime(float t)
extraWaitTime = t
endFunction
function ClearExtraWaitTime()
extraWaitTime = 0
endFunction
2021-10-05 22:22:24 +00:00
;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
2021-10-05 22:22:24 +00:00
endFunction