Compare commits

...

2 Commits

Author SHA1 Message Date
278c4cfefd Added CritterSpawn Congestion Fix 1.52 by Excinerus 2023-10-25 23:34:09 +02:00
ea53444dbe Removed update procedures 2023-10-25 23:32:49 +02:00
12 changed files with 256 additions and 304 deletions

View File

@ -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

View File

@ -1 +1 @@
version = 2.0.12.5
version = 2.0.13

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -151,15 +151,3 @@ Function TriggerAnimationSyncUpdate()
RegisterForSingleUpdate(3.0)
EndIf
EndFunction
Function UpdateFillPackages(Package akFillPackage) ; Version 2.0.8 Update
Int i = 0
While i < akCustomAnimPackageArray.Length
akCustomAnimPackageArray[i] = akFillPackage
i += 1
EndWhile
If Is3DLoaded()
ResetAnimations()
EndIf
EndFunction

View File

@ -543,17 +543,6 @@ Function ClearPlayerAlias()
EndFunction
Function UpdateAarosCarryTraySpell() ; Version 2.0.8 Update
If _00E_FS_NQ07_AarosREF.HasSpell(_00E_AbCarryDrinkTray_Old)
_00E_FS_NQ07_AarosREF.RemoveSpell(_00E_AbCarryDrinkTray_Old)
Wait(1.0)
Int iCurStage = GetCurrentStageID()
If iCurStage >= 35 && iCurStage < 50
AarosAddSpell()
EndIf
EndIf
EndFunction
Function AskCreatePotion()
If GetCurrentStageID() == 115

View File

@ -195,10 +195,6 @@ Function RegisterNewHousingObjectOffsets(Float fNewOffsetAngleZ, Float fNewOffse
fStoredOffsetPositionZ = fNewOffsetPositionZ
EndFunction
Function Update_209()
bStartTutorialShown = ((Self as Quest) as _00E_PlayerhousingTutorial).StartTutorialIsShown()
EndFunction
;=====================================================================================
; CONTROLS

View File

@ -1,7 +1,7 @@
Scriptname _00E_PlayerSetUpScript extends ObjectReference
{Initializes all the necessary Quests, maintains the player, contains various OnPlayerLoadGame() failsafes. This script is important for proper updating, do not overwrite it.}
Float Property CURRENT_PATCH_VERSION = 2.124 AutoReadOnly
Float Property CURRENT_PATCH_VERSION = 2.13 AutoReadOnly
;=====================================================================================
@ -34,8 +34,6 @@ Event OnInit()
EnableDisableKillmove()
; Added in 1.5.8.0
; starts all quests that are in the formlist
Int iIndex = QuestsToStart.GetSize()
While iIndex > 0
@ -59,7 +57,7 @@ Event OnPlayerLoadGame()
_00E_Func_CheckEnderalDLL.Run()
Maintenance()
If GetState() != "RealPlayer" ; Post-1.2.5.0 version update
If GetState() != "RealPlayer"
GoToState("RealPlayer")
EndIf
EndIf
@ -74,114 +72,6 @@ State RealPlayer
EndState
;=====================================================================================
; 2.0.6
;=====================================================================================
Function UpdateMQ12b_206()
_00E_MQ12b_Functions MQ12bFunctions = Game.GetForm(0x2EBAD) as _00E_MQ12b_Functions
Quest MQ15 = Game.GetFormFromFile(0x0002EBB0, "Skyrim.esm") as Quest
If MQ12bFunctions.GetCurrentStageID() >= 135 && MQ15.GetCurrentStageID() < 5
MQ12bFunctions.OldReenableDisabledNPC()
EndIf
EndFunction
;=====================================================================================
; 2.0.8
;=====================================================================================
Function Update_208()
; Obsolete abilities cleanup
_00E_FS_NQ07_Functions FS_NQ07_Quest = Game.GetFormFromFile(0x0001CA09, "Enderal - Forgotten Stories.esm") as _00E_FS_NQ07_Functions
Int iFS_NQ07Stage = FS_NQ07_Quest.GetCurrentStageID()
If iFS_NQ07Stage >= 5 && iFS_NQ07Stage < 410
FS_NQ07_Quest.UpdateAarosCarryTraySpell()
Else
RemoveObsoleteSpell(0x0009A893, "Skyrim.esm", 0x000F649A, "Skyrim.esm")
EndIf
RemoveObsoleteSpell(0x0010D339, "Skyrim.esm", 0x000F649A, "Skyrim.esm")
RemoveObsoleteSpell(0x0006FFAF, "Skyrim.esm", 0x000F6498, "Skyrim.esm")
RemoveObsoleteSpell(0x000F64A0, "Skyrim.esm", 0x000F6498, "Skyrim.esm")
; Refill _00E_AnimTest_SC package arrays where needed
FillAnimTestPackages(0x00137889, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x0013788A, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x0013789F, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x001378A2, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x001378AA, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x001378AB, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x00147E12, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x00147E14, "Skyrim.esm", 0x00070657, "Skyrim.esm")
FillAnimTestPackages(0x0010D336, "Skyrim.esm", 0x00070645, "Skyrim.esm")
FillAnimTestPackages(0x0010D33B, "Skyrim.esm", 0x00070645, "Skyrim.esm")
FillAnimTestPackages(0x00147DEA, "Skyrim.esm", 0x00070645, "Skyrim.esm")
FillAnimTestPackages(0x00147E37, "Skyrim.esm", 0x00070645, "Skyrim.esm")
; "Gypsy minstrels" update
Keyword performSpotKwd = Game.GetFormFromFile(0x000FB905, "Skyrim.esm") as Keyword
(Game.GetFormFromFile(0x00044EBC, "Skyrim.esm") as _00E_BardPlayInstrumentScript).LinkedPlayMarkerKeyword = performSpotKwd
(Game.GetFormFromFile(0x00046D79, "Skyrim.esm") as _00E_GypsyMinstrelsControlScript).UpdateWaypoint()
EndFunction
Function RemoveObsoleteSpell(Int idNPC, String esmNPC, Int idSpell, String esmSpell)
Spell obsoleteSpell = Game.GetFormFromFile(idSpell, esmSpell) as Spell
(Game.GetFormFromFile(idNPC, esmNPC) as Actor).RemoveSpell(obsoleteSpell)
EndFunction
Function FillAnimTestPackages(Int idNPC, String esmNPC, Int idPackage, String esmPackage)
Package newPackage = Game.GetFormFromFile(idPackage, esmPackage) as Package
(Game.GetFormFromFile(idNPC, esmNPC) as _00E_AnimTest_SC).UpdateFillPackages(newPackage)
EndFunction
;=====================================================================================
; 2.0.10
;=====================================================================================
Function Update_210()
Spell abSoulcaller = Game.GetFormFromFile(0x002F0EA, "Enderal - Forgotten Stories.esm") as Spell
If PlayerREF.HasSpell(abSoulcaller)
PlayerREF.RemoveSpell(abSoulcaller)
Utility.Wait(1)
PlayerREF.AddSpell(abSoulcaller, False)
EndIf
Perk perkBloodlust = Game.GetFormFromFile(0x00069D38, "Skyrim.esm") as Perk
_ResetPerk(perkBloodlust)
_00E_PlayerhousingMaster.GetMaster().Update_209()
; Previous versions may have silence tracks stuck
Levelsystem.RemoveSilence()
EndFunction
;=====================================================================================
; 2.0.12
;=====================================================================================
Function Update_212()
PlayerREF.AddPerk(Game.GetFormFromFile(0x14CF8, "Enderal - Forgotten Stories.esm") as Perk) ; _00E_SE_WerewolfBlockFurniturePerk
PlayerREF.AddPerk(Game.GetFormFromFile(0x14CFA, "Enderal - Forgotten Stories.esm") as Perk) ; _00E_SE_DismantlePerk
PlayerREF.AddPerk(Game.GetFormFromFile(0x14CF9, "Enderal - Forgotten Stories.esm") as Perk) ; _00E_SE_2_0_12_UpgradePerk
EndFunction
Function Update_212_hotfix1()
Quest rNQ05 = Game.GetFormFromFile(0x1C82F, "Enderal - Forgotten Stories.esm") as Quest
if rNQ05.GetCurrentStageID() >= 30 && rNQ05.GetCurrentStageID() < 40
PlayerREF.AddPerk(Game.GetFormFromFile(0x14CF6, "Enderal - Forgotten Stories.esm") as Perk) ; _00E_FS_NQ05_CraftPotionPerk
endif
Quest rNQ07 = Game.GetFormFromFile(0x1CA09, "Enderal - Forgotten Stories.esm") as Quest
if rNQ07.GetCurrentStageID() == 115
PlayerREF.AddPerk(Game.GetFormFromFile(0x14CF7, "Enderal - Forgotten Stories.esm") as Perk) ; _00E_FS_NQ07_CraftPotionPerk
endif
EndFunction
;=====================================================================================
; ALL UPDATES
;=====================================================================================
@ -259,28 +149,14 @@ endfunction
Function Maintenance()
if fPatchVersion <= 1.62
if fPatchVersion <= 2.13
Debug.MessageBox("A savegame was loaded which was made before the release of Enderal SE. In this save, several new features won't be available and there is a chance that you'll encounter grave bugs. Please, start a new game.")
Game.QuitToMainMenu()
return
;Game.QuitToMainMenu()
;return
endif
if fPatchVersion < CURRENT_PATCH_VERSION
If fPatchVersion < 2.06
UpdateMQ12b_206()
EndIf
If fPatchVersion < 2.08
Update_208()
EndIf
If fPatchVersion < 2.10
Update_210()
EndIf
If fPatchVersion < 2.12
Update_212()
EndIf
If fPatchVersion < 2.121
Update_212_hotfix1()
EndIf
;
fPatchVersion = CURRENT_PATCH_VERSION
endif

View File

@ -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
; Next
i = i + 1
endWhile
;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
;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
function QueueAdditionalSpawns()
VanillaLoopBreak()
if (!isSpawning && iSpawnedCritterCount < iMaxCritterCount*10)
UnregisterForUpdate()
RegisterForSingleUpdate(1+ (1+iSpawnedCritterCount) * iRespawnDelay )
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]
Event OnCritterDied()
VanillaLoopBreak()
if iDeadCritterCount < iSpawnedCritterCount
iDeadCritterCount += 1
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);
endif
; a critter died, if we're not currently spawning, try to refresh the queue or push back the delay
QueueAdditionalSpawns()
endEvent
; 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
else
; debug.trace("("+self+") SpawnCritter() failed because iCurrentCritterCount is "+iCurrentCritterCount)
; ;debug.trace("("+self+") iMaxCritterCount: "+iMaxCritterCount)
endif
return False ; [USKP 1.3.1] to help eliminate log spam and potential long term save bloating.
endFunction
; Spawns one Critter at a specific location
; returns true on success [USKP 2.0.1]
;
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 == 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
; Create the critter and cast it to the critter base class
ObjectReference critterRef = arSpawnRef.PlaceAtMe(critterType, 1, false, true)
Critter thecritter = critterRef as Critter
if thecritter == none
; STEVE40+USKP extra check
return false
endFunction
float Function GetPlayerDistance() ; Caches player reference too
if VanillaLoopBreak()
return fMaxPlayerDistance + 1
endif
if !PlayerRef
PlayerRef = Game.GetPlayer()
endif
return PlayerRef.GetDistance(self)
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
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
return IsActiveTime() && CustomCheck()
else
shouldTryAgain = false
return false
endif
endFunction
; Set initial variables on the critter
; ; Debug.TraceConditional("Spawner " + self + " is creating Critter " + thecritter, bPrintDebug);
thecritter.SetInitialSpawnerProperties(fLeashLength, fLeashHeight, fLeashDepth, fMaxPlayerDistance + fLeashLength, self)
function SetExtraWaitTime(float t)
extraWaitTime = t
endFunction
function ClearExtraWaitTime()
extraWaitTime = 0
endFunction
; Increment count [USKP 2.0.1]
iCurrentCritterCount += 1
;Custom method to override in custom scripts
bool Function CustomCheck()
return true
endFunction
; Utility method that returns the player's distance
float Function GetPlayerDistance()
return Game.GetPlayer().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)
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
bool Function IsActiveTime()
if VanillaLoopBreak()
return false
endif
endFunction
bool Function IsActiveTime()
bool binTimeRange = false
; 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
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
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))
endFunction