Jump to content

Is it possible to find out what body slots armor in a container is using via Papyrus?


Recommended Posts

The closest I got was GetEquipType from the Papyrus Common Library, but that returns None on everything. Trying placeatme to then use GetEquipType on the object reference also returned none.

And if it didn't, that would also not be helpful, because there is zero info on what an EquipSlot even is, there is no information about it on the wiki other than an F4SE function to get the parent slots (i.e. an array of the thing there is zero information about).

Other than that I found GetArmorFormOnBipedSlot in Lighthouse, which works to find armor pieces on an actor, but obviously is no help with items in a container.

What I'm trying to do is a super dumb, slow, brute force way for actors to upgrade their armor: go through everything in actor's inventory, if it's armor, check the body slots, go through everything in container and look for armor that has the exact same combo of body slots, take note of all items that match and their item value, equip whatever has the highest value.

If all fails I could make something *extremely* dumb by having a mannequin in a holding cell, which equips each item tested and loops through all body slots to see where GetArmorFormOnBipedSlot returns the armor. But I'd rather not haha.

Link to comment
Share on other sites

I thought Instance Data can return the BipedSlots array but it appears there's no such function. I think I can add it to Garden of Eden if you're interested. (Garden of Eden currently has DoArmorSlotsOverlap(Armor akArmor1, Armor akArmor2) which returns True if equipping Armor1 on the Player Actor would result in unequipping Armor2. In theory, it should work).

Armor[] Function GetMatchingArmors(ObjectReference akInventoryOwner, Armor akArmor)
	Armor[] Armors = new Armor[0]
	If !akInventoryOwner
		Debug.MessageBox("Error: akInventoryOwner is None")
		Return Armors
	EndIf
	If !akArmor
		Debug.MessageBox("Error: akArmor is None")
		Return Armors
	EndIf
	Form[] InventoryItems = akInventoryOwner.GetInventoryItems()
	Int InventoryItemsSize = InventoryItems.Length
	If InventoryItemsSize == 0
		Debug.MessageBox("Error: InventoryItemsSize == 0")
		Return Armors
	EndIf
	Int Index = 0
	While Index < InventoryItemsSize
		Armor LoopArmor = InventoryItems[Index] as Armor
		If LoopArmor && akArmor != LoopArmor && GardenOfEden2.DoArmorSlotsOverlap(LoopArmor, akArmor)
			Armors.Add(LoopArmor)
		EndIf
		Index = Index + 1
	EndWhile
	Return Armors
EndFunction

 

  • Like 1
Link to comment
Share on other sites

3 hours ago, LarannKiar said:

I thought Instance Data can return the BipedSlots array but it appears there's no such function. I think I can add it to Garden of Eden if you're interested.

Oh, I'm super interested, that would be so useful!

I really want it to apply only to pieces that only occupy one helmet, chest, arm or leg armor a slot, and nothing else. Which I can also achieve with With DoArmorSlotsOverlap by making 2 items for each of the slots I care about, one that uses the slot, and one that uses all slots except the slot in question. Then I check if something overlaps with the first, but doesn't overlap with the second.

Performance isn't really an issue, something that runs lazily (i.e. don't loop through all armors in the container, just pick a random one to compare with what's worn) or only when triggered manually it is still a lot faster than trying to micromanage settlers.

Also thanks for the example function, I can just yoink that as is to make sure NPC don't unequip full body armor, should they wear any, to equip a single armor piece.

Edited by NeinGaming
Link to comment
Share on other sites

From preliminary testing (no time atm) I can say, this is so cool, and thank you so much! It is amazing to me how often people just write up perfectly valid code (instead of pseudo code), I really appreciate it. That stuff does take time, and a lot of people here do it as a matter of course even when some randos with zero published mods ask a question because they had an idea. I mean.

It would be a ton of work, but it's exciting to know that theoretically it's possible for actors to even weigh the pros and cons of one armor piece covering several slots, and individual pieces covering one or more of them. Ideally I'd get at the actual magic effects and armor rating and whatnot, but I'm also considering to just look at the attached mods and add up their their value.

Right now it's for settlers, to test, but in the long run I want everybody to just run around and loot things off each other 😄 I'll get there yet.

Speaking of, my dream is a Synthesis patcher that allows configuring the value/desirability of object effects (and/or the resulting magic effects), damage resistances etc. and then adjusts the bottle cap value of all ingestibles and armor mods in the load order to reflect that their "real value" (according to the config), so that you can simply sort by value and get at least a good estimate of how items compare. But that's neither here nor there, and way out of my league atm.

Link to comment
Share on other sites

3 hours ago, NeinGaming said:

From preliminary testing (no time atm) I can say, this is so cool, and thank you so much! It is amazing to me how often people just write up perfectly valid code (instead of pseudo code), I really appreciate it. That stuff does take time, and a lot of people here do it as a matter of course even when some randos with zero published mods ask a question because they had an idea. I mean.

It would be a ton of work, but it's exciting to know that theoretically it's possible for actors to even weigh the pros and cons of one armor piece covering several slots, and individual pieces covering one or more of them. Ideally I'd get at the actual magic effects and armor rating and whatnot, but I'm also considering to just look at the attached mods and add up their their value.

Right now it's for settlers, to test, but in the long run I want everybody to just run around and loot things off each other 😄 I'll get there yet.

Speaking of, my dream is a Synthesis patcher that allows configuring the value/desirability of object effects (and/or the resulting magic effects), damage resistances etc. and then adjusts the bottle cap value of all ingestibles and armor mods in the load order to reflect that their "real value" (according to the config), so that you can simply sort by value and get at least a good estimate of how items compare. But that's neither here nor there, and way out of my league atm.

It's a bit more complicated but possible. 🙂 

Bool Function EquipArmorWithHighestValue(Actor akInventoryOwner, Armor akArmor, bool abKeepBaseForm)
	If !akInventoryOwner
		Debug.MessageBox("Error: akInventoryOwner is None")
		Return False
	EndIf
	If !akArmor
		Debug.MessageBox("Error: akArmor is None")
		Return False
	EndIf
	Armor[] Armors = GetMatchingArmors(akInventoryOwner, akArmor)
	Int ArmorsSize = Armors.Length
	If ArmorsSize == 0
		Debug.MessageBox("Error: ArmorsSize == 0")
		Return False
	EndIf
	Int Index0 = 0
	Int HighestValue = 0
	Int ItemIndexWithHighestValue = -1
	While Index0 < ArmorsSize
		Armor LoopArmor = Armors[Index0] as Armor
		If LoopArmor && (!abKeepBaseForm || LoopArmor == akArmor)
			Int[] ArmorInstances = GardenOfEden.GetItemIndexesByFormID(akInventoryOwner, LoopArmor.GetFormID())
			Int ArmorInstancesSize = ArmorInstances.Length
			If ArmorInstancesSize == 0
				Debug.MessageBox("Error: ArmorInstancesSize == 0 at " + Index0 + " for Armor " + GardenOfEden.GetHexFormID(LoopArmor))
				Return False
			EndIf
			Int Index1 = 0
			While Index1 < ArmorInstancesSize
				Int Value = GardenOfEden.GetNthItemValue(akInventoryOwner, ArmorInstances[Index1])
				If HighestValue == Value
					ItemIndexWithHighestValue = ArmorInstances[Index1]
				ElseIf HighestValue < Value
					HighestValue = Value
					ItemIndexWithHighestValue = ArmorInstances[Index1]
				EndIf
				Index1 = Index1 + 1
			EndWhile
		EndIf
		Index0 = Index0 + 1
	EndWhile
	If ItemIndexWithHighestValue == -1
		Debug.MessageBox("Error: ItemIndexWithHighestValue == -1 |  HighestValue == " + HighestValue)
		Return False
	EndIf
	Int HighestItemIndex = GardenOfEden.GetInventoryItemCount(akInventoryOwner)
	If ItemIndexWithHighestValue > HighestItemIndex
		Debug.MessageBox("Error: ItemIndexWithHighestValue > HighestItemIndex")
		Return False
	EndIf
	Debug.MessageBox("Info: equipping Armor " + GardenOfEden.GetNthItemName(akInventoryOwner, ItemIndexWithHighestValue) + " with Value of " + HighestValue + " at ItemIndex " + ItemIndexWithHighestValue)
	GardenOfEden.EquipNthItem(akInventoryOwner, ItemIndexWithHighestValue)
	Return True
EndFunction

  • Like 1
Link to comment
Share on other sites

Oh wow!! Thank you so much again. I figured I might have to deal with object instances, but haven't done that so far, and you saved me a bunch of hours of experimenting, by giving me a baseline that works I can play around with and expand on. I swear I wasn't trying to talk you into that.

Just to give you an idea what a quantum leap this will be me for me, this is something I tried to hack into the excellent SKK Workshop Monitor (back then, even just making a activator that allows accessing a hidden container completely mystified me, modding mods was the best I could do).
 

Spoiler


    ObjectReference ThisWeaponRef = ContainerToUse.DropObject(ThisWeapon, 1)
    If (ThisWeaponRef != None)
        ObjectMod[] TheseMods = ThisWeaponRef.GetAllMods()
        thisValue += TheseMods.length
        TempContainer.AddItem(ThisRef, 1, True)
;        If (TheseMods.length > 0)
;           int iIndex = 0
;            While (iIndex < TheseMods.length)
;                objectmod ThisMod = TheseMods[iIndex]
;                MiscObject ThisMisc = ThisMod.GetLooseMod()
;                thisValue += (ThisMisc as ObjectReference).GetGoldValue() ;ThisMisc as Form?
;                iIndex += 1
;            EndWhile
;        EndIf
    Endif

It kinda says it all... I tried to figure out how to add up the value of mods on an item, failed for some reason (I don't recall what the issue was, but my whole approach was so bad it doesn't matter now), so I resorted to just adding the *number of* mods to the item value. 😄 That was so useless and frustrating I just gave up on it for a while.

Link to comment
Share on other sites

I mostly receive requests for Starfield mods but I'd like to update some of my Fallout 4 mods too and these functions were good practices. 🙂 It's also a pleasant change to write Papyrus scripts again.

Garden of Eden doesn't have "SetNthItemValue" and while F4SE's Form SetGoldValue can change the value of Loose Mod MiscItems, that doesn't change the value of the item because that is determined by the iValue Property Modifier of the ObjectMod and not by the value of the Loose Mod.

Since F4SE doesn't have "SetPropertyModifiers", you can't change iValue ingame. So, you have to create an ObjectMod in the Creation Kit, add your iValue to it and attach it to the target items with a script. Here's a simple function.

 
; adds akValueMod to all items in akInventoryOwner's inventory
; whose base forms can be found in akItemBases and have at least one ObjectMod
; from akTargetMods
Int Function AddValueModToItems(ObjectReference akInventoryOwner, Form[] akItemBases, ObjectMod[] akTargetMods, ObjectMod akValueMod) Global
	Int Result = -1
	If !akInventoryOwner
		Debug.MessageBox("Error: akInventoryOwner is None")
		Return Result
	EndIf
	Int akItemBasesSize = akItemBases.Length
	If akItemBasesSize == 0
		Debug.MessageBox("Error: akItemBasesSize == 0")
		Return Result
	EndIf
	Int akTargetModsSize = akTargetMods.Length
	If akTargetModsSize == 0
		Debug.MessageBox("Error: akTargetModsSize == 0")
		Return Result
	EndIf
	If !akValueMod
		Debug.MessageBox("Error: akValueMod is None")
		Return Result
	EndIf
	Int HighestItemIndex = GardenOfEden.GetInventoryItemCount(akInventoryOwner)
	Int Index0 = 1
	Int IncrementedItemValueCount = 0
	String sLog
	While Index0 <= HighestItemIndex
		Form LoopItem = Game.GetForm(GardenOfEden.GetNthItemFormID(akInventoryOwner, Index0))
		If LoopItem as Weapon || LoopItem as Armor
			If akItemBases.Find(LoopItem) >= 0
				ObjectMod[] SortedItemMods = GardenOfEden2.SortFormsByForms(akTargetMods as Form[], GardenOfEden2.GetNthItemMods(akInventoryOwner, Index0, bExcludeModColls = true) as Form[]) as ObjectMod[]
				Int SortedItemModsSize = SortedItemMods.Length
				If SortedItemModsSize > 0
					Int Index1 = 0
					While Index1 < SortedItemModsSize
						ObjectMod ItemMod = SortedItemMods[Index1] as ObjectMod
						If ItemMod
							MiscObject LooseMod = GardenOfEden2.GetLooseMod(ItemMod)
							If LooseMod
								Int OldValue = GardenOfEden.GetNthItemValue(akInventoryOwner, Index0)
								String ItemName = GardenOfEden.GetNthItemName(akInventoryOwner, Index0)
								Int AttachResult = GardenOfEden2.AttachModToNthItem(akInventoryOwner, Index0, akValueMod)
								If AttachResult == 1
									sLog = sLog + "[Item: " + ItemName + "] | [Mod: " + LooseMod.GetName() + "] | [OldValue: " + OldValue + "]\n"
									IncrementedItemValueCount = IncrementedItemValueCount + 1
								EndIf
							EndIf
						EndIf
						Index1 = Index1 + 1
					EndWhile
				EndIf
			EndIf
		EndIf
		Index0 = Index0 + 1
	EndWhile
	If IncrementedItemValueCount == 0 
		Result = 0
	ElseIf IncrementedItemValueCount == akTargetModsSize
		Result = 1
	Else
		Result = 2
	EndIf
	Debug.MessageBox("IncrementModValues: Result [" + Result + "] | \n" + sLog)
	Return Result
EndFunction
  • Like 1
Link to comment
Share on other sites

I don't like "requesting" things, but getting the body slots of armor would really be awesome indeed.

I can't quite follow your code though, why attach said value mod? What would properties would I give such that mod? I didn't have time to really meditate on the above code, but am I guessing correctly that I'd make a list of OMODs, determine how valuable they are, and then create an OMOD to reflect that value, that gets attached to all items that have any in the list of OMODs? That might be overkill for me, or maybe I just misunderstand (likely :D)

While it would be sweet to just get the real item value of a modded items after taking mods into account, or getting the value modifier an OMOD has (we can't get at that, right? I didn't look deep yet, I just searched all the reference files from various papyrus extenders for objectmod and it didn't seem that way), I think in the meantime adding up the raw value of the loose mod items might be a good enough approximation, at least something to play test with.

It's certainly a giant improvement over the nothing I had 2 days ago. Here's what I'm doing right now, I did it this way because I mistakenly thought I would get the real value from the item instance:
 

Spoiler

- get all armor forms in the actor inventory (need to add a check if they're actually equipped tho), loop through them

-- find the highest value instance of the armor form in the actor inventory

-- get all forms from the equipment container that overlap with the armor form, loop through them

--- get the highest highest value instance of the overlapping armor form in the equipment container

-- if the highest value instance in the equipment container is worth more than that on the actor, I'm using a temp container to swap to not invalidate the item indexes:

--- transfer the container instance to temp container A

--- transfer the actor instance to temp container B (after unequipping if need be)

--- transfer the instance from temp container A to the actor and equip it, and the instance from temp container B to the equipment container

I jumped in my chair the first time that worked without a hitch (did all sorts of silly mistakes at first, obviously).

Now I just need to plug actually looking at the mods into the function that gets the highest value instance of a form from a container, and there you go!

It can never be "perfect" anyway, that would require taking actor perks and who knows what else into account. Having settlers take the roughly best stuff, so you sell or scrap the things none of them have a use for, is already such a boon... I might have to look at the values of loose mod items and maybe balance them a little (haven't looked at them yet, but considering how totally different Nuka Colas tend to have the same price I assume the absolute worst), but I wouldn't mind doing that anyway.

Link to comment
Share on other sites

image.jpeg.61380b9dede1fa7e3ea179c1eed8c3d7.jpeg

Oh, yes!

I was VERY confused there for a while, until I realized I had to remove "akArmor != LoopArmor" from this bit so it wouldn't skip the same base type (since I'm obviously using it not to look in the actors own inventory, but in the container)

	If LoopArmor && akArmor != LoopArmor && GardenOfEden2.DoArmorSlotsOverlap(LoopArmor, akArmor)
			Armors.Add(LoopArmor)
		EndIf
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...