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.

 

 

 

Scriptname BTDCREATUREtoPLAYERDmg extends ReferenceAlias
{Script that handles Creature to Player bleed damage}

;---------------------------------------------------------------------------------------------------------------------------------------
; The HitChance conditions in the function blocks control the hit chances to apply the bleed damage spells to targets. To increase the chances of applying a bleed, use a lower number. To reduce the chances of applying a bleed, use a higher number.
;---------------------------------------------------------------------------------------------------------------------------------------

Actor Property PlayerRef Auto

Race Property WolfRace Auto
Race Property SlaughterfishRace Auto
Race Property SabreCatRace Auto
Race Property SabreCatSnowyRace Auto
Race Property BearBlackRace Auto
Race Property BearBrownRace Auto
Race Property BearSnowRace Auto
Race Property IceWraithRace Auto
Race Property WerewolfBeastRace Auto
Race Property SprigganRace Auto
Race Property SprigganMatronRace Auto
Race Property TrollRace Auto
Race Property TrollFrostRace Auto
Race Property DwarvenSphereRace Auto
Race Property DwarvenCenturionRace Auto
Race Property DLC1DeathHoundRace Auto
Race Property DLC1SabreCatGlowRace Auto
Race Property SprigganEarthMotherRace Auto
Race Property DLC1GargoyleRace Auto
Race Property DLC1GargoyleVariantBossRace Auto
Race Property DLC1GargoyleVariantGreenRace Auto
Race Property DLC2SprigganBurntRace Auto
Race Property DLC2WerebearBeastRace Auto
Race Property DLC1LD_ForgemasterRace Auto
Race Property DLC2DwarvenBallistaRace Auto
Race Property DLC2dunKarstaagIceWraithRace Auto
Race Property DragonRace Auto

Spell Property BTDSpellCREATURESmalltoPLAYERBleedDmg Auto
Spell Property BTDSpellCREATUREMediumtoPLAYERBleedDmg Auto
Spell Property BTDSpellCREATURELargetoPLAYERBleedDmg Auto
Spell Property BTDSpellCREATUREXLargetoPLAYERBleedDmg Auto

Keyword Property ActorTypeAnimal Auto
Keyword Property ActorTypeCreature Auto
Keyword Property ActorTypeDwarven Auto

Actor Aggressor
Race AggressorRace

Int HitChance

; Dragons and hagravens had to be excluded since their breaths and spells would apply bleeds to the player
;------------------------------------------------------------------------------------------------------------------------
; HIT REGISTERED ON PLAYER
;------------------------------------------------------------------------------------------------------------------------
Auto State Ready 

	Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
		GoToState("Busy") ; Don't run this OnHit Event again until it's finished and the cooldown is up
		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)
			else 
				Utility.Wait(0.5)
				GoToState("Ready") ; The OnHit Event has finished and cooldown is up, so it can be called again
			endif
		else
			Utility.Wait(0.5)
			GoToState("Ready") ; The OnHit Event has finished and cooldown is up, so it can be called again
		endif
		
	EndEvent

EndState


State Busy

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

;------------------------------------------------------------------------------------------------------------------------
; FUNCTION BLOCKS
;------------------------------------------------------------------------------------------------------------------------

; ---------- Player hit by an animal ----------
Function PlayerHitByAnimal(Actor Param1)

AggressorRace = Aggressor.GetRace()
HitChance = Utility.RandomInt()
	
	if ((AggressorRace == WolfRace) && (HitChance >= 70)) || ((AggressorRace == SlaughterfishRace) && (HitChance >= 30))
		BTDSpellCREATURESmalltoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
		
	elseif ((AggressorRace == SabreCatRace) && (HitChance >= 60)) || ((AggressorRace == SabreCatSnowyRace) && (HitChance >= 50)) || ((AggressorRace == BearBlackRace) && (HitChance >= 50)) || ((AggressorRace == BearBrownRace) && (HitChance >= 60)) || ((AggressorRace == BearSnowRace) && (HitChance >= 50)) || ((AggressorRace == DLC1SabreCatGlowRace) && (HitChance >= 50))
		BTDSpellCREATUREMediumtoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
		
	elseif ((AggressorRace == TrollRace) && (HitChance >= 60)) || ((AggressorRace == TrollFrostRace) && (HitChance >= 50))
		BTDSpellCREATURELargetoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
	endif
	
	Utility.Wait(0.5)
	GoToState("Ready") ; The OnHit Event has finished and cooldown is up, so it can be called again
			
EndFunction

; ---------- Player hit by a creature ----------
Function PlayerHitByCreature(Actor Param1)

AggressorRace = Aggressor.GetRace()
HitChance = Utility.RandomInt()

	if ((AggressorRace == DLC1DeathHoundRace) && (HitChance >= 70))
		BTDSpellCREATURESmalltoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
		
	elseif ((AggressorRace == IceWraithRace) && (HitChance >= 50)) || ((AggressorRace == DLC2dunKarstaagIceWraithRace) && (HitChance >= 50))
		BTDSpellCREATUREMediumtoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
		
	elseif ((AggressorRace == WerewolfBeastRace) && (HitChance >= 50)) || ((AggressorRace == SprigganRace) && (HitChance >= 60)) || ((AggressorRace == SprigganMatronRace) && (HitChance >= 50)) || ((AggressorRace == SprigganEarthMotherRace) && (HitChance >= 40)) || ((AggressorRace == DLC1GargoyleRace) && (HitChance >= 50)) || ((AggressorRace == DLC1GargoyleVariantBossRace) && (HitChance >= 40)) || ((AggressorRace == DLC1GargoyleVariantGreenRace) && (HitChance >= 50)) || ((AggressorRace == DLC2SprigganBurntRace) && (HitChance >= 60)) || ((AggressorRace == DLC2WerebearBeastRace) && (HitChance >= 50))
		BTDSpellCREATURELargetoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
	endif
	
	Utility.Wait(0.5)
	GoToState("Ready") ; The OnHit Event has finished and cooldown is up, so it can be called again
		
EndFunction

; ---------- Player hit by a dwarven automaton ----------
Function PlayerHitByDwarven(Actor Param1)

AggressorRace = Aggressor.GetRace()
HitChance = Utility.RandomInt()

	if ((AggressorRace == DwarvenSphereRace) && (HitChance >= 50))
		BTDSpellCREATURELargetoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
		
	elseif ((AggressorRace == DwarvenCenturionRace) && (HitChance >= 20)) || ((AggressorRace == DLC1LD_ForgemasterRace) || (AggressorRace == DLC2DwarvenBallistaRace) && (HitChance >= 10))
		BTDSpellCREATUREXLargetoPLAYERBleedDmg.cast(PlayerRef,PlayerRef)
	endif
	
	Utility.Wait(0.5)
	GoToState("Ready") ; The OnHit Event has finished and cooldown is up, so it can be called again
		
EndFunction

 

 

 

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

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

 

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

 

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

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.

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.

 

 

 

 

Scriptname BTDCREATUREtoPLAYERDmg extends ReferenceAlias
{rewritten by Big Man 2016}    ; handles creature to Player bleed damage

; source: Big Man

--------------------------------------------------------------------------------------------------------------------------------


  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 i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)
Spell BleedUm


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")

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


IF (( abHitBlocked ) || (akSource as Spell)) || (!(Attacker.HasKeyword(ActorTypeDwarven)) && !(Attacker.HasKeyword(ActorTypeAnimal))&& !(Attacker.HasKeyword(ActorTypeCreature)))   

   gotoState("Ready")

ElseIf (i >= 50) && SmallAttacker.HasForm(R)
    BleedUm = SmallBleed
ElseIf (i >= 50) && MediumAttacker.HasForm(R)
    BleedUm = MediumBleed
ElseIf (i >= 50) && LargeAttacker.HasForm(R)
    BleedUm = LargeBleed
ELseIf (i >= 50) && XLargeAttacker.HasForm(R)
    BleedUm = XLargeBleed
EndIf

If (BleedUm)
    BleedUm.Cast((Self.GetReference() As Actor),(Self.GetReference() As Actor))  
EndIf

 gotoState("Ready")

EndEvent
EndState

State Busy
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
; Nothing
EndEvent
EndState

 

 

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.

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.

 

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

 


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:

 

;=========================
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) || !(akAggressor as Actor) || !(akSource as Weapon)  
   ; 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???
   gotoState("Ready")
   RETURN   
Else
    PlayerHitBy(akAggressor as Actor)
EndIf
ENDEVENT
;=======
endState

 



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:

 

;=========================
Auto State Ready     ; HIT REGISTERED ON PLAYER
;===============
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
IF ( abHitBlocked )                                 ; IF (abHitBlocked == true)
    RETURN    ; - STOP - /1    hit was blocked
ENDIF
;---------------------
IF (akSource as Spell)
    RETURN    ; - STOP - /2    spells are skipped
ENDIF
;---------------------
IF (akAggressor as Actor)
ELSE
    RETURN    ; - STOP - /3    no actor, no blood
ENDIF
;---------------------
IF (akSource as Weapon) ;|| (akProjectile)
ELSE
    RETURN    ; - STOP - /4    no weapon, no blood
ENDIF
;--------------------- all conditions above are thread safe
    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

 

 

Edited by ReDragon2013
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.

 

 

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"?

 

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".

 

 

 

Scriptname BTDCREATUREtoPLAYERDmg extends ReferenceAlias
{rewritten by Big Man 2016}    ; handles creature to Player bleed damage

; source: Big Man

  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

Spell BleedUm
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")

Actor Herbert = (AkAggressor As Actor)

IF (( abHitBlocked ) || (akSource as Spell))||(!Herbert.HasKeyword(ActorTypeDwarven) && !Herbert.HasKeyword(ActorTypeAnimal) && !Herbert.HasKeyword(ActorTypeCreature))   
  
gotoState("Ready")

Else

Race R = Herbert.GetRace()

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

int i = Utility.RandomInt()            ; = Utility.RandomInt(0,100)

If SI > -1
BleedUm = SmallBleed
If SI <= 3
iChance = 40
ElseIf SI <= 6
iChance = 60
ElseIf SI <= 9
iChance = 80
EndIf

ElseIf MI > -1
BleedUm = MediumBleed
If MI <= 3
iChance = 40
ElseIf MI <= 6
iChance = 60
ElseIf MI <= 9
iChance = 80
EndIf

ElseIf LI > -1
BleedUm = LargeBleed
If LI <= 3
iChance = 40
ElseIf LI <= 6
iChance = 60
ElseIf LI <= 9
iChance = 80
EndIf

ElseIf XI > -1
BleedUm = XLargeBleed
If XI <= 3
iChance = 40
ElseIf XI <= 6
iChance = 60
ElseIf XI <= 9
iChance = 80
EndIf
EndIF

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

 gotoState("Ready")

EndIf
EndEvent
EndState

State Busy
EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)
; Nothing
EndEvent
EndState

 

 

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:

 

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.


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:


OnHit
.return NONE
.paramTable
.param akAggressor ObjectReference
.param akSource Form
.param akProjectile Projectile
.param abPowerAttack Bool
.param abSneakAttack Bool
.param abBashAttack Bool
.param abHitBlocked Bool
.endParamTable
.localTable
.local ::nonevar none
.local ::temp0 actor
.local ::temp1 race
.local ::temp2 spell
.local ::temp3 bool
.local ::temp4 bool
.local ::temp5 bool
.local Attacker Actor
.local R Race
.local ::temp6 form
.local ::temp7 int
.local SI Int
.local MI Int
.local LI Int
.local XI Int
.local iChance Int
.local ::temp8 bool
.local ::temp9 bool
.local i int
.local ::temp10 objectreference
.local ::temp11 actor
.local ::temp12 objectreference
.endLocalTable



my version with functions to divide the code:


OnHit
.return NONE
.paramTable
.param akAggressor ObjectReference
.param akSource Form
.param akProjectile Projectile
.param abPowerAttack Bool
.param abSneakAttack Bool
.param abBashAttack Bool
.param abHitBlocked Bool
.endParamTable
.localTable
.local ::temp0 spell
.local ::temp1 actor
.local ::temp2 weapon
.local ::nonevar none
.endLocalTable

function PlayerHitBy
.return NONE
.paramTable
.param aRef Actor
.endParamTable
.localTable
.local ::temp3 race
.local ::temp4 bool
.local ::temp6 bool
.local r race
.local sp spell
.local ::temp5 spell
.local ::temp7 objectreference
.local ::nonevar none
.local selfRef objectReference
.endLocalTable

function PlayerHitByAnimal (these both function PlayerHitByDwarven, PlayerHitByCreature use one temp bool more)
.return Spell
.paramTable
.param r Race
.endParamTable
.localTable
.local ::temp8 int
.local ::temp9 bool
.local ::temp11 bool
.local i int
.local sp spell
.local ::temp10 spell
.local x int
.endLocalTable

function myF_GetSS (all functions myF_GetSM, myF_GetSL, myF_GetSX have the same header)
.return Spell
.paramTable
.param i Int
.param iChance Int
.endParamTable
.localTable
.local ::temp22 bool
.local ::temp23 spell
.endLocalTable

 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...