Jump to content

Photo

Saving and restoring equipped items

equipment hotkey save restore

  • Please log in to reply
34 replies to this topic

#1
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Edit: This topic allowed me to actually create a plugin: Loadout!

Hey there,

I have been trying to write a piece of papyrus code that summarizes the currently equipped items of the player. I have a lot of programming experience but I have very little experience with the creation kit so I might be misusing things or solving problems in an overly complex manner. The code uses SKSE by the way.

What the code 'does' is register the left shift key event. When I press the left shift key the player's items are queried one by one with
p.getNthForm(n)
. The items, which are actually Forms, are then checked for being either armor or ammo and if they are it is checked if the player has them equipped (I hoped on a performance improvement by checking the types first). After that the left and right hand are checked for having a weapon/shield/torch/spell equipped or not and finally I check the shout.

Example output in the debug log is the following:

[04/07/2013 - 12:11:14AM] Items |Apparel: Dawnbreaker|Left hand: Spell - Flames|Right hand: Weapon - Dawnbreaker|Shout: [Shout &--#60; (0002F7BA)&--#62;]|


The problems are that
  • The debug.trace function seems to be very unresponsive and a big number of shift presses is skipped. This may be because the script takes long to execute (order of seconds) and stays in the "busy" state.
  • The answer is incorrect. I was also wearing apparel and had arrows equipped. They did not show up in this particular debug message when they should have.
The code:

Spoiler


I created a quest and attached the script to it. Don't mind the version part, the update function is called from another script in the OnPlayerLoadGame event.

Is there anyone who might be able to help me solve (one of) these problems?

Thanks in advance,

Deazurain

Edited by Deazurain, 08 April 2013 - 10:12 PM.


#2
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
For you second problem, your GetType integers are off. It's set to activator and weapon. You need 26/42 for armor/ammo. I ran a modified version of you script in a spell and this is what returned in my papyrus log. So maybe that was just a silly typo or something but your script works fine.

Armor Property TestArmor Auto

Ammo Property TestAmmo Auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
    Actor p = Game.GetPlayer()
    int t
    int n = p.getNumItems()
    while n
        n -= 1
        Form f = p.getNthForm(n)
        t = f.getType()
        if t == 26 || t == 42 ; the form type is armor or ammo
            if p.isEquipped(f)
                Debug.Trace("PLAYERINVENTORYTRACE: "+f.GetName())
        ;        s += "Apparel: " + f.getName() + "|"
            endif
        endif
    endwhile
    Debug.Notification("We're done!")
endEvent

[04/06/2013 - 10:22:26PM] PLAYERINVENTORYTRACE: Steel Bolt
[04/06/2013 - 10:22:29PM] PLAYERINVENTORYTRACE: College Boots
[04/06/2013 - 10:22:29PM] PLAYERINVENTORYTRACE: Cape - Imperial
[04/06/2013 - 10:22:29PM] PLAYERINVENTORYTRACE: Amulet of Dibella
[04/06/2013 - 10:22:30PM] PLAYERINVENTORYTRACE: Master Wizard's Robes
[04/06/2013 - 10:22:30PM] PLAYERINVENTORYTRACE: Master Wizard's Hood

As for your first problem, I'm assuming it's because you are in your busy state. Is this actually a problem though? It took me about 8 seconds to go through all of my inventory search for forms. You can put a Debug.Trace in your Busy state to be sure of what's going on. Also, if you aren't putting any trace or notifications in your busy state, you can leave it completely empty (don't even need to have the Event OnKeyDown in there.

Edited by mojo22, 06 April 2013 - 09:34 PM.


#3
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
O dear thats a pretty dumb mistake. Thanks for clearing that up.

I do have some more questions now:
  • Putting the script in a spell - What are your reasons for doing it like this? I have been thinking very long trying to figure out how and where to attach my script if it is 'continuously running'. I now have a 'on startup' and 'run once' enabled quest with the script I posted here and I attached a Reference Alias with a script that handles a OnPlayerLoadGame event so that I can update the plugin data format and/or register events when I update the script.
  • Measure the script execution time - Can I measure how long my event handler takes to execute?I
  • Obtaining equipped items - Is there a faster way to do this? 8 seconds is an incredibly long time. My eventual goal is to replace the favorites menu. I want to save and restore complete 'loadouts' as i call them instead of a single favorite item. This way I can quickly swap between my role playing clothes, my ranged weapon and armor set, my bartering equipment etc. It would be nice if the menu in which the storing is done would be a little responsive. Is it possible to 'split' my script up into multiple threads that query items 1-20 and another one 21 to 40 etc.?
  • Data structure - How would you recommend saving the items in order for them to be restored. Do I make arrays of length 8 of FormList for the apparel, Form for the left hand, Form for the right hand and Shout for the shout or can i make a datastructure 'Loadout' of which I create an array of length 8? And do I store the Forms by reference? Will that create memory leaks if an items is thrown away, sold or taken via a script?
  • Disable original favorites menu - I want to replace the original favorites menu by a list of 8 loadout slots which you can rename and store your current loadout in. I am clueless how to disble the original menu and create my own :). I want to focus on getting the background working though before trying to make a menu for it.
Thats a lot of non-trivial questions, at least for me, and I would love to find an answer to them :). Thanks again for your help already!

#4
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
So I changed the script a little and measured the time it took to process my inventory with about 200 items (which is not unusual) with getCurrentRealTime():

[04/07/2013 - 02:03:10PM] Items |Apparel: Ring of Resist Magic

|Apparel: Elven Gauntlets
|Apparel: Steel Arrow
|Apparel: Forsworn Boots
|Apparel: Clothes
|Apparel: Necklace of Major Haggling
|Apparel: Masque of Clavicus Vile
|Left hand: Spell - Flames
|Right hand: Weapon - Dawnbreaker
|Shout: Whirlwind Sprint
|Time taken: 15026|


The job is done and it works. The thing is that it took 15 seconds which is in my eyes unacceptable. I do not think that it is possible to achieve enough optimizations to narrow the processing time to 1 second using the current approach. There has to be a different way to get a list of the equipped items! My gut tells me that Actor.isEquipped(Form f) is what makes the loop incredibly slow.

I've been looking at Function AddInventoryEventFilter(Form akFilter) from the ObjectReference script and Form Function GetWornForm(int slotMask) from the Actor script. The problem with the inventoryeventfilter is that it probably doesnt affect ObjectReference.getNthForm(int n) and the problem with GetWornForm(int slotMask) is that I need to know all possible combinations of masks that are used. This is undesireable because it is impossible for me to know what other mods use. Who knows, they might have made an 'Boothat' that equips on your feet and head or something funky like that.

#5
DienesToo

DienesToo
  • Members
  • Pip
  • Curate
  • Joined: 03-June 12
  • 567 posts
You can use
Armor armo
armo = mynpc.getWornForm(Armor.getMaskForSlot(aNumber)) as Armor
            if armo != none
           	 ;whatever
            endif

to get the form equipped to a specific bipedobjectslot. Its worth pointing out that something that fills slots say 37 and 42 (feet and circlet or a boothat) will be returned for both mynpc.getWornForm(Armor.getMaskForSlot(37)) and mynpc.getWornForm(Armor.getMaskForSlot(42)). You don't need to know what its particular total slotmask is.

It'll be way faster to test each slot than each item in their inventory. I would make an array to hold the equipped armors and after getting each slot check if the item is already in the array, if not add it. Then you end up with the full list of what they are wearing.

#6
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Thanks for your reply.

I'm thinking about just doing something like

int mask = 1, n = 0
Form[] equipment = new Form[31]
while n < 31
   equipment[n++] = getWornForm(mask)
   mask *= 2
endwhile


I dont care checking for doubles because I hope that equipping the same item multiple times sequentially has no effect.

Now the problem is making 8 instances of {equipment[31], shout} data structures. Will I have to name them? :'( I thought papyrus was oop!

Edited by Deazurain, 07 April 2013 - 08:18 AM.


#7
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Output with the following code:


Actor p = Game.GetPlayer()
string s = "Items |"
int t ; type holder
float startTime = Utility.getCurrentRealTime()

; Get apparel
int n = 0
int mask = 1
while n < 31
Form f = p.getWornForm(mask)
if f != None
s += f.getName() + "|"
endif
n += 1
mask *= 2
endwhile

; Get shout
Form theshout = p.getEquippedShout()
s += "Shout: " + theshout.getName() + "|"

; Add time info
float endTime = Utility.getCurrentRealTime()
int milisecondsTaken = (1000 * (endTime - startTime)) as int
s += "Time taken: " + milisecondsTaken + "|"

; Log it
Debug.trace(s)

is:

Items |Masque of Clavicus Vile|Masque of Clavicus Vile|Clothes|Elven Gauntlets|Necklace of Major Haggling|Ring of Resist Magic|Forsworn Boots|Masque of Clavicus Vile|Masque of Clavicus Vile|Shout: Whirlwind Sprint|Time taken: 1852|


What is awesome: it is about 10 times as fast ;D
What is not awesome: I am missing the 'Steel arrows' equipment

I hoped that left and right hand were also slots but apparently they are not. I'll have to figure out the specifics for handling shields which may be armor and torches which is another special case i think.

So this is definitely a better method but I still dont know how to solve the data saving and menu replacing problems and I want to know why mojo put the script in a spell.

#8
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts

O dear thats a pretty dumb mistake. Thanks for clearing that up.

I do have some more questions now:

  • Putting the script in a spell - What are your reasons for doing it like this? I have been thinking very long trying to figure out how and where to attach my script if it is 'continuously running'. I now have a 'on startup' and 'run once' enabled quest with the script I posted here and I attached a Reference Alias with a script that handles a OnPlayerLoadGame event so that I can update the plugin data format and/or register events when I update the script.
  • Measure the script execution time - Can I measure how long my event handler takes to execute?I
  • Obtaining equipped items - Is there a faster way to do this? 8 seconds is an incredibly long time. My eventual goal is to replace the favorites menu. I want to save and restore complete 'loadouts' as i call them instead of a single favorite item. This way I can quickly swap between my role playing clothes, my ranged weapon and armor set, my bartering equipment etc. It would be nice if the menu in which the storing is done would be a little responsive. Is it possible to 'split' my script up into multiple threads that query items 1-20 and another one 21 to 40 etc.?
  • Data structure - How would you recommend saving the items in order for them to be restored. Do I make arrays of length 8 of FormList for the apparel, Form for the left hand, Form for the right hand and Shout for the shout or can i make a datastructure 'Loadout' of which I create an array of length 8? And do I store the Forms by reference? Will that create memory leaks if an items is thrown away, sold or taken via a script?
  • Disable original favorites menu - I want to replace the original favorites menu by a list of 8 loadout slots which you can rename and store your current loadout in. I am clueless how to disble the original menu and create my own :smile:. I want to focus on getting the background working though before trying to make a menu for it.
Thats a lot of non-trivial questions, at least for me, and I would love to find an answer to them :smile:. Thanks again for your help already!


1. I just put the script in a spell simply because it was the easiest and fastest way for me to test it :). A quest would probably be better for a finalized mod.

2. I see you found that out.

3. So I am NOT an experience programmer - only have matlab experience :(. But I'm pretty sure you can split it into multiple threads. I do recall seeing a post on these forums of where someone did this for a mod that has a long start up time, but I'm not sure where it is. I'll see if I can find it.

4. As for storing data, there are probably a few different ways you could do it. The formlst sounds like the easiest. I haven't heard of any such memory leaks coming from a form list so I think you're fine. If you want to detect if an item has been removed that was stored in your formlist, you could try attach this to your player alias.

http://www.creationk...ObjectReference
http://www.creationk...ObjectReference

One thing to note though about OnItemRemoved, is that if the item is deleted, akItemReference returns none. So in this case you may want to do a full recheck of the player's inventory to see what was lost.

5. No clue haha. Never worked with that stuff.

That's unfortunate that arrows don't appear to have a slot mask. I'm kind of curious of how Bethesda handles the jail events, because when you go to jail the remove all your items, but when you leave all your items are re-equipped. Not sure if this is done by script or if its hardcoded.

#9
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
For kicks and giggles, I tried to create multiple threads using your first method. It didn't seem to improve the speed as fast as I think it was. I think the script is functioning as it should though, although maybe it can be improved upon to run faster... not sure if 8 threads is too much. Here were the resutls anyways.

Script:

Spoiler


[04/07/2013 - 01:55:12PM] 9 17 25 33 41 49 57 66
[04/07/2013 - 01:55:12PM] Starting Thread 1
[04/07/2013 - 01:55:12PM] Starting Thread 2
[04/07/2013 - 01:55:13PM] PLAYERINVENTORYTRACE: Ancient Nord Arrow
[04/07/2013 - 01:55:13PM] Starting Thread 3
[04/07/2013 - 01:55:13PM] Starting Thread 4
[04/07/2013 - 01:55:14PM] Starting Thread 5
[04/07/2013 - 01:55:14PM] Starting Thread 6
[04/07/2013 - 01:55:14PM] Starting Thread 7
[04/07/2013 - 01:55:14PM] Starting Thread 8
[04/07/2013 - 01:55:15PM] PLAYERINVENTORYTRACE: Necromancer's Amulet
[04/07/2013 - 01:55:16PM] PLAYERINVENTORYTRACE: Master Wizard's Hood
[04/07/2013 - 01:55:16PM] PLAYERINVENTORYTRACE: Master Wizard's Robes
[04/07/2013 - 01:55:16PM] PLAYERINVENTORYTRACE: Cape - Imperial
[04/07/2013 - 01:55:17PM] PLAYERINVENTORYTRACE: College Boots
[04/07/2013 - 01:55:18PM] Time: 6.419998
[04/07/2013 - 01:55:18PM] TotalItems: 66

Did a bit more testing to see how much faster threading could be (and with my method it's not by much :( )
113 Items - 4 Runs each
1 Thread: 15.4s +/- 1.04 StDev
8 Threads: 13.6s +/- 0.44 StDev
P-value = 0.028

Edited by mojo22, 07 April 2013 - 01:21 PM.


#10
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I never would've thought of starting threads like that. I think you could improve readability by creation a function that is called in the OnUpdate events: function checkEquipmentThread(Actor p, int lowerBound, int upperBound, string nextThread) that does the search from lower to upper bound. Maybe a minimum amount of items per thread to determine the amount of threads like [10, infinity) so that the thread startup is worth it. Looks like a fun and in some practical applications valuable experiment to me.

Anyway, it appears that the method suggested by DienesToo (Actor.getWornForm(int mask)) is better suited for checking the equipped items.

So lets summarize the open issues:
  • Using FormList as a dynamic array - I found the following on the wiki talk page www creationkit com/Talk:RemoveAddedForm_-_FormList (there's no dots in the link): A Form removed from a FormList isn't actually removed. The FormList will have an empty space. I wonder if this is still the case.
  • Data structure - Is it possible to create your own data structure of which you can make an array? Like for example in Java where you can define a class Point { int x, y; } of which it is possible to define an array of Points: Point point_array[] = new Point[10]; // not sure if the syntax is correct.
  • Disable original favorites menu - I want to replace the original favorites menu by a list of 8 loadout (a complete set of equipped items/spells and shout) slots which you can rename and store your current loadout in. I am clueless how to disble the original menu and create my own. I want to focus on getting the background working though before trying to make a menu for it.
  • Check if a Form is in the player inventory - When selecting a loadout, I need to make sure that the items in the loadout are still in the inventory of the player. Unless items not in the inventory cannot be equipped, I'll have to check that.

I'm kind of curious of how Bethesda handles the jail events, because when you go to jail the remove all your items, but when you leave all your items are re-equipped. Not sure if this is done by script or if its hardcoded.


Going through a list of 200 items in the engine itself happens extremely fast (compared to doing it one by one in papyrus). The removeAllItems function probably is used to place the player items in a temporal container and later on the same funciton is used but now the items are moved from the temporal container to the player.

#11
DienesToo

DienesToo
  • Members
  • Pip
  • Curate
  • Joined: 03-June 12
  • 567 posts
I'm not sure how formlists work these days so i can't speak to that. Papyrus is rather crippled compared to a real programing language, no custom objects. But you could make another script that sort of works as an object using properties.

I think there are a couple plugins that modify the favorites menu, you could try and see how they work.

You can use EquipItemEx() to only equip an item if the player has it, no need to check first.

#12
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Do you have an example of where such an 'object script' is used? Can I create 8 instances of a script using papyrus or will I have to do that in the creation kit editor?

How did you find out what EquipItemEx does? I believe the SKSE documentation basically doesn't exist. So you either guess by looking at the psc script sources or plow through the native injected functions right?

Edit: Oh yeah, is it possible to check the source code of all the plugins hosted on the workshop or only if the author explicitly released it?

Edited by Deazurain, 07 April 2013 - 04:26 PM.


#13
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts

  • Using FormList as a dynamic array - I found the following on the wiki talk page www creationkit com/Talk:RemoveAddedForm_-_FormList (there's no dots in the link): A Form removed from a FormList isn't actually removed. The FormList will have an empty space. I wonder if this is still the case.
  • Data structure - Is it possible to create your own data structure of which you can make an array? Like for example in Java where you can define a class Point { int x, y; } of which it is possible to define an array of Points: Point point_array[] = new Point[10]; // not sure if the syntax is correct.



1. Interesting, haven't actually had a need to manipulate formlists before. They mention that Revert() still works though, which resets the formlist back to its original state (empty), so I think formlists could still be safe to use.
2. http://www.creationk...rray_Reference. http://www.creationk...Arrays_(Papyrus) Yes you can create arrays to store data

As for the source files, some mods release the source files, some don't. Most likely you'll need to open up the BSA to get to them.

Edited by mojo22, 07 April 2013 - 04:39 PM.


#14
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
FormList.Revert() would do the job for my usecase if it really completely resets the formlist. I'll write a quick test for that.

I wanted to see how long it would take to just walk through the entire inventory using what seems to be the only approach:

dbg("Pressed 0 - Walk throught player inventory compare with none")
float startTime = Utility.getCurrentRealTime()
Actor p = Game.GetPlayer()
int n = p.getNumItems()
Form f
while n
  n -= 1
  f = p.getNthForm(n)
  if f == none
   dbg("Found Form that equalled none :S")
  endif
endwhile
float endTime = Utility.getCurrentRealTime()
dbg("Took " + ((1000 * (endTime - startTime)) as int) + " ms for " + p.getNumItems() + " item(s)")

It produced the following debug trace:


[04/07/2013 - 11:48:45PM] Quick Loadout: Pressed 0 - Walk throught player inventory compare with none
[04/07/2013 - 11:48:52PM] Quick Loadout: Took 6611 ms for 142 item(s)


That is almost 7 seconds for 150 items and I'm only comparing them :S. I found no ObjectReference.containsForm(Form f) function so that would be pretty annoying I guess when someone needs it. Luckily I don't because DienesToo mentioned that EquipItemEx() will only equip an item if it is in the inventory. But yeah.. there has to be a better way.

Edited by Deazurain, 07 April 2013 - 04:52 PM.


#15
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
I still recommend using this event for that http://www.creationk...bjectReference. Find out what item was removed and take it out of your formlist. That way you don't have to cycle through all the items again.

#16
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I don't believe I have to cycle through all items.

Taking your approach to ensure that an item is in the inventory would mean that
  • Items removed/added through scripts must trigger the event, i'll have to test this
  • if an item is put in a container and retrieved later, it must still be equipped using a loadout. This means that I need to keep track of a separate boolean variable for every piece of equipment and set it to true when a Form is in the inventory and to false when it is not in the inventory.
So yeah, I'm glad that I do not have to cycle trough the items because of EquipItemEx() :)

#17
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I hereby declare FormLists worthy of the task I have in mind of them. The only downside is that I will have to create 8 of them in the editor. I believe there is some editor object scripting language in which I can generate 8 formlists easily but I'll look into that another time.



[04/08/2013 - 12:41:07AM] Quick Loadout: Test: FormList.revert()

[04/08/2013 - 12:41:07AM] Quick Loadout: FormList.getSize(): 0, expected 0

[04/08/2013 - 12:41:07AM] Quick Loadout: FormList.getSize(): 3, expected 3

[04/08/2013 - 12:41:07AM] Quick Loadout: FormList.getSize(): 0, expected 0

[04/08/2013 - 12:41:08AM] Quick Loadout: FormList.getSize(): 1, expected 1


Using the code:

FormList property fl auto
function testFormListRevert()
  dbg("Test: FormList.revert()")
  Actor p = Game.GetPlayer()
  if p.getNumItems() < 3
	return
  endif
  dbg("FormList.getSize(): " + fl.getSize() + ", expected 0")
  fl.AddForm(p.getNthForm(0))
  fl.AddForm(p.getNthForm(1))
  fl.AddForm(p.getNthForm(2))
  dbg("FormList.getSize(): " + fl.getSize() + ", expected 3")
  fl.revert()
  dbg("FormList.getSize(): " + fl.getSize() + ", expected 0")
  fl.AddForm(p.getNthForm(0))
  dbg("FormList.getSize(): " + fl.getSize() + ", expected 1")
endfunction

For which I had to create a FormList object and link it to the property fl in the script.

Edited by Deazurain, 07 April 2013 - 05:49 PM.


#18
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Ahahah ITS WORKING ITS WORKING!!! Thanks guys I wouldn't have been able to get this working at all without you :)

If i press '9' my current equipment is stored in about 2-4 seconds, then I can change clothes, weapons and shouts as much as I want but when I press '0' my original set of items is restored ;D. It even looks like the character is actually changing clothes because I just unequip all the items from the player and then equip the items in the FormList :P which takes about a second.

The script contains a lot of test code but that might be valuable to someone in the future so I will post the entire script here first:

Spoiler


Unfortunately I could not think of a way to make this work with Torches. There is no function getEquippedTorch like there are for a shield, weapon, spell or shout. I would like to have a function that just returns the Form in a hand if there is one. My own incomplete version looks like this:


; Unfortunately does not work for weird stuff like torches
Form function getFormInLeftHand(Actor a) global
  int t = a.getEquippedItemType(0) ; left hand

  if t == 0 ; itemTypeNothing, can't use the property from global function
    return None
  elseif t == 9 ; itemTypeSpell, can't use the property from global function
    return a.getEquippedSpell(0) ; left hand
  elseif t == 10 ; itemTypeShield, can't use the property from global function
    return a.getEquippedShield() ; always the left hand
  elseif t == 11 ; itemTypeTorch, can't use the property from global function
    return None ; searching the inventory for an equipped Torch item takes too long
  endif

  return a.getEquippedWeapon(true) ; left hand
endfunction

The function for the right hand is a stripped down version (because you can't equip a shield or torch in your right hand).

So now I have to create 10 FormLists to hold the equipped items/shouts (since we have 10 number keys) and 10 strings to hold the name of each loadout and a menu where you can save your loadout and (re)name them. Yuck!

#19
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Okay so I went on a journey to find out how to make a custom menu. The journey wasn't that long since I found the root of all evil in under an hour: https://github.com/Mardoxx/skyrimui/.

As much fun it seems getting the compilation to work and then creating a functioning menu, I will not do it now. I guess I'm going to stick with saving/restoring loadouts with hotkeys. I'm thinking of using a combination such as ALT+1, ALT+2, ..., ALT+0 for saving and 1, 2, ... 0 for loading. Let's see isKeyPressed from SKSE works as intended :smile:

Edit: WHOA I WAS ABLE TO POST A LINK ;D

Edited by Deazurain, 08 April 2013 - 04:30 AM.


#20
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Great stuff, isKeyPressed worked like a charm. The player has 10 loadout slots and it is responsive enough to be usable.

[img]http://oi45.tinypic.com/2ivb80k.jpg[/img]

#21
SmkViper

SmkViper
  • Bethesda Game Studios
  • Programmer
  • Joined: 04-April 06
  • 350 posts
If you're interested in finding out what is slowing your script down, I would suggest using the built-in profiling tools like StartStackProfiling and StartScriptProfiling in the debug script, and StartObjectProfiling on Form. (Remember to turn on profiling in the ini file, the wiki pages have that information)

I think there's also a performance thread on the forums here floating around that can help with processing the output, but I don't have a link on me at the moment.

#22
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I think that I have a fairly good idea of what is causing the biggest slowdown currently:

  int n = 0; I used to use n as an array index
  int mask = 1
  while n < 31
    loadout.addForm(p.getWornForm(mask))
    n += 1
    mask *= 2
  endwhile

I could select a limited amount of masks but that would break compatibility with other mods. I don't think there is a way of speeding this up without splitting the job up into multiple 'threads' to trick the VM and reserve more processing time. For now I will just not swap equipment for 2-3 seconds after pressing a save loadout hotkey.

Thanks for the suggestion though, it might come in handy some other time.


#23
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Darn it, spell powers like 'Voice of the Emperor' aren't shouts so I'll have to handle these differently.

The wiki has never done a great job helping me out and I want to enrich it with my findings but I cant :'(. See the Actor.getEquippedSpell(int source) wiki gives the following info in the source parameter


0: Left hand
1: Right hand
2: Other
3: Instant


I'll have to test what instant and other are... And the next person who wants to use it, and the next next person...................

#24
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
I think "other" may be potion and instant is the "voice slot".

#25
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I have not looked at how the potion system works but I would guess that when a potion is consumed, a timed spell effect is added to the player. It doesnt make sense to me if potion is a spell in itself.

Another thing is, getEquippedSpell(int source) returns a single Object. How would you get all the potion spells (applied or available to the Actor) like that? 'Other' has to be a class of spells of which you can have only 1 equipped at a time.

#26
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
I think the potion slot is mainly for other NPCs, In the actor tab (or maybe it's the race window) there are check boxes of equip slots for what NPCs can use and potion is one of them.

#27
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
It's a mystery to me ^^ but you are probably right

#28
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
Well using the testcode:


function testGetEquippedSpell()
  Actor p = Game.getPlayer()
  dbg("getEquippedSpell(0): " + p.getEquippedSpell(0).getName(), true)
  dbg("getEquippedSpell(1): " + p.getEquippedSpell(1).getName(), true)
  dbg("getEquippedSpell(2): " + p.getEquippedSpell(2).getName(), true)
  dbg("getEquippedSpell(3): " + p.getEquippedSpell(3).getName(), true)
endfunction

gave this result:


[04/08/2013 - 06:26:03PM] DeazurainLoadout: getEquippedSpell(0): Flames
[04/08/2013 - 06:26:03PM] error: Cannot call GetName() on a None object, aborting function call
[04/08/2013 - 06:26:03PM] warning: Assigning None to a non-object variable named "::temp19"
[04/08/2013 - 06:26:03PM] DeazurainLoadout: getEquippedSpell(1):
[04/08/2013 - 06:26:03PM] DeazurainLoadout: getEquippedSpell(2): Voice of the Emperor
[04/08/2013 - 06:26:03PM] error: Cannot call GetName() on a None object, aborting function call
[04/08/2013 - 06:26:03PM] warning: Assigning None to a non-object variable named "::temp19"
[04/08/2013 - 06:26:03PM] DeazurainLoadout: getEquippedSpell(3):


I removed the stack traces, the errors were to be expected. It appears that powers fall under the category 'Other' which corresponds to aiSource = 2.

Edited by Deazurain, 08 April 2013 - 11:31 AM.


#29
mojo22

mojo22
  • Members
  • Pip
  • Curate
  • Joined: 03-January 11
  • 783 posts
FYI, you can cast your spell as a form and then call GetName on it for the function to work.

(PlayerRef.GetEquippedSpell(1) as Form).GetName()


#30
Deazurain

Deazurain
  • Members
  • Novice
  • Joined: 06-April 13
  • 28 posts
I don't know if I understand what you are trying to say. Doesn't this still produce a runtime error (or maybe even compile time):
(None as Form).getName()

It's never necessary to cast an object to a parent class if the class of the object doesn't override the function you're calling. In other words (pseudocode):

class Form
  function getName()
    return self.name
  endfunction
endclass

class Spell extends Form
endclass

if I have an object s of the type Spell, the following will do exactly the same:

s.getName()
(s as Form).getName()

It's one of the nice things in OOP :)

Note that it is not possible to create your real own type of object. There is supposed to be some workarounds though.


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users