NeinGaming Posted Wednesday at 10:17 AM Share Posted Wednesday at 10:17 AM 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 More sharing options...
LarannKiar Posted Thursday at 09:31 AM Share Posted Thursday at 09:31 AM 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 1 Link to comment Share on other sites More sharing options...
NeinGaming Posted Thursday at 12:48 PM Author Share Posted Thursday at 12:48 PM (edited) 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 Thursday at 12:49 PM by NeinGaming Link to comment Share on other sites More sharing options...
NeinGaming Posted Thursday at 03:45 PM Author Share Posted Thursday at 03:45 PM 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 More sharing options...
LarannKiar Posted Thursday at 07:11 PM Share Posted Thursday at 07:11 PM 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 1 Link to comment Share on other sites More sharing options...
NeinGaming Posted Thursday at 10:21 PM Author Share Posted Thursday at 10:21 PM 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 More sharing options...
LarannKiar Posted Friday at 10:03 AM Share Posted Friday at 10:03 AM 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 1 Link to comment Share on other sites More sharing options...
NeinGaming Posted Friday at 02:20 PM Author Share Posted Friday at 02:20 PM 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 More sharing options...
NeinGaming Posted Friday at 03:09 PM Author Share Posted Friday at 03:09 PM 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 More sharing options...
NeinGaming Posted Friday at 03:38 PM Author Share Posted Friday at 03:38 PM Come to think of it: In addition to DoArmorSlotsOverlap(), a function that checks for an exact match would be really useful! Not as a request, just as an objectively true statement ^^ Link to comment Share on other sites More sharing options...
Recommended Posts