Scripting Issue. Trying to add a single item to NPC inventor

Post » Thu Jun 21, 2012 1:06 am

Hey guys.

I'm experiencing a weird issue.

I have a while loop running inside an OnEquipped event on a script attached to a piece of armor. When certain conditions are met while an NPC is wearing this armor, the script is supposed to add a single item (a different piece of armor) to the NPC's inventory (which they subsequently equip since it is a piece of armor). I have the additem function inside an if statement that explicitly states only to add the item if 0 are found in the NPC's inventory. Problem is, it seems to be adding a random amount of the items (usually 0 through 15) despite this condition. Here's a code snippet (apologies for the formatting... I'm not sure why the board's code display function is cramming it all together....)

Scriptname aaDAFArmorScript extends ObjectReferenceint stageEvent OnEquipped(Actor akActor)stage = 5  ; enter into loop once armor is equipped...  While (akActor.IsEquipped(DynamicArmorProperty[0]))  			     float  actorsHealth = akActor.GetActorValuePercentage("Health")   if actorsHealth >= 0.75 && stage != 1	if stage != 5	 akActor.RemoveItem(DynamicArmorProperty[stage], 1, true)	endif	; Add a single Stage 1 Token.  Let NPC choose to equip it.	if (akActor.GetItemCount(DynamicArmorProperty[1]) == 0)	 akActor.AddItem(DynamicArmorProperty[1], 1, true)	endIf	stage = 1     elseif actorsHealth < 0.75 && actorsHealth >= 0.50 && stage != 2	if stage != 5	 akActor.RemoveItem(DynamicArmorProperty[stage], 1, true)	endif	; Equip Stage 2	if (akActor.GetItemCount(DynamicArmorProperty[2]) == 0)	 akActor.AddItem(DynamicArmorProperty[2], 1, true)	endIf	 stage = 2   elseif actorsHealth < 0.50 && actorsHealth >= 0.25 && stage != 3	if stage != 5	 akActor.RemoveItem(DynamicArmorProperty[stage], 1, true)	endif  	; Equip Stage  3	if (akActor.GetItemCount(DynamicArmorProperty[3]) == 0)	 akActor.AddItem(DynamicArmorProperty[3], 1, true)	endIf	stage = 3   elseif actorsHealth < 0.25 && stage != 4	if stage != 5	 akActor.RemoveItem(DynamicArmorProperty[stage], 1, true)	endif	; Equip Stage 4	if (akActor.GetItemCount(DynamicArmorProperty[4]) == 0)	 akActor.AddItem(DynamicArmorProperty[4], 1, true)	endIf	stage = 4   endif  EndWhileendEvent; Remove leftover armor piecesEvent OnUnequipped(Actor akActor)   akActor.RemoveItem(DynamicArmorProperty[1], 1, true)   akActor.RemoveItem(DynamicArmorProperty[2], 1, true)    akActor.RemoveItem(DynamicArmorProperty[3], 1, true)   akActor.RemoveItem(DynamicArmorProperty[4], 1, true)endEventArmor[] Property DynamicArmorProperty  Auto


I think it has something to do with the AI possibly equipping and unequipping the involved items to make a check if they are 'better' than what they are wearing already. I notice a flickering of the model for a bit after they equip it, which means to me they keep equipping and unequipping it, before settling on equipping. That however still doesn't explain how the game decides to neglect the condition of adding the item only if there are 0 in the inventory. If anyone has any ideas or needs more information please let me know. Thanks!!
User avatar
Neil
 
Posts: 3357
Joined: Sat Jul 14, 2007 5:08 am

Post » Wed Jun 20, 2012 9:02 pm

It does sound weird. Does the problem occur if the script is on the player instead?
User avatar
Tracy Byworth
 
Posts: 3403
Joined: Sun Jul 02, 2006 10:09 pm

Post » Wed Jun 20, 2012 8:38 pm

It does sound weird. Does the problem occur if the script is on the player instead?

Nope. That said, I have to throw in an equip function if it is on the player since the player doesn't try to automatically equip stuff that is 'better'.

I may have just found a workaround though, assuming I can fix a small problem that it presents. If I change the 'additem' functions in the above script to 'equipitem' it will automatically add a single instance of that item (and only a single instance). If I set the 'stay equipped' bool parameter to true then the NPC can't waffle about the choice and has to keep it equipped. With this setup everything seems to work except when the NPC removes the base armor object. The OnUnequipped event commands telling the NPC to remove the added armor piece doesn't work since the 'stay equipped' bool parameter was set to true. I'm currently tinkering with trying to fix that, but would love to know of another solution to the above problem if one exists.
User avatar
Nicole Coucopoulos
 
Posts: 3484
Joined: Fri Feb 23, 2007 4:09 am

Post » Thu Jun 21, 2012 6:12 am

OK, based on your descriptions of the problem, it sounds like you're having trouble with multiple threads. I have no idea why this is so, but the OnEquipped portion of your script is being called multiple times, so you actually have multiple loops running. So each of them reach the condition check with GetIemCount in the same split second and call the AddItem function.

What you should do is try using some states instead. So using states in your script, it would look like this:

Scriptname aaDAFArmorScript extends ObjectReferenceArmor[] Property DynamicArmorProperty  AutoBool Loopint stageAuto State Inactive    Event OnEquipped(Actor akActor)        GoToState("Active")        stage = 5        Loop = True        while (Loop)            float actorsHealth = akActor.GetActorValuePercentage("Health")            if (actorsHealth >= 0.75) && !(stage == 1)                akActor.RemoveItem(DynamicArmorProperty[stage])                stage = 1                akActor.AddItem(DynamicArmorProperty[stage])            elseif (actorsHealth >= 0.5) && !(stage == 2)                akActor.RemoveItem(DynamicArmorProperty[stage])                stage = 2                akActor.AddItem(DynamicArmorProperty[stage])            elseif (actorsHealth >= 0.25) && !(stage == 3)                akActor.RemoveItem(DynamicArmorProperty[stage])                stage = 3                akActor.AddItem(DynamicArmorProperty[stage])            elseif !(stage == 4)                akActor.RemoveItem(DynamicArmorProperty[stage])                stage = 4                akActor.AddItem(DynamicArmorProperty[stage])            endif        endwhile    EndEventEndStateState Active    Event OnUnequipped(Actor akActor)        Loop = False        akActor.RemoveItem(DynamicArmorProperty[1], 1, true)        akActor.RemoveItem(DynamicArmorProperty[2], 1, true)        akActor.RemoveItem(DynamicArmorProperty[3], 1, true)        akActor.RemoveItem(DynamicArmorProperty[4], 1, true)        GoToState("Inactive")    endEventEndState
User avatar
Tha King o Geekz
 
Posts: 3556
Joined: Mon May 07, 2007 9:14 pm

Post » Wed Jun 20, 2012 7:45 pm

What you describe sounds exactly what's happening. I also had no idea about the state feature. I found that if I implement my above "solution" and add an explicit unequipitem function before the removeitem in the OnUnequipped event I get the desired results. That said, I'm sure the multiple threads are still happening, so it looks like adding the states will tidy it up nicely. Thanks a lot for the help! I'll make sure to credit you once I release the mod.
User avatar
Jesus Lopez
 
Posts: 3508
Joined: Thu Aug 16, 2007 10:16 pm

Post » Thu Jun 21, 2012 8:58 am

Aaaa! While TRUE loop! Please use RegisterForSingleUpdate() and Event OnUpdate() instead. This is going to give me nightmares...and make the game slow.

http://www.creationkit.com/OnUpdate_-_Form#Notes
User avatar
Heather M
 
Posts: 3487
Joined: Mon Aug 27, 2007 5:40 am

Post » Thu Jun 21, 2012 4:13 am

Aaaa! While TRUE loop! Please use RegisterForSingleUpdate() and Event OnUpdate() instead. This is going to give me nightmares...and make the game slow.

http://www.creationkit.com/OnUpdate_-_Form#Notes

http://www.gamesas.com/topic/1367642-is-there-a-way-to-run-on-update-on-a-token/
User avatar
Mariana
 
Posts: 3426
Joined: Mon Jun 12, 2006 9:39 pm

Post » Wed Jun 20, 2012 5:42 pm

Yeah, I hope the OnUpdate event gets fixed in the future.

I'm not an expert at judging how much processing power different functions take, but would it be possible to limit the amount of excess processing being done by the infinite loop by slowing it down with Utility.Wait() functions (or some other way)? I'd guess the wait function uses processing power to determine when it's done waiting, but again, not really sure.

The point is that the true loop doesn't need to run hundreds of checks a second, so any way to slow it down while conserving processing power would be a good bandage to the OnUpdate bug. Any ideas?
User avatar
Lyndsey Bird
 
Posts: 3539
Joined: Sun Oct 22, 2006 2:57 am

Post » Thu Jun 21, 2012 5:58 am

Script the armor to add/remove an ability instead. The ability should have multiple effects, each effect conditioned so that it is only active when the owner npc's health is at a certain percentage.

Each effect will have a script adding its respective piece of armor to the owner npc.
User avatar
DAVId Bryant
 
Posts: 3366
Joined: Wed Nov 14, 2007 11:41 pm

Post » Wed Jun 20, 2012 11:58 pm

Script the armor to add/remove an ability instead. The ability should have multiple effects, each effect conditioned so that it is only active when the owner npc's health is at a certain percentage.

Each effect will have a script adding its respective piece of armor to the owner npc.

Oh, yeah I guess that gets around the entire requirement of the loop. I'll have to check out how the mechanics would work out in CK though. A major goal is to allow easy access for end users to create new dynamic armor/clothes (meaning the less time in the CK the better). With this current set up, someone creating a new 'dynamic armor' would just have to enter the armor object data, the subsequent armor objects, attach this script to the main object, and fill the properties with the other armor objects accordingly. Even that is an unfortunate amount of work though. You'd think with the armoraddon structure there'd be a way to dynamically modify armor models for an object (which is the purpose of this mod to begin with).
User avatar
amhain
 
Posts: 3506
Joined: Sun Jan 07, 2007 12:31 pm


Return to V - Skyrim