Jump to content

[LE] Throttling OnHit scripts


lesi123

Recommended Posts

  • Replies 67
  • Created
  • Last Reply

Top Posters In This Topic

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?

 

 

No it is not a mess. Or even close to one. It has some small syntax errors because I wrote in in about 10 mins and did not compile it in the kit. This is a basic outline of a script for someone else to use.

 

 

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

 

you took this:

 

IF (( abHitBlocked ) || (akSource as Spell)) || (!(Attacker.HasKeyword(ActorTypeDwarven)) && !(Attacker.HasKeyword(ActorTypeAnimal))&& !(Attacker.HasKeyword(ActorTypeCreature)))   
    gotoState("Ready")
else
    ; formlist action here
    gotoState("Ready")
endif

 

 

 

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

 

IF (abHitBlocked) || (akSource as Spell)
    ; this is ok
    gotoState("Ready")

ELSEIF !Attacker.HasKeyword(ActorTypeDwarven) && !Attacker.HasKeyword(ActorTypeAnimal) && !Attacker.HasKeyword(ActorTypeCreature)
    ; this is wrong!
    gotoState("Ready")

else
    ; formlist action here
    gotoState("Ready")
endif

 

 

 

 

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

 

IF (abHitBlocked) || (akSource as Spell) || !Attacker
    gotoState("Ready")

ELSEIF Attacker.HasKeyword(ActorTypeCreature) || Attacker.HasKeyword(ActorTypeAnimal) || Attacker.HasKeyword(ActorTypeDwarven)
    ; formlist action here
    gotoState("Ready")
ENDIF

 

 

 

Have you seen the different?

Note: You forgot to check, is the overgiven objectReference akAggressor a valid Actor.

 

 

 

I do not need to verify if the akattacker is a valid actor.

 

As you can see if the Akaggressor does not have those keywords the script will stop. Only a valid actor will have those keywords.

 

Also if the attacker had any of those keyword you would not go to state "ready".

 

This is a basic outline of a script I did for someone else. The fine details and testing will need to be done by them.

 

 

 

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?

 

 

 

No with this many races I would always use a formlist or an array and no the problem would be very easy to track down. Yes you make the formlist so the processor does not have to do it every time you run the script.

 

 

 

In case you want to change the chance of blood by a special race, what do you do?

 

 

You can not see? To change the chance of blood you simply adjust the the script.

 

 

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.

 

 

 

Yes you must have the DLCs installed they would be a "requirement."

 

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.

 

 

You are correct. That could be better optimized. But I don' t think the results would be significant.

 

Have a look at my new "ReDragon" Inspired attempt!

 

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)

 

 

 

You are correct again. However I did not look up the function in the Wiki and wanted to be sure it would work. Fully optimizing the script would be the responsibility of the user if they chose to use it.

 

6) And finally a comparing of stackVars:

 

 

Actually I like what I saw with my script. ReDragon you need to keep in mind how the script is going to function in game.

 

Also something you refuse to address is the fact your script does not shut down the OnHit event until all of the functions are completed. - ( This has been confirmed to be incorrect by ReDragon. The function completes before the State is set back to ready.)

 

BTW I have been considering adding you to my friends list. Don't get your hopes up to high but it is fair for me to tell you that you are in serious consideration!!! :thumbsup:

 

I have updated my script because of your "Criticisms" have a look!!

 

 

 

Scriptname Perfection extends ReferenceAlias
 
{rewritten by Big Man 2016 Inspired By ReDragon}    ; handles creature to Player bleed damage
 
; source: Big Man - ReDragon
 
  Spell PROPERTY SmallBleed auto
  Spell PROPERTY MediumBleed auto
  Spell PROPERTY LargeBleed auto
  Spell PROPERTY XLargeBleed auto
 
Keyword PROPERTY ActorTypeAnimal   auto
Keyword PROPERTY ActorTypeCreature auto
Keyword PROPERTY ActorTypeDwarven  auto
 
FormList Property SmallAttacker auto
FormList Property MediumAttacker auto
FormList Property LargeAttacker auto
FormList Property XLargeAttacker auto
 
Int iChance
 
Auto State Ready     ; HIT REGISTERED ON PLAYER
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
 
gotoState("Busy")
 
IF  abHitBlocked || (akSource as Spell)
 
gotoState("Ready")
  
ElseIf !akAggressor.HasKeyword(ActorTypeDwarven) && !akAggressor.HasKeyword(ActorTypeAnimal) && !akAggressor.HasKeyword(ActorTypeCreature)
 
gotoState("Ready")
 
Else
 
Race R = (AkAggressor As Actor).GetRace()
 
    Int SI = SmallAttacker.Find(R)
    If SI > -1 
        SAF(SI)
    Else
    Int MI = MediumAttacker.Find(R)
    If  MI > -1
        MAF(MI)
    Else
    Int LI = LargeAttacker.Find(R)
    If  LI > -1
        LAF(LI)
    Else
    Int XI = XLargeAttacker.Find(R) 
    If XI > -1
        XAF(XI)
    Else
        gotoState("Ready")
 
    EndIf
    EndIf
    EndIf
    EndIf
EndIf
EndEvent
EndState
 
Function SAF(Int SI)
int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)
If SI <= 3
    iChance = 40
ElseIf SI <= 6
    iChance = 60
ElseIf SI <= 9
    iChance = 80
EndIf 
    If(i >= iChance)
        SmallBleed.Cast(Self.GetReference(),Self.GetReference())
    EndIf
        gotoState("Ready")
EndFunction
 
 Function MAF(Int MI)
int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)
If MI <= 3
    iChance = 40
ElseIf MI <= 6
    iChance = 60
ElseIf MI <= 9
    iChance = 80
EndIf
    If (i >= iChance)
        MediumBleed.Cast(Self.GetReference(),Self.GetReference())
    EndIf
        gotoState("Ready")
EndFunction
 
 Function LAF(Int LI)
int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)
If LI <= 3
    iChance = 40
ElseIf LI <= 6
    iChance = 60
ElseIf LI <= 9
    iChance = 80
EndIf
    If (i >= iChance)
        LargeBleed.Cast(Self.GetReference(),Self.GetReference())
    EndIf
        gotoState("Ready")
EndFunction
 
Function XAF(Int XI)
int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)
If XI <= 3
    iChance = 40
ElseIf XI <= 6
    iChance = 60
ElseIf XI <= 9
    iChance = 80
EndIf
    If (i >= iChance)
        XLargeBleed.Cast(Self.GetReference(),Self.GetReference())
    EndIf
        gotoState("Ready")
EndFunction
 
 
State Busy
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
; Nothing
EndEvent
EndState

 

 

 

Also because I have learned so much from you I helped you with your main condition script.

 

 

 

EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)

If (akSource as Spell); This is way more likely to happen than hit blocked so it should go first. Also you do not need to check for weapon. The hit from these aggressors will be either spell or melee, you have just checked spell. 
RETURN ; - STOP - /1 spells are skipped
ElseIF ( abHitBlocked ) 
RETURN ; - STOP - /2 hit was blocked
ElseIf (akAggressor as Actor)
Else
    RETURN    ; - STOP - /3    no actor, no blood
EndIf
    gotoState("Busy")             ; ### STATE ### do not run this function again until it has finished

    PlayerHitBy(akAggressor as Actor)
        
    gotoState("Ready")             ; ### STATE ### function has finished back to auto state, it can be called again
ENDEVENT
;=======
endState 

 

 

 

Keep up the good work!! :devil:

Edited by Masterofnet
Link to comment
Share on other sites

Here's my script that I have been testing:


	Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)

		GoToState("Busy") ; Don't run this function again until it's finished
		Aggressor = akAggressor as Actor

		if (akAggressor != None) && (akSource as Weapon) && (abHitBlocked != true) && (Aggressor.GetRace() != DragonRace) && !(akSource as Spell)
			if (Aggressor.HasKeyword(ActorTypeAnimal))
				PlayerHitByAnimal(Aggressor)
			elseif (Aggressor.HasKeyword(ActorTypeCreature))
				PlayerHitByCreature(Aggressor)
			elseif (Aggressor.HasKeyword(ActorTypeDwarven))
				PlayerHitByDwarven(Aggressor)
			endif
		endif

 

The bool will return haskeyword on actor. I found the answer.

Edited by Masterofnet
Link to comment
Share on other sites

This is a new script using only one formlist. I like it the best and it can set any race to any spell and chance of bleeding damage.

 

 

 

Scriptname OneFormList extends ReferenceAlias

{rewritten by MON 2016}    ; handles creature to Player bleed damage

; source: MON - ReDragon

  Spell PROPERTY SmallBleed auto
  Spell PROPERTY MediumBleed auto
  Spell PROPERTY LargeBleed auto
  Spell PROPERTY XLargeBleed auto

FormList Property AttackerRace auto

Auto State Ready     ; HIT REGISTERED ON PLAYER
EVENT OnHit(ObjectReference A, Form S, Projectile P, Bool B1, Bool B2, Bool B3, Bool B4)

If (A As Actor)
Else
 Return
EndIf

Int AR = AttackerRace.Find((A As Actor).GetRace())

If AR < 0
 Return
EndIf

If (S As Spell)
 Return
EndIf

If B4 ;Hit Blocked
 Return
EndIf

gotoState("Busy")

int i = Utility.RandomInt() ; = Utility.RandomInt(0,100)
Int Ic ;Chance of Bleed Damage
Spell Sp

    If AR == 0
     Sp = SmallBleed
     Ic = 30
    ElseIf AR <= 2   
     Sp = SmallBleed
     Ic = 70
    ElseIf AR <= 8 
     Sp = MediumBleed
     Ic = 50
    ElseIf AR <= 10   
     Sp = MediumBleed
     Ic = 60
    ElseIf AR <= 12   
     Sp = LargeBleed
     Ic = 40
    ElseIf AR <= 19 
     Sp = LargeBleed
     Ic = 50
    ElseIf AR <= 22   
     Sp = LargeBleed
     Ic = 60
    ElseIf AR <= 24 
     Sp = XLargeBleed
     Ic = 10
    ElseIf AR <= 25 
     Sp = XLargeBleed
     Ic = 60
    EndIf

    If Sp && (i >= Ic)
     objectReference Pl = self.GetReference()
     sp.Cast(Pl, Pl)
    EndIf

gotoState("Ready")   

EndEvent
EndState

State Busy
 EVENT OnHit(ObjectReference A, Form S, Projectile P, Bool B1, Bool B2, Bool B3, Bool B4)
 EndEvent
EndState

;=============================================
; FormList
;=============================================
;0 SlaughterfishRace  - Small - 30
;1 DLC1DeathHoundRace - Small - 70
;2 WolfRace - Small - 70
;=============================================
;3 SabreCatSnowyRace - Medium - 50
;4 DLC1SabreCatGlowRace - Medium - 50
;5 BearBlackRace - Medium - 50
;6 BearSnowRace - Medium - 50
;7 IceWraithRace - Medium - 50
;8 DLC2dunKarstaagIceWraithRace - Medium - 50
;9 BearBrownRace - Medium - 60
;10 SabreCatRace - Medium - 60
;=============================================
;11 SprigganEarthMotherRace - Large - 40
;12 DLC1GargoyleVariantBossRace - Large - 40
;13 DLC1GargoyleVariantGreenRace - Large - 50
;14 DLC2WerebearBeastRace - Large - 50
;15 DwarvenSphereRace - Large - 50
;16 TrollFrostRace - Large - 50
;17 DLC1GargoyleRace - Large - 50
;18 WerewolfBeastRace - Large - 50
;19 SprigganMatronRace - Large - 50
;20 SprigganRace - Large - 60
;21 DLC2SprigganBurntRace - Large - 60
;22 TrollRace - Large - 60
;=============================================
;23 DLC1LD_ForgemasterRace - XLarge - 10
;24 DLC2DwarvenBallistaRace - XLarge - 10
;25 DwarvenCenturionRace - XLarge - 20 

 

 

Edited by Masterofnet
Link to comment
Share on other sites

I had a look into your last script approach with one formlist. What I can say: "much better",

but it can be performed into one formlist of races and one integer array of chances.

 

Here is my rewrite, now I renamed the script proposal to PlayerAliasSampleHitScript.

 

 

 

Scriptname PlayerAliasSampleHitScript extends ReferenceAlias
{rewritten by ReDragon 2016}    ; handles creature to Player bleed damage

; original source: OneFormList made by Big Man
; https://forums.nexusmods.com/index.php?/topic/5174600-throttling-onhit-scripts/page-6

  Spell PROPERTY SmallBleed  auto
  Spell PROPERTY MediumBleed auto
  Spell PROPERTY LargeBleed  auto
  Spell PROPERTY XLargeBleed auto

 ;Keyword PROPERTY ActorTypeAnimal   auto    ; UnUSED by now
 ;Keyword PROPERTY ActorTypeCreature auto    ; UnUSED
 ;Keyword PROPERTY ActorTypeDwarven  auto    ; UnUSED

; formlist and array have to be of the same size
; ----------------------------------------------
  FormList PROPERTY AttackerRace auto        ; list of races you like to see as attacker to make player bleeding
  Int[]    PROPERTY AttackChance auto        ; array of chances
  ; How to use!    base value + chance
  ; small  bleed =   0
  ; medium bleed = 100
  ; large  bleed = 200
  ; xlarge bleed = 300

; sample for entry one is wolf it has a chance of 40 to cast the spell, the bleeding spell should be small
  ;i = AttackChance[0] = 40

; sample for entry 5 which could be a sabrecat with a chance of 60, bleeding spell is large
  ; i = AttackChance[4] = 260       ; edit: 23/Dec/2016
 

; -- FUNCTION --

FUNCTION myF_Action(Actor aRef)
;------------------------------
    int n = AttackerRace.Find( aRef.GetRace() )        ; try to find a match up with races

IF (n < 0)
    RETURN    ; - STOP -    race not found inside our formlist, abort function!
ENDIF
;---------------------
IF (AttackerRace.GetSize() == AttackChance.Length)
ELSE
    RETURN    ; - STOP -    formlist and integer array do not match in size
ENDIF
;---------------------
    int i       = Utility.RandomInt(0, 100)            ; "int i = Utility.RandomInt()" is the same code
    int iChance = AttackChance[n]                    ; get the chance of using for the bleeding spell

spell sp
; ------------------------
    IF     (iChance < 100)
        sp = SmallBleed            ; small

    ELSEIF (iChance < 200)
        sp = MediumBleed           ; medium

    ELSEIF (iChance < 300)
        sp = LargeBleed            ; large

    ELSE
        sp = XLargeBleed           ; xlarge
    ENDIF
; -----------------------
    WHILE (iChance > 100)
        iChance = iChance - 100            ; update the value from our integer formlist until we are in range (0..100)
    ENDWHILE

    IF (i >= iChance)
        objectReference oRef = self.GetReference()
        sp.Cast(oRef, oRef)
    ENDIF
ENDFUNCTION


; -- EVENTs --

EVENT OnDying(Actor akKiller)
    gotoState("Done")                ; ### STATE ###
    Debug.Trace("PlayerTest: OnDying() - has been reached.. " +self)    ; player is near of dead
ENDEVENT


;===============================
State Done    ; player is dead
;=========
EndState

;===============================
State Busy    ; player was hit, test for aggressor  race and bleeding spell chance
;=========
EndState

;===============================
Auto State Ready     ; HIT REGISTERED ON PLAYER
;===============
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool b1, Bool b2, Bool b3, Bool b4)
IF (akAggressor as Actor)
ELSE
    RETURN    ; - STOP -    no actor, no blood
ENDIF

IF ( b4 )                                        ; b4 = abHitBlocked
    RETURN    ; - STOP -    hit was blocked
ENDIF

IF (akSource as Spell)
    RETURN    ; - STOP -    hit was made as spell damage
ENDIF

IF ( akProjectile )
    RETURN    ; - STOP -    projectiles like arrows are not allowed, if you like to have such a special test
ENDIF

;IF (akSource as Weapon)
;ELSE
;    RETURN    ; - STOP -    hit was not made by melee or spell (tested before)
;ENDIF
;=====================
    gotoState("Busy")                    ; ### STATE ###
    myF_Action(akAggressor as Actor)

IF (self.GetState() == "Busy")
    gotoState("Ready")                    ; ### STATE ###
ENDIF
ENDEVENT
;=======
EndState


;/ *************************************
  event OnHit
  .return NONE
     .paramTable
       .param akAggressor ObjectReference
       .param akSource Form
       .param akProjectile Projectile
       .param b1 Bool
       .param b2 Bool
       .param b3 Bool
       .param b4 Bool
     .endParamTable
     .localTable
        .local ::temp8 actor
        .local ::temp9 spell
        .local ::nonevar none
        .local ::temp10 string
        .local ::temp11 bool
     .endLocalTable

  function myF_Action
  .return NONE
     .paramTable
        .param aRef Actor
     .endParamTable
     .localTable
        .local ::temp0 race
        .local ::temp1 form
        .local ::temp2 int
        .local ::temp3 bool
        .local ::temp4 int
        .local ::temp5 bool
        .local n int
        .local i int
        .local iChance int
        .local sp spell
        .local ::temp6 objectreference
        .local ::nonevar none
        .local oRef objectReference
     .endLocalTable
 ****************************************** /;

 

 

Edited by ReDragon2013
Link to comment
Share on other sites

Just for info:

bad coding (unfortunately Bethesda game studio scripter prefer that for objectReferences)

if (akAggressor != None)

better

if ( akAggressor )

bad coding (second part)

if (abHitBlocked != true)

better

if (abHitBlocked == false)

much better

if !abHitBlocked
  ; code here
endif

or if possible, you can use that construct to shrink code size

if abHitBlocked
else
  ; code here
endif
Edited by ReDragon2013
Link to comment
Share on other sites

I had a look into your last script approach with one formlist. What I can say: "much better",

but it can be performed into one formlist of races and one integer array of chances.

 

Here is my rewrite, now I renamed the script proposal to PlayerAliasSampleHitScript.

 

The "Projectile akProjectile" function of the OnHit Event, does not work. It will return "none" every time.

 

;Keyword PROPERTY ActorTypeDwarven auto ; UnUSED I agree.

 

Cool script. Simple and I am willing to bet very fast. I have updated your examples to make them a little easier to understand. I have also updated my One FormList Script.

 

 

 

  ; How to use!    base value + chance
  ; small  bleed =   0
  ; medium bleed = 100
  ; large  bleed = 200
  ; xlarge bleed = 300

; sample for entry 1 is wolf it has a chance of 40 to cast the spell, the bleeding spell should be small
;i = AttackChance[0] = 40

; sample for entry 2 is a Medium Race has a chance of 80 to cast the spell, the bleeding spell should be Medium
;i = AttackChance[1] = 180

; sample for entry 5 which could be a sabrecat with a chance of 60, bleeding spell is large
; i = AttackChance[4] = 240 ; This should be 260?

; sample for entry 7 is a XLarge Race has a chance of 30 to cast the spell, the bleeding spell should be XLarge
;i = AttackChance[6] = 330

 

 

 

 

It has been now 4 months since I have done any scripting and testing so I am a bit rusty. I apologize if this is a dumb question.

 

You gotoState("Busy") start your function, then go right back to gotoState("Ready"). Will your function fully complete before the code sets the Script State back to "Ready" ?

;=====================
    gotoState("Busy")                    ; ### STATE ###

    myF_Action(akAggressor as Actor) 

IF (self.GetState() == "Busy")
    gotoState("Ready") ; Will the myF_Action Function complete before this is triggered? 
ENDIF
ENDEVENT
;=======
Edited by Masterofnet
Link to comment
Share on other sites

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.

 

Please have a look at the last script from ReDragon and the last one I came up with. ReDragon is 100% correct that you are not going to stop having issues with your script until you get it fixed.

 

There is "Throttling OnHit Events" and there is trying to "Throttle" yours.

 

This is the coolest thread I have seen in a long time. ReDragon has put on a Payprus clinic.

Edited by Masterofnet
Link to comment
Share on other sites

You asked me about states:

Bool bDead        ; global BoolVar (heap) [Default=False]

EVENT OnDying(Actor akKiller)        ; player is near of dead
    bDead = TRUE
    gotoState("Done")                ; ### STATE ###
    Debug.Trace("PlayerTest: OnDying() - has been reached.. " +self)    ; player is near of dead
ENDEVENT

EVENT OnDeath(Actor akKiller)       ; 1..2 seconds later (depends on script engine performance) the player is definitely dead
    bDead = TRUE
    gotoState("Done")                ; ### STATE ###
    Debug.Trace("PlayerTest: OnDeath() - has been reached.. " +self)    ; player is dead
ENDEVENT

;============================
state Done
endState

;============================
state Busy
;=========
;EVENT OnHit (..)
  ; OnHit() event has to be written here, IF OnHit() would be declared in empty state like OnDying() or OnDeath()
;ENDEVENT
;=======
endState

;============================
auto state Ready
;===============
EVENT OnHit (..)
 ..
    gotoState("Busy")                      ; now script is in state "Busy"
    myF_Action(akAggressor as Actor)       ; function is getting running now

; Will the function myF_Action() completed before the next code is triggered? YES.
; at this point myF_Action() has been finished by "Return" or "endfunction", the current state can be "Busy" or "Done"

IF ( bDead )
   ; script switched to state "Done" caused by script code inside events OnDying() / OnDeath()
   ; keep state "Done"
ELSE
     gotoState("Ready")
ENDIF
ENDEVENT
;=======
endState

Maybe this would be better explaining of using papyrus states.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...