ReDragon2013 Posted May 8, 2018 Share Posted May 8, 2018 (edited) jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check):" Scriptname EquipmentSwappingScript extends activemagiceffect {Swaps equipment between NPC and Player, if that NPC does not have a designated outfit.} FormList Property playerGearForSwapping Auto FormList Property npcGearForSwapping Auto Actor Property PlayerREF Auto Function GetWornEquipmentButNotHands(Actor akTarget, FormList akWornForms) {adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor example} int index int slotsChecked slotsChecked += 0x00100000 slotsChecked += 0x00200000 ;ignore reserved slots slotsChecked += 0x80000000 int thisSlot = 0x01 while (thisSlot < 0x80000000) if (Math.LogicalAnd(slotsChecked, thisSlot) != thisSlot) ;only check slots we have not found anything equipped on already Armor thisArmor = akTarget.GetWornForm(thisSlot) as Armor if (thisArmor) akWornForms.AddForm(thisArmor) index += 1 slotsChecked += thisArmor.GetSlotMask() ;add all slots this item covers to our slotsChecked variable else ;no armor was found on this slot slotsChecked += thisSlot endif endif thisSlot *= 2 ;double the number to move on to the next slot endWhile EndFunction Function SwapEquipment(Actor akFirst, Actor akSecond, FormList akListOfItems) akFirst.RemoveItem(akListOfItems, 1, true, akSecond) EndFunction Function EquipPlayer() int idx = 0 Form[] listOfItems = npcGearForSwapping.ToArray() ;EquipItemEx will not take FormLists while(idx < listOfItems.Length) PlayerREF.EquipItemEx(listOfItems[idx]) idx += 1 endWhile EndFunction Function FindAndSwapGear(Actor akNPC) GetWornEquipmentButNotHands(PlayerREF, playerGearForSwapping) GetWornEquipmentButNotHands(akNPC, npcGearForSwapping) SwapEquipment(PlayerREF, akNPC, playerGearForSwapping) SwapEquipment(akNPC, PlayerREF, npcGearForSwapping) EquipPlayer() ;cleanup playerGearForSwapping.Revert() npcGearForSwapping.Revert() EndFunction Event OnEffectStart(Actor akTarget, Actor akCaster) if (akCaster == PlayerREF && akTarget.GetActorBase().GetOutfit().GetNumParts() == 0) ;to prevent item duplication FindAndSwapGear(akTarget) elseif(akTarget == PlayerREF) FindAndSwapGear(akCaster) endif EndEvent Dear jacobpaige,I find it offensive when someone who asks for help tries to teach the helpers. My English is not as good as yours, so I always try to take simple name convention that can understand everyone.Maybe someday you will get into such a situation. Nevertheless you have answered and shown gratitude.Here is the script optimized in runtime and stacksize usage, remember you gave us code snippets and wrote nothing about the mod purpose, that comes just slowly to the surface: Scriptname jpSwapArmorEffectScript extends ActiveMagicEffect {optimized by ReDragon 2018} ; swaps equipment between NPC and Player, if that NPC does not have a designated outfit. ; https://forums.nexusmods.com/index.php?/topic/6619756-code-optimization-questions/page-3 ; jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check)" ; "Thanks for all the suggestions by the way. I realize how much time it must have taken to write all that, ; and it's helpful to see the approaches that other people would have taken." FormList PROPERTY playerGearList auto Hidden ; playerGearForSwapping FormList PROPERTY npcGearList auto Hidden ; npcGearForSwapping Int iBusy = 2 ; threadlock variable ; make both actors persistent for a while by using script variables Actor playerRef Actor npcRef ;------------------------- Bool FUNCTION myF_IsSKSE() ;------------------------- IF (SKSE.GetVersion() > 0) Return TRUE ENDIF ;--------- Debug.Trace("jpSwapArmor: SKSE is missing! " +self) ; see "papyrus.0.log" within folder "..\My Games\Skyrim\Logs\Script" Return False ENDFUNCTION ;--------------------------------------------------------- Bool FUNCTION myF_IsPlayer(Actor akTarget, Actor akCaster) ;--------------------------------------------------------- playerRef = Game.GetPlayer() ; fill script variable IF (akTarget == playerRef) IF (akTarget == akCaster) Return False ; player is both ENDIF npcRef = akCaster ; npc is the caster Return TRUE ; * and player is target ENDIF ;--------- IF (akCaster == playerRef) ; https://www.creationkit.com/index.php?title=GetNumParts_-_Outfit outfit OT = akTarget.GetActorBase().GetOutfit() IF (OT) && (OT.GetNumParts() == 0) ; SKSE required! GetOutfit(), GetNumParts() npcRef = akTarget ; npc ist the target Return TRUE ; * and player is caster ENDIF ENDIF Return False ; player not found ENDFUNCTION ; -- EVENTs -- 3 EVENT OnEffectStart(Actor akTarget, Actor akCaster) IF myF_IsSKSE() ELSE self.Dispel() RETURN ; - STOP - SKSE not found ENDIF ;--------------------- IF myF_IsPlayer(akTarget, akCaster) ELSE self.Dispel() RETURN ; - STOP - something is bad ENDIF ;--------------------- RegisterForSingleUpdate(0.0) ; register additional thread RegisterForSingleUpdateGameTime(0.0) ; register additional thread ;--- WHILE (iBusy) ; (iBusy > 0) Utility.Wait(0.1) ; we are hold on here ENDWHILE ; until both update events are finished ;--- SwapEquipments() EquipPlayer() ; cleanup script variables playerGearList.Revert() npcGearList.Revert() ENDEVENT EVENT OnUpdate() GetWornEquipmentByPlayer() ENDEVENT EVENT OnUpdateGameTime() GetWornEquipmentByNPC() ENDEVENT ; -- FUNCTIONs -- (2) + 4 = 6 ;------------------------ FUNCTION SwapEquipments() ;------------------------ npcRef.RemoveItem(npcGearList as Form, 1, TRUE, playerRef as ObjectReference) ; to the player Utility.Wait(0.05) playerRef.RemoveItem(playerGearList as Form, 1, TRUE, npcRef as ObjectReference) ; to the npc ENDFUNCTION ;--------------------- FUNCTION EquipPlayer() ;--------------------- ;;; form[] a = npcGearList.ToArray() ; **OBSOLETE** int iMax = npcGearList.GetSize() int i = 0 WHILE (i < iMax) playerRef.EquipItemEx( npcGearList.GetAt(i) ) ; SKSE !! i = i + 1 ENDWHILE ENDFUNCTION ;---------------------------------- FUNCTION GetWornEquipmentByPlayer() ; Player action threading safe ;---------------------------------- ; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor ;/ *** avoid next SKSE provided slotmasks ; int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead ; int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate ; int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 ****** /; int iMask = 0x80300000 ; ignore slotmasks by default int i = 0x00000001 ; init by 1 WHILE (i < 0x80000000) IF (Math.LogicalAnd(iMask, i) == i) ; SKSE !! ; slots we have found equipped on already or excluded by default ELSE form fm = playerRef.GetWornForm(iSlot) ; SKSE !! IF (fm as Armor) playerGearList.AddForm(fm) iMask += fm.GetSlotMask() ; SKSE !! add all slots this item covers too ELSE iMask += i ; no armor was found on this slot ENDIF ENDIF i = i * 2 ; double the number to move on to the next slot ENDWHILE myF_Ready() ENDFUNCTION ;------------------- FUNCTION myF_Ready() ;------------------- iBusy = iBusy - 1 ; update threadlock variable ENDFUNCTION ;------------------------------- FUNCTION GetWornEquipmentByNPC() ; NPC action threading safe ;------------------------------- ; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor ;/ *** avoid next SKSE provided slotmasks ; int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead ; int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate ; int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 ****** /; int iMask = 0x80300000 ; ignore slotmasks by default int i = 0x00000001 ; init by 1 WHILE (i < 0x80000000) IF (Math.LogicalAnd(iMask, i) == i) ; SKSE !! ; slots we have found equipped on already or excluded by default ELSE form fm = npcRef.GetWornForm(iSlot) ; SKSE !! IF (fm as Armor) npcGearList.AddForm(fm) iMask += fm.GetSlotMask() ; SKSE !! add all slots this item covers too ELSE iMask += i ; no armor was found on this slot ENDIF ENDIF i = i * 2 ; double the number to move on to the next slot ENDWHILE myF_Ready() ENDFUNCTION ;/ *** SKSE implementation ******************************** int Property kSlotMask30 = 0x00000001 AutoReadOnly ; HEAD int Property kSlotMask31 = 0x00000002 AutoReadOnly ; Hair int Property kSlotMask32 = 0x00000004 AutoReadOnly ; BODY int Property kSlotMask33 = 0x00000008 AutoReadOnly ; Hands int Property kSlotMask34 = 0x00000010 AutoReadOnly ; Forearms int Property kSlotMask35 = 0x00000020 AutoReadOnly ; Amulet int Property kSlotMask36 = 0x00000040 AutoReadOnly ; Ring int Property kSlotMask37 = 0x00000080 AutoReadOnly ; Feet int Property kSlotMask38 = 0x00000100 AutoReadOnly ; Calves int Property kSlotMask39 = 0x00000200 AutoReadOnly ; SHIELD int Property kSlotMask40 = 0x00000400 AutoReadOnly ; TAIL int Property kSlotMask41 = 0x00000800 AutoReadOnly ; LongHair int Property kSlotMask42 = 0x00001000 AutoReadOnly ; Circlet int Property kSlotMask43 = 0x00002000 AutoReadOnly ; Ears int Property kSlotMask44 = 0x00004000 AutoReadOnly ; Unnamed int Property kSlotMask45 = 0x00008000 AutoReadOnly ; Unnamed int Property kSlotMask46 = 0x00010000 AutoReadOnly ; Unnamed int Property kSlotMask47 = 0x00020000 AutoReadOnly ; Unnamed int Property kSlotMask48 = 0x00040000 AutoReadOnly ; Unnamed int Property kSlotMask49 = 0x00080000 AutoReadOnly ; Unnamed int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead * masked out int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate * masked out int Property kSlotMask52 = 0x00400000 AutoReadOnly ; Unnamed int Property kSlotMask53 = 0x00800000 AutoReadOnly ; Unnamed int Property kSlotMask54 = 0x01000000 AutoReadOnly ; Unnamed int Property kSlotMask55 = 0x02000000 AutoReadOnly ; Unnamed int Property kSlotMask56 = 0x04000000 AutoReadOnly ; Unnamed int Property kSlotMask57 = 0x08000000 AutoReadOnly ; Unnamed int Property kSlotMask58 = 0x10000000 AutoReadOnly ; Unnamed int Property kSlotMask59 = 0x20000000 AutoReadOnly ; Unnamed int Property kSlotMask60 = 0x40000000 AutoReadOnly ; Unnamed int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 * masked out ************************************************************ /; Once more for the future, if you use script code or suggestions provided by other forum users,its a good idea to say thank you within the source code, not only anonymous inside a forum threadthat will be forgotten next month. Edited May 11, 2018 by ReDragon2013 Link to comment Share on other sites More sharing options...
jacobpaige Posted May 8, 2018 Author Share Posted May 8, 2018 Dear jacobpaige, I find it offensive when someone who asks for help tries to teach the helpers. My English is not as good as yours, so I always try to take simple name convention that can understand everyone.Maybe someday you will get into such a situation. Nevertheless you have answered and shown gratitude. Here is the script optimized in runtime and stacksize usage, remember you gave us code snippets and wrote nothing about the mod purpose, that comes just slowly to the surface: Scriptname jpSwapArmorEffectScript extends ActiveMagicEffect {optimized by ReDragon 2018} ; swaps equipment between NPC and Player, if that NPC does not have a designated outfit. ; https://forums.nexusmods.com/index.php?/topic/6619756-code-optimization-questions/page-3 ; jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check)" ; "Thanks for all the suggestions by the way. I realize how much time it must have taken to write all that, ; and it's helpful to see the approaches that other people would have taken." FormList PROPERTY playerGearList auto Hidden ; playerGearForSwapping FormList PROPERTY npcGearList auto Hidden ; npcGearForSwapping Int iBusy = 2 ; threadlock variable ; make both actors persistent for a while by using script variables Actor playerRef Actor npcRef ;------------------------- Bool FUNCTION myF_IsSKSE() ;------------------------- IF (SKSE.GetVersion() > 0) Return TRUE ENDIF ;--------- Debug.Trace("jpSwapArmor: SKSE is missing! " +self) ; see "papyrus.0.log" within folder "..\My Games\Skyrim\Logs\Script" Return False ENDFUNCTION ;--------------------------------------------------------- Bool FUNCTION myF_IsPlayer(Actor akTarget, Actor akCaster) ;--------------------------------------------------------- playerRef = Game.GetPlayer() IF (akTarget == playerRef) IF (akTarget == akCaster) Return False ; player is both ENDIF npcRef = akCaster ; npc is the caster Return TRUE ; * and player is target ENDIF ;--------- IF (akCaster == playerRef) ; https://www.creationkit.com/index.php?title=GetNumParts_-_Outfit IF (akTarget.GetActorBase().GetOutfit().GetNumParts() == 0) ; SKSE required! GetOutfit(), GetNumParts() npcRef = akTarget ; npc ist the target Return TRUE ; * and player is caster ENDIF ENDIF Return False ; player not found ENDFUNCTION ; -- EVENTs -- 3 EVENT OnEffectStart(Actor akTarget, Actor akCaster) IF myF_IsSKSE() ELSE self.Dispel() RETURN ; - STOP - SKSE not found ENDIF ;--------------------- IF myF_IsPlayer(akTarget, akCaster) ELSE self.Dispel() RETURN ; - STOP - something is bad or missing player ENDIF ;--------------------- RegisterForSingleUpdate(0.0) ; create a second thread RegisterForSingleUpdateGameTime(0.0) ; create a third thread ;--- WHILE (iBusy) ; (iBusy > 0) Utility.Wait(0.25) ; we are hold on here ENDWHILE ; until both update events are finished ;--- SwapEquipments() EquipPlayer() playerGearList.Revert() ; cleanup both formlists npcGearList.Revert() ENDEVENT EVENT OnUpdate() GetWornEquipmentByPlayer() ENDEVENT EVENT OnUpdateGameTime() GetWornEquipmentByNPC() ENDEVENT ; -- FUNCTIONs -- (2) + 4 = 6 ;------------------------ FUNCTION SwapEquipments() ;------------------------ playerRef.RemoveItem(playerGearList, 1, TRUE, npcRef as ObjectReference) npcRef.RemoveItem(npcGearList, 1, TRUE, playerRef as ObjectReference) ENDFUNCTION ;--------------------- FUNCTION EquipPlayer() ;--------------------- ;;; form[] a = npcGearList.ToArray() ; **OBSOLETE** int iMax = npcGearList.GetSize() int i = 0 WHILE (i < iMax) playerRef.EquipItemEx( npcGearList.GetAt(i) ) ; SKSE !! i = i + 1 ENDWHILE ENDFUNCTION ;---------------------------------- FUNCTION GetWornEquipmentByPlayer() ; Player action threading safe ;---------------------------------- ; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor ;/ *** avoid next SKSE provided slotmasks int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 ****** /; int iMask = 0x80300000 ; ignore slotmasks by default int iSlot = 0x00000001 ; init by 1 int i WHILE (iSlot < 0x80000000) IF (Math.LogicalAnd(iMask, iSlot) == iSlot) ; SKSE !! ; slots we have found equipped on already or excluded by initial ELSE form fm = playerRef.GetWornForm(iSlot) ; SKSE !! IF (fm as Armor) playerGearList.AddForm(fm) i = i + 1 iMask += fm.GetSlotMask() ; SKSE !! add all slots this item covers too ELSE iMask += iSlot ; no armor was found on this slot ENDIF ENDIF iSlot = iSlot * 2 ; double the number to move on to the next slot ENDWHILE iBusy = iBusy - 1 ENDFUNCTION ;------------------------------- FUNCTION GetWornEquipmentByNPC() ; NPC action threading safe ;------------------------------- ; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor ;/ *** avoid next SKSE provided slotmasks int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 ****** /; int iMask = 0x80300000 ; ignore slotmasks by default int iSlot = 0x00000001 ; init by 1 int i WHILE (iSlot < 0x80000000) IF (Math.LogicalAnd(iMask, iSlot) == iSlot) ; SKSE !! ; slots we have found equipped on already or excluded by initial ELSE form fm = npcRef.GetWornForm(iSlot) ; SKSE !! IF (fm as Armor) npcGearList.AddForm(fm) i = i + 1 iMask += fm.GetSlotMask() ; SKSE !! add all slots this item covers too ELSE iMask += iSlot ; no armor was found on this slot ENDIF ENDIF iSlot = iSlot * 2 ; double the number to move on to the next slot ENDWHILE iBusy = iBusy - 1 ENDFUNCTION ;/ *** SKSE implementation ******************************** int Property kSlotMask30 = 0x00000001 AutoReadOnly ; HEAD int Property kSlotMask31 = 0x00000002 AutoReadOnly ; Hair int Property kSlotMask32 = 0x00000004 AutoReadOnly ; BODY int Property kSlotMask33 = 0x00000008 AutoReadOnly ; Hands int Property kSlotMask34 = 0x00000010 AutoReadOnly ; Forearms int Property kSlotMask35 = 0x00000020 AutoReadOnly ; Amulet int Property kSlotMask36 = 0x00000040 AutoReadOnly ; Ring int Property kSlotMask37 = 0x00000080 AutoReadOnly ; Feet int Property kSlotMask38 = 0x00000100 AutoReadOnly ; Calves int Property kSlotMask39 = 0x00000200 AutoReadOnly ; SHIELD int Property kSlotMask40 = 0x00000400 AutoReadOnly ; TAIL int Property kSlotMask41 = 0x00000800 AutoReadOnly ; LongHair int Property kSlotMask42 = 0x00001000 AutoReadOnly ; Circlet int Property kSlotMask43 = 0x00002000 AutoReadOnly ; Ears int Property kSlotMask44 = 0x00004000 AutoReadOnly ; Unnamed int Property kSlotMask45 = 0x00008000 AutoReadOnly ; Unnamed int Property kSlotMask46 = 0x00010000 AutoReadOnly ; Unnamed int Property kSlotMask47 = 0x00020000 AutoReadOnly ; Unnamed int Property kSlotMask48 = 0x00040000 AutoReadOnly ; Unnamed int Property kSlotMask49 = 0x00080000 AutoReadOnly ; Unnamed int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead * masked out int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate * masked out int Property kSlotMask52 = 0x00400000 AutoReadOnly ; Unnamed int Property kSlotMask53 = 0x00800000 AutoReadOnly ; Unnamed int Property kSlotMask54 = 0x01000000 AutoReadOnly ; Unnamed int Property kSlotMask55 = 0x02000000 AutoReadOnly ; Unnamed int Property kSlotMask56 = 0x04000000 AutoReadOnly ; Unnamed int Property kSlotMask57 = 0x08000000 AutoReadOnly ; Unnamed int Property kSlotMask58 = 0x10000000 AutoReadOnly ; Unnamed int Property kSlotMask59 = 0x20000000 AutoReadOnly ; Unnamed int Property kSlotMask60 = 0x40000000 AutoReadOnly ; Unnamed int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01 * masked out ************************************************************ /; Once more for the future, if you use script code or suggestions provided by other forum users,its a good idea to say thank you within the source code, not only anonymous inside a forum threadthat will be forgotten next month. I actually put the credit in the mod description where people are actually likely to see it. You can click the link in my signature if you'd like to check. Also, I like to help people, especially those that are trying to help me. I'm sorry that my attempts to help offended you instead. I didn't realize that there were two updates you could simultaneously register for. I'll have to revisit the code and see if I can work that in without breaking anything :wink: Thanks :smile: In the SwapEquipments function, why are you casting the two Actors as ObjectRefrences? I did originally try what you're doing in EquipPlayer with the FormList, and while it did compile, it didn't actually work. Though, looking back on it, I was still making a lot of mistakes with properties, so I'll give it another go and make sure that that's not what was going on. Does threading not spawn a copy of the function and its variables for each thread? If it does, then why have two GetWornEquipment functions? If it doesn't, then that's incredibly bad design. Link to comment Share on other sites More sharing options...
jacobpaige Posted May 8, 2018 Author Share Posted May 8, 2018 How you approach this is going to depend on what exactly you're after. As I understand it, you're trying to do the following: 1. Allow the player to swap their equipped gear with that on a mannequin or follower (but not NPCs in the general case?);2. Allow them to do so either through activating the mannequin or casting a spell on either the mannequin or the follower. (Maybe also through follower dialogue? Not specified yet);3. Do so in a way that will play relatively nicely with other mannequin-modifying mods;4. Still allow vanilla activate->show inventory mannequin inventory management to occur. Is that correct? Are there any other pieces to this? Am I missing anything? Well, the minimum functionality was just being able to swap gear with mannequins and followers. Since all NPCs have pre-determined Outfits, it's pretty easy to rule out non-followers, but if people want to mod their games to get rid of Outfits on all NPCs, then this will wind up working on non-followers. For the moment, I've decided not to care about that edge case. If it becomes an issue, I'll add in more explicit logic to identify followers. As for casting a spell on the mannequin, that was my second approach (after giving up on dynamic menus), but I couldn't get the spell to hit the mannequin. Even when I turned off it's isGhost and isInvulnerable flags, it still just ignored the spell. It's why I switched to having it cast the spell at the player on activation instead. Regardless, I wanted to avoid doing it through dialogue since follower dialogue trees can get pretty crowded as it is and a large part of this mod is the convenience factor, which would be reduced by having to go through the dialogue options. It would definitely be nice if it didn't interfere with other mannequin mods, but I wasn't able to manage it. It's not hard to patch my modifications in (assuming you don't care about accessing the inventory directly), but it does currently require a patch. Honestly, I'm a bit on the fence about the fourth one. It wouldn't be that difficult to implement, but it would slow things down compared to my current implementation, and I'm not sure how much use people would actually get out of it. Link to comment Share on other sites More sharing options...
simtam Posted May 8, 2018 Share Posted May 8, 2018 Or just rewrite your main function to have it all in a single loop, so that the first equipment swap can start right away. Maybe even add some fancy visual effect that justifies the spell taking it's time. With video games it's more important to provide quick feedback to the action, then to complete it instantly. Link to comment Share on other sites More sharing options...
jacobpaige Posted May 8, 2018 Author Share Posted May 8, 2018 Or just rewrite your main function to have it all in a single loop, so that the first equipment swap can start right away. Maybe even add some fancy visual effect that justifies the spell taking it's time. With video games it's more important to provide quick feedback to the action, then to complete it instantly. It's already giving visual feedback with the healing animation. It just takes a few seconds after that for the actual swap to occur, which is what I was trying to fix. Link to comment Share on other sites More sharing options...
jacobpaige Posted May 8, 2018 Author Share Posted May 8, 2018 @ReDragon2013 I tried both the threading suggestion and the FormList suggestion, neither worked. Not sure if I'm doing something wrong, but as far as I can tell, I'm doing the same thing as you, just with different variable names. Link to comment Share on other sites More sharing options...
foamyesque Posted May 9, 2018 Share Posted May 9, 2018 (edited) FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash. @jacob: I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic. Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower? Edited May 9, 2018 by foamyesque Link to comment Share on other sites More sharing options...
jacobpaige Posted May 9, 2018 Author Share Posted May 9, 2018 FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash. @jacob: I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic. Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower?How would you access the mannequin's inventories? I haven't been able to find anything that would let me do that short of removing everything then sending it all back and monitoring it through an OnAddItem event, which requires a chest for each mannequin. Or do you mean their equipped items? Either way, if the player walks into a room with 50 mannequins, wouldn't polling them all for their outfits create a noticeable lag? Especially for people with lots of mods that use scripts? Or were you thinking something like a cloaking spell with a very limited range so that you'd never catch more than a few mannequins at a time? So, how would the player decide if they wanted normal (whatever that means for them) activation vs. an equipment swap? Tie the perk to whether or not the spell was equipped in the player's hand or something? Would that also be able to switch back to the normal activation if the player unequipped the spell? I've not done much with quests or perks yet so I'm very hazy on the details. At minimum, active followers, at maximum, those that are currently willing to follow. This is mainly meant to make it quick and easy for people to rearrange their and their followers' wardrobes without having to go through a few dozen menus and dialogue trees while the followers constantly make discontent comments about carrying their burdens. I'm sure there are plenty of people out there, especially RPrs and fashionistas, that like to change everyone's outfits frequently. I just wanted to make that process less painful. Link to comment Share on other sites More sharing options...
foamyesque Posted May 9, 2018 Share Posted May 9, 2018 FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash. @jacob: I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic. Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower?How would you access the mannequin's inventories? I haven't been able to find anything that would let me do that short of removing everything then sending it all back and monitoring it through an OnAddItem event, which requires a chest for each mannequin. Or do you mean their equipped items? Either way, if the player walks into a room with 50 mannequins, wouldn't polling them all for their outfits create a noticeable lag? Especially for people with lots of mods that use scripts? Or were you thinking something like a cloaking spell with a very limited range so that you'd never catch more than a few mannequins at a time? So, how would the player decide if they wanted normal (whatever that means for them) activation vs. an equipment swap? Tie the perk to whether or not the spell was equipped in the player's hand or something? Would that also be able to switch back to the normal activation if the player unequipped the spell? I've not done much with quests or perks yet so I'm very hazy on the details. At minimum, active followers, at maximum, those that are currently willing to follow. This is mainly meant to make it quick and easy for people to rearrange their and their followers' wardrobes without having to go through a few dozen menus and dialogue trees while the followers constantly make discontent comments about carrying their burdens. I'm sure there are plenty of people out there, especially RPrs and fashionistas, that like to change everyone's outfits frequently. I just wanted to make that process less painful. I meant their equipped items, but accessing their inventories is also possible (much less painful with SKSE, but doable even in vanilla). With SKSE, you can use GetContainerForms() to get a Form[] array back, containing every form the actor has at least one of, or alternatively you could iterate through a GetNthForm() loop. GetContainerForms() is by far the better choice; it's not documented on the wiki, but I use it extensively after being pointed to it by cdcooley, and it works beautifully. In vanilla, you'd need to do the dump/onitemremoved combination to build a list, then bring everything on that list back. It's awkward and slow and suboptimal, but possible. As regards processing a lot of mannequins: Yes, there's some impingement on the script engine, but because you're running the scripts on separate aliases, you get to take advantage of papyrus's multithreading; they'll all run slower, but they'll also all complete at more or less the same time. The total execution time is generally significantly shorter than running them all in sequence would be, too, because of the way the frame-linked functions work. There's a period of increased vulnerability to stack dumps if something else dramatic happens during the execution, but it isn't terribly likely to happen. W.r.t. the activation option, look at how the Tower perk is implemented in the CK. What happens ingame is that you get a messagebox popup when you activate something (which you can filter through the condition systems) that looks like this: https://youtu.be/lhX108ee0U4?t=320 There'll be the normal activation text, that executes whatever OnActivate and default processing occurs, and then there'll be your custom activation option that can be chosen. It's rather obtrusive, but this may let you get things to play nice with other scripts. As I said, I want to test this, since I'm not completely sure of what will happen with competing OnActivate() events. There's some other approaches that could be taken with perks to shift things around too that I might experiment with. W.r.t followers: given the target range, I think a check against PotentialFollowerFaction will be more accurate (and faster) than the GetOutfit() check you're currently using. Link to comment Share on other sites More sharing options...
jacobpaige Posted May 9, 2018 Author Share Posted May 9, 2018 I meant their equipped items, but accessing their inventories is also possible (much less painful with SKSE, but doable even in vanilla). With SKSE, you can use GetContainerForms() to get a Form[] array back, containing every form the actor has at least one of, or alternatively you could iterate through a GetNthForm() loop. GetContainerForms() is by far the better choice; it's not documented on the wiki, but I use it extensively after being pointed to it by cdcooley, and it works beautifully. In vanilla, you'd need to do the dump/onitemremoved combination to build a list, then bring everything on that list back. It's awkward and slow and suboptimal, but possible. As regards processing a lot of mannequins: Yes, there's some impingement on the script engine, but because you're running the scripts on separate aliases, you get to take advantage of papyrus's multithreading; they'll all run slower, but they'll also all complete at more or less the same time. The total execution time is generally significantly shorter than running them all in sequence would be, too, because of the way the frame-linked functions work. There's a period of increased vulnerability to stack dumps if something else dramatic happens during the execution, but it isn't terribly likely to happen. W.r.t. the activation option, look at how the Tower perk is implemented in the CK. What happens ingame is that you get a messagebox popup when you activate something (which you can filter through the condition systems) that looks like this: https://youtu.be/lhX108ee0U4?t=320 There'll be the normal activation text, that executes whatever OnActivate and default processing occurs, and then there'll be your custom activation option that can be chosen. It's rather obtrusive, but this may let you get things to play nice with other scripts. As I said, I want to test this, since I'm not completely sure of what will happen with competing OnActivate() events. There's some other approaches that could be taken with perks to shift things around too that I might experiment with. W.r.t followers: given the target range, I think a check against PotentialFollowerFaction will be more accurate (and faster) than the GetOutfit() check you're currently using. You have no idea how much time I've wasted on trying to find a better way to access the entire inventory. It'll help a ton with another mod I'm working on. If I alter one form in the list though, will it alter every instance, or just a random one? Specifically, I'm changing armor and damage ratings on equipment. Ah, I thought you meant the Tower of Strength perk, not the ability you get from the Tower Stone. That sounds like a much better idea than what I've been doing and should completely remove the need for compatibility patches with other mods :D I'll have a look at the perk and see how it's doing what it's doing and if I can replicate it. Thanks :smile: Well, the GetOutfit check is necessary to prevent item duplication. The fact that it also let me find followers was an added bonus that allowed me to not have it as a separate check. Still, if it's an attribute that can be quickly checked, rather than a list that has to be compiled and iterated over, then it's probably worth doing anyway. I'm just not sure if it would cause problems with custom followers. I've not made one before, so I don't know if PotentialFollowerFaction is a required faction for anyone that might follow you. Link to comment Share on other sites More sharing options...
Recommended Posts