Jump to content

[LE] Throttling OnHit scripts


lesi123

Recommended Posts

Sorry I haven't kept a very close tabs on this thread since my last post but I think I found a workaround with the script after lots (and lots...) of debug notifications.

 

 

  Reveal hidden contents

 

 

My thought was by adding a Utility.Wait(X) before the script is to head back into the Ready state, the script would be forced to chill out in the busy state until he "cooldown" was up. I just got finished a fight that dang sun dragon for 20 minutes and then a 5 minute fight with a pack of 5 wolves; bleeds were being applied appropriately and no errors or stack dumps showed up in the log from the session.

 

ReDragon, thank you very much for the rewrite! I'll have to read through it, I'm still not the best at scripting and it takes awhile to grasp things. I'll have to do more testing later though, work comes early in the morning. I'll update this topic if I have anything else to report.

 

Again, thank you everyone for your help!

Link to comment
Share on other sites

  • Replies 67
  • Created
  • Last Reply

Top Posters In This Topic

  On 12/20/2016 at 4:24 AM, cdcooley said:

If you get a stack dump I would expect one or two instances of the script to appear somewhere, the problem is that you seem to have more than that. Does the scripting system recover after the combat is over? Stack dumps aren't inherently a critical problem, they just mean the scripting system isn't keeping up with all of the action. Your mod is adding more scripting activity to an already heavy load and you're trying to stress it out so it does make sense you can trigger them.

 

Maybe the problem is related to the fact that you're casting a spell on the player (which has its own hit effect) and under heavy load the game isn't getting that fully processed until after the state has changed, but even if that were the case there shouldn't be a way for multiple cases to stack up. There's also a chance you've got some sort of locking problem which is preventing scripts from completing. If the scripting system eventually stops applying the effect and then doesn't recover after combat is over that would be the most likely problem.

 

Since I was fighting a single sun dragon and the only real mods I have are my mod and Deadly Dragons, I wouldn't have thought the system would overload as much as it did. That, and the dragon was part of the DragonRace, I had expected the script to stop at the (Aggressor.GetRace() != DragonRace) on the first line. I'm seriously stumped on that one.

Link to comment
Share on other sites

  On 12/20/2016 at 4:29 AM, lesi123 said:

 

My thought was by adding a Utility.Wait(X) before the script is to head back into the Ready state, the script would be forced to chill out in the busy state until he "cooldown" was up. I just got finished a fight that dang sun dragon for 20 minutes and then a 5 minute fight with a pack of 5 wolves; bleeds were being applied appropriately and no errors or stack dumps showed up in the log from the session.

 

 

The utility wait is a good idea. Did you notice RedDragon did not have the event active in the Busy state? That is a complicated script of Red Dragons, I would like to know what the outcome is should you choose to try it.

 

Please also consider other options for what you are doing here. Placing a spell or perk on the NPCs would be less taxing if you are able to do it.

 

BTW How can your script be working with animals when you shut it down if the hit did not come from a weapon??

if (akAggressor != None) && (akSource as Weapon) && (abHitBlocked != true) && (Aggressor.GetRace() != DragonRace) && !(akSource as Spell)
Edited by Masterofnet
Link to comment
Share on other sites

  On 12/20/2016 at 4:55 AM, Masterofnet said:

 

The utility wait is a good idea. Did you notice RedDragon did not have the event active in the Busy state? That is a complicated script of Red Dragons, I would like to know what the outcome is should you choose to try it.

 

Please also consider other options for what you are doing here. Placing a spell or perk on the NPCs would be less taxing if you are able to do it.

 

BTW How can your script be working with animals when you shut it down if the hit did not come from a weapon??

if (akAggressor != None) && (akSource as Weapon) && (abHitBlocked != true) && (Aggressor.GetRace() != DragonRace) && !(akSource as Spell)

 

I had added in the OnHit event inside the Busy state so I could fire a debug notification to see how many times the event was firing while in that state. Without the utility.wait, I never saw my message fire from the Busy state during a fight.

 

I've tried different methods of accomplishing what I wanted over the past few years (abilities, perks, spells), but many would conflict with other mods and I wanted to avoid that as much as possible. Also, since this script is attached to a quest on the player, it can be turned on or off which I find very nice.

 

Most sources of damage that hit the player returns as a weapon so I had to get more specific with keywords. That's why I had to take out dragons and hagravens from the script because dragon breaths and hagraven firebolt spells were returning as weapons and applying bleeds to the player.

 

I used these in my OnHit events for testing:

debug.notification("Attacker is" + akAggressor)
debug.notification("Source is" + akSource)

I don't get it, but I'll roll with the punches.

Edited by lesi123
Link to comment
Share on other sites

  On 12/20/2016 at 3:44 AM, ReDragon2013 said:

I do not want to interrupt your disput about states and flages, but sometimes it is a good idea to look at Bethesda wiki

http://www.creationkit.com/index.php?title=States_(Papyrus)

 

cdcooley, I do not hate states. Please edit your posting.

My comments on the relative merits of states and flags for optimizing Papyrus scripts are based on comments from a developer of the system.

 

SmkViper has made multiple posts on the topic over the years, here's one of the most recent.

  On 10/6/2015 at 5:52 PM, SmkViper said:

Please be careful when using OnHit - especially with a spell like flames. Flames hits you very, very quickly and can easily overwhelm your script without using states to control it.

 

Example:

Event OnHit(...)
GotoState("ProcessingHit")
; Do stuff
GotoState("")
EndEvent

State ProcessingHit
Event OnHit(...)
; make sure this is empty - comments are ok, anything else isn't
EndEvent
EndState

Unfortunately it's very difficult to find older postings on the Bethesda forums or I would quote one of his earlier postings where he explained how the state system optimizes access in a way that flag variables can't.

 

Optimizing with flag variables and early return statements was the technique we used in the old scripting language from previous games and it worked very well there, but Papyrus is internally structured very differently and different optimization techniques are needed. Flags are certainly still useful but not for protecting from script overload for the OnHit or inventory events.

Link to comment
Share on other sites

Most animals do unarmed damage and it is hard to believe that a spell would return as a weapon.

Also you are shutting the script down if the damage comes from a spell so I don't see how a Shout or Spell from a Hagraven would work.

 

However I have not tested that on an OnHit event.

 

I had a go at the script myself. It restricts your ability to apply the chance of bleed damage based on the attacker a little bit but I do not think it would be noticeable in game.

 

 

 

  Reveal hidden contents

 

Edited by Masterofnet
Link to comment
Share on other sites

Hi cdcooley, I am far away to know all about papyrus scripting, but during the last two years I learned a lot about it.

Let me say thank you for your always good postings.

  On 12/20/2016 at 2:23 PM, cdcooley said:

My comments on the relative merits of states and flags for optimizing Papyrus scripts are based on comments from a developer of the system.

SmkViper has made multiple posts on the topic over the years, here's one of the most recent.

 

SmkViper, on 06 Oct 2015 - 6:52 PM, said:
Please be careful when using OnHit - especially with a spell like flames. Flames hits you very, very quickly and can easily overwhelm your script without using states to control it.

  Reveal hidden contents


you wrote: "Flags are certainly still useful but not for protecting from script overload for the OnHit or inventory events."

 

That is right and the reason why some events should be protected by states, especially OnHit(). But if you would like remember the initial posting here was written about functions.

Functions and events are different. An event may trigger always and that can be very quickly. A function wont do that, she has to be called by another function or from event.

 

What is the point, events need states to prevent stack overload, functions are fine with a global boolVar as thread lock.

 

Cheers..

Link to comment
Share on other sites

Masterofnet wrote: "I do not know how this script is going to fire on animal hits when it is shut down if the hit does not come from a weapon???"

I wrote as code description: "all conditions above are thread safe". Maybe you do not noticed it.
My conditions are optimized in order and using of temp stack variables.

next code is equal in result

IF !(akSource as Weapon)
    RETURN    ; - STOP -
ENDIF

as this script code

IF (akSource as Weapon)
ELSE
    RETURN    ; - STOP -
ENDIF

Masterofnet crippled OnHit() version:

  Reveal hidden contents



I do not like to write this down, but you crippled my script version a lot. Because you made it much more unreadable.
The use of so many "gotostate" code lines is imho bad script design and the result of your script rewriting.
I always try to prevent gotostate inside functions, unfortunately not possible ever.

my original hit event version:

  Reveal hidden contents

 

Edited by ReDragon2013
Link to comment
Share on other sites

  On 12/20/2016 at 6:37 PM, ReDragon2013 said:

Masterofnet wrote: "I do not know how this script is going to fire on animal hits when it is shut down if the hit does not come from a weapon???"

 

I wrote as code description: "all conditions above are thread safe". Maybe you do not noticed it.

My conditions are optimized in order and using of temp stack variables.

 

 

Your script very clearly stated "No Weapon No Blood". It looks to me like if your script detects a hit "not" made by a Weapon that it will not fire the functions. What does your script do if it detects "no weapon"?

 

  On 12/20/2016 at 6:37 PM, ReDragon2013 said:

I do not like to write this down, but you crippled my script version a lot. Because you made it much more unreadable.

The use of so many "gotostate" code lines is imho bad script design and the result of your script rewriting.

I always try to prevent gotostate inside functions, unfortunately not possible ever.

 

You mean at the end of your script? Ya I had a feeling you were not going to like that. I have never used " Return" the way you did so I was not sure how that would effect the placement of the "GoToStates".

 

Also I do not see where you apply the spell to the player. I think that would be the place to insert gotostate"ready".

;-------------------------------------------
Spell FUNCTION myF_GetSX(Int i, Int iChance)        ; xLarge
;-------------------------------------------
IF (i >= iChance)
    RETURN BTDSpellCREATUREXLargetoPLAYERBleedDmg ; What exatally will "RETURN" do here??  Will the script continue to run or will it "Quit"?
ENDIF
    RETURN None ; What exatally will "RETURN" do here??  Will the script continue to run or will it "Quit"?
ENDFUNCTION

Your script fires the Busy State and then immediately fires the Ready State. Why bother with the Busy State?

Also this is a OnHit event. An OnHIt event that performs this many functions needs to be shut down until all of the functions complete. It does not look like your script is doing that.

 

What do you think of my version? Is it easy enough for you to read? Hopefully its not "crippled".

 

 

  Reveal hidden contents

 

Edited by Masterofnet
Link to comment
Share on other sites

Dear Masterofnet,

please be a bit more shy, and keep in mind my English skill is not as good as yours.

 

I admire your confidence to your script proposal, but what I have to say is: "Sorry, it is a mess." Let me explain why?

1) the script has syntax errors
;--------------------------------------------------------------------------------------------------------------------------------
; above is code line 6: ..\BTDCREATUREtoPLAYERDmg.psc(6,0): missing EOF at '-'
; you forget semicolon

;int i = Utility.RandomInt() ; = Utility.RandomInt(0,100)
; above is code line 25: ..\BTDCREATUREtoPLAYERDmg.psc(25,8): no viable alternative at input 'Utility'
; ..\BTDCREATUREtoPLAYERDmg.psc(25,15): required (...)+ loop did not match anything at input '.'
; ..\BTDCREATUREtoPLAYERDmg.psc(25,4): Unknown user flag Utility
; you cannot use papyrus code on property or variable declaration, the code line has to be inside a function or event !!!


2) you have a logical error in the main condition:

you took this:

  Reveal hidden contents



we could break it up into two conditions, which will have the same result like yours:

  Reveal hidden contents




to make it right you would have to use next conditions:

  Reveal hidden contents



Have you seen the different?
Note: You forgot to check, is the overgiven objectReference akAggressor a valid Actor.


3) Regardless of the things form above, you implemented formlists to make your code better looking. bad decision..
You have to create the formlists with the CreationKit. If you make something wrong here it is hard to analyze.
In case you want to change the chance of blood by a special race, what do you do?

The race conditions (I have used) are much better to handle and I use the native function GetFormID() with modulo like that:

int x = r.GetFormID()
    x = x % 0x00100000

to prevent of binding the mod file (*.esp) to other masterfiles than Skyrim.esm and Update.esm.
If you use formlists you have to make sure DLC1 and DLC2 have to be loaded with your mod.


4) In case the conditions above would be ok, your code runs through all of the four formlists.

    Actor Attacker = (AkAggressor As Actor)
    Race R = Attacker.GetRace()

    Int SI = SmallAttacker.Find(R)
    Int MI = MediumAttacker.Find(R)
    Int LI = LargeAttacker.Find(R)
    Int XI = XLargeAttacker.Find(R)

This is wasting of processor time and could be surely improved. But do you remember my meaning about formlist, it doesn't make sense for me to do that.


5) Let us assume we forget all the things above, we should look at the next code:

; here would be the right place inside your script version
    int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)

    If (BleedUm) && (i >= iChance)
         BleedUm.Cast((Self.GetReference() As Actor),(Self.GetReference() As Actor))  
    EndIf

The native method "cast" needs always objectReference as parameter, so you should use that instead

    If (BleedUm) && (i >= iChance)
        objectReference oRef = self.GetReference()        ; oRef could have any name you like
        BleedUm.Cast(oRef, oRef)  
    EndIf

6) And finally a comparing of stackVars:

your version without functions:

  Reveal hidden contents



my version with functions to divide the code:

  Reveal hidden contents

 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...