PAPVAFS11 Posted August 6, 2023 Share Posted August 6, 2023 Event OnTimer(Int iTimer) if iTimer == 1 DoRepeatingTimerStuff() if akOurActor == Game.GetPlayer() || akActor.HasKeyword(ImportantActorNeedsFastTimer) StartTimer(0.1,1) else StartTimer(Utility.RandomFloat(0.5,1.0),1) endif endif EndEvent The intent of this is for repeating timers assigned to many objects to not all tick at once, and to give some headroom for them to finish what they're doing before the timer ticks again. I ask this question as I don't know how expensive RandomFloat() is. That also leads into this example: Event OnSomethingHappened(Actor akHappener) if akHappener != Game.GetPlayer() Utility.Wait(Utility.RandomFloat(0.1,0.5)) endif DoEventStuff() EndEvent Again, in the case that OnSomethingHappened() is called by many simultaneous sources, the Wait() is called with a RandomFloat() to nudge them into happening at different times. Do these work as I want them to? I don't have anything I need them for, but I'd like to work them into habit if they're useful. Link to comment Share on other sites More sharing options...
SKKmods Posted August 6, 2023 Share Posted August 6, 2023 Depends how they are used. I would not publish a solution with a timer constantly firing every second, but my compute cost and quality aspirations may be different to yours. Utility.Random* takes one frame to execute so can be considered "expensive" compared to non frame bound functions. Utiltiy.Wait is not expensive but be aware that script will freeze/lock and receive no input (e.g. other events) during that wait period. If the script reacts to multiple events I would put any wait functions into a seperate standalone script and call it using (FormName as ScriptName).CallFunctionNoWait("WaitFunctionName", new var[0]) Link to comment Share on other sites More sharing options...
Fantafaust Posted August 6, 2023 Share Posted August 6, 2023 Answering this as a hypothetical question, I would say the answer is yes, this is a bad idea. Only because there's likely to be a better way to do what you want without the timer that would(seemingly) get repeated ad infinitum with no way to abort it. Link to comment Share on other sites More sharing options...
niston Posted August 6, 2023 Share Posted August 6, 2023 Using randomization to stretch out event processing is not a bad idea per se. And the fact that Utility.Random() takes a frame to complete is negligible in your application. What I can however tell you is that mods which spawn lots of fast timers (< 1.0s) tend to break the workshop system, for some very odd reason. The symptom is that workshop assignments are no longer possible in the game at all and the only known fix is to roll back the save to before the bug got triggered. Finally, it's always preferable (if possible) to react to events as they occur, instead of using timers and polling for something to happen or change. Even more so if high frequency polling is needed to detect/react. Link to comment Share on other sites More sharing options...
PAPVAFS11 Posted August 6, 2023 Author Share Posted August 6, 2023 (edited) I have several applications for looping short timers. I'm at a loss for how I would change them to be reactive instead of proactive, as several of them monitor for actor value changes, like STR, END, and AGI. For example, I have a speed penalty handler to clamp SpeedMult penalties. I'm also using this as a workaround for SpeedMult penalties on NPCs, as I couldn't get spell-driven SpeedMult changes to work on them. This interacts with both armor enchantments and conditional spells that add to or subtract from the speed penalty AV. Another script I wrote handles encumbrance, and that relies directly on SPECIAL values in the encumbrance calculation itself. I don't currently know of any events that trigger on AV changes or spell applications, so I wouldn't know how to break from timers. My playtesting showed me that delays greater than a second were also very clunky for the player, so I tried to keep it short. At the very least, I do have conditional checks inside the timers (IsDead(), GetValue() != fLastValue, etc.) to be sure they actually need to do something, or if they should stop where they are. NPC timers are also usually around 3.0-5.0 seconds. If there's any way I can improve these, I'm all ears. I can post the full scripts if need be. Edited August 6, 2023 by PAPVAFS11 Link to comment Share on other sites More sharing options...
RaidersClamoring Posted August 6, 2023 Share Posted August 6, 2023 Random might be expensive but you can prepare by filling up an array in advance, and it might not even be necessary to recalculate the randoms in that array if the constant remains the same. It would come down to player experience. You then simply cycle through the array and grab a prepared random instead of calculating a new one. Link to comment Share on other sites More sharing options...
LarannKiar Posted August 6, 2023 Share Posted August 6, 2023 (edited) I have several applications for looping short timers. I'm at a loss for how I would change them to be reactive instead of proactive, as several of them monitor for actor value changes, like STR, END, and AGI. For example, I have a speed penalty handler to clamp SpeedMult penalties. I'm also using this as a workaround for SpeedMult penalties on NPCs, as I couldn't get spell-driven SpeedMult changes to work on them. This interacts with both armor enchantments and conditional spells that add to or subtract from the speed penalty AV. Another script I wrote handles encumbrance, and that relies directly on SPECIAL values in the encumbrance calculation itself. I don't currently know of any events that trigger on AV changes or spell applications, so I wouldn't know how to break from timers. My playtesting showed me that delays greater than a second were also very clunky for the player, so I tried to keep it short. At the very least, I do have conditional checks inside the timers (IsDead(), GetValue() != fLastValue, etc.) to be sure they actually need to do something, or if they should stop where they are. NPC timers are also usually around 3.0-5.0 seconds. If there's any way I can improve these, I'm all ears. I can post the full scripts if need be. You can add these NPCs to a RefCollectionAlias and apply a Spell to them through the alias. The Spell's associated Magic Effect can be either a Value Modifier that changes the SpeedMult AV of GetTargetActor() directly (use the Detrimental flag to reduce value) or contain a script that GetTargetActor().SetValue(SpeedMult, NewValue) OnEffectStart() and OnEffectFinish(). And as a Spell can have more Magic Effects, you can set up the effects with conditions like: Effect 1) "GetValue 'SpeedMult' == 1 (Run on: Subject)"Effect 2) "GetValue 'SpeedMult' >= 2 (Run on: Subject)" This way, since an object reference script event like OnActorValueChanged() is not implemented in Papyrus, you request the engine to process the conditions. Edited August 6, 2023 by LarannKiar Link to comment Share on other sites More sharing options...
Fantafaust Posted August 7, 2023 Share Posted August 7, 2023 Another for SPECIAL stat changes would be registering for the perk menu closing and rechecking at that point, as well as registering for item equips and unequips and rechecking then.This would cover SPECIAL stat training, armor equips/removal, and at least initial food/Chem usage Link to comment Share on other sites More sharing options...
Recommended Posts