ReDragon2013 Posted December 21, 2016 Share Posted December 21, 2016 about the use of RETURN, use the next link whenever you have a question about papyrus scriptinghttp://www.creationkit.com/index.php?title=Category:Scripting you will find a link to the Bethesda wiki return page Link to comment Share on other sites More sharing options...
Masterofnet Posted December 21, 2016 Share Posted December 21, 2016 (edited) about the use of RETURN, use the next link whenever you have a question about papyrus scriptinghttp://www.creationkit.com/index.php?title=Category:Scripting you will find a link to the Bethesda wiki return page You are a nice person and I have just been joking with you a bit, Edited December 22, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
Masterofnet Posted December 21, 2016 Share Posted December 21, 2016 (edited) 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 December 23, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
Masterofnet Posted December 21, 2016 Share Posted December 21, 2016 (edited) 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 December 22, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
Masterofnet Posted December 22, 2016 Share Posted December 22, 2016 (edited) 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 December 24, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
ReDragon2013 Posted December 22, 2016 Share Posted December 22, 2016 (edited) 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 December 23, 2016 by ReDragon2013 Link to comment Share on other sites More sharing options...
ReDragon2013 Posted December 22, 2016 Share Posted December 22, 2016 (edited) 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 endifor if possible, you can use that construct to shrink code size if abHitBlocked else ; code here endif Edited December 22, 2016 by ReDragon2013 Link to comment Share on other sites More sharing options...
Masterofnet Posted December 22, 2016 Share Posted December 22, 2016 (edited) 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 December 23, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
Masterofnet Posted December 23, 2016 Share Posted December 23, 2016 (edited) 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 December 23, 2016 by Masterofnet Link to comment Share on other sites More sharing options...
ReDragon2013 Posted December 23, 2016 Share Posted December 23, 2016 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 More sharing options...
Recommended Posts