IsharaMeradin Posted April 27, 2023 Share Posted April 27, 2023 I have the following procedure within a functioning script for xEdit Spoiler procedure ProcessAmmo(e: IInterface); var i: integer; keywords: IInterface; kwd, list: string; begin // skip main quest arrow if EditorID(e) = 'MQ101SteelArrow' then Exit; // skip test bolts If EditorID(e) = 'TestDLC1Bolt' then Exit; // skip non-playable by flag or missing name if (GetElementNativeValues(e, 'DATA\Flags') and 2 <> 0) or not ElementExists(e, 'FULL') then Exit; // skip bound projectiles (checking for WeapTypeBoundArrow [KYWD:0010D501]) keywords := ElementBySignature(e, 'KWDA'); for i := 0 to Pred(ElementCount(keywords)) do begin kwd := EditorID(LinksTo(ElementByIndex(keywords, i))); if kwd = 'WeapTypeBoundArrow' then Exit; end; // skip practice arrows (damage=0) if GetElementNativeValues(e, 'DATA\Damage') = 0 then Exit; // try using Data flags to sort between arrow and bolt if (GetElementNativeValues(e, 'DATA\Flags') and 4 <> 0) then list := 'abim_IMS_TotalArrowList' else list := 'abim_IMS_TotalBoltList'; // determine arrow or bolt by 'bolt' word in the name // if Pos('bolt', LowerCase(GetElementEditValues(e, 'FULL'))) <> 0 then // list := 'abim_IMS_TotalBoltList' // else // list := 'abim_IMS_TotalArrowList'; AddObjectToList(list, e); end; This particular procedure grabs items that are either arrows or bolts and puts them into one of two form lists. I would like to modify this in such a way that the end result of the lists are in order of base damage, preferably highest to smallest. Is this even possible? And if it is possible, may I get an example that I could try to adapt? Link to comment Share on other sites More sharing options...
IsharaMeradin Posted April 27, 2023 Author Share Posted April 27, 2023 In looking at my script a bit further, I probably should have included the AddObjectToList procedure as well. Spoiler procedure AddObjectToList(list: string; comp: IInterface); var g, flst, entries, entry: IInterface; i: integer; begin // skip injected records //Adding all plugins as masters to the patch file at a specific point //should solve the need to bypass injected records // If isInjected(comp) then // Exit; // exclude Hearthfires Sawn Log If EditorID(comp) = 'BYOHMaterialLog' then Exit; // exclude gold currency If EditorID(comp) = 'Gold001' then Exit; // create new patch plugin if doesn't exist yet // creates an esl flagged esp in SSEEdit if not Assigned(ModPatch) then begin if wbAppName = 'TES5' then ModPatch := AddNewFileName(sModPatch) else ModPatch := AddNewFileName(sModPatch,true); // if failed for some reason if not Assigned(ModPatch) then raise Exception.Create('Error creating a new patch plugin!'); end; // getting list record from patch file g := GroupBySignature(ModPatch, 'FLST'); flst := MainRecordByEditorID(g, list); // copy as override from master if doesn't exist yet if not Assigned(flst) then begin g := GroupBySignature(ModMaster, 'FLST'); flst := MainRecordByEditorID(g, list); if not Assigned(flst) then raise Exception.Create(list + ' FLST doesn''t exist in the master file!'); // add masters to patch plugin before copying record AddRequiredElementMasters(flst, ModPatch, False); // copy as override into patch flst := wbCopyElementToFile(flst, ModPatch, False, True); end; entries := ElementByName(flst, 'FormIDs'); if not Assigned(entries) then entries := Add(flst, 'FormIDs', True); for i := Pred(ElementCount(entries)) downto 0 do begin entry := ElementByIndex(entries, i); // component already in list if GetLoadOrderFormID(LinksTo(entry)) = GetLoadOrderFormID(comp) then Exit; // we checked all items in list but still no match found, add component to list if i = 0 then begin // if the first entry is not NULL then add a new one if Assigned(LinksTo(entry)) then entry := ElementAssign(entries, HighInteger, nil, False); //AddMessage('Processing '+EditorID(comp)+'...'); AddMasterIfMissing(ModPatch, GetFileName(comp)); SetEditValue(entry, Name(comp)); end; end; end; As I understand it, the ProcessAmmo procedure looks at the individual records and when finding a match sends it off to the AddObjectToList procedure where the list gets updated. I need to go through that list, find the highest damage value. Then go through the list as many times as needed while going down from the highest damage value to zero. Probably would be best to create a new list in memory that would then become the final list to push into the plugin. psudeo code? for i := Pred(ElementCount(entries)) downto 0 do begin d := 0 if GetElementNativeValues(e, 'DATA\Damage') > d d := GetElementNativeValues(e, 'DATA\Damage') end for i := d downto 0 for i := Pred(ElementCount(entries)) downto 0 do begin if GetElementNativeValues(e, 'DATA\Damage') = d //add to new internal list end end //assign internal list to formlist record some how But my thinking is more along the lines with Papyrus and not Pascal. I have no idea what is or is not possible with Pascal. Link to comment Share on other sites More sharing options...
ghastley Posted April 28, 2023 Share Posted April 28, 2023 FormList is an unordered collection implemented on an ordered list, meaning that the underlying list has an order for retrieval, but does not support any form of re-ordering. New items are always appended to the list, and cannot be added anywhere but the end. What is your use case for an ordered form list? Link to comment Share on other sites More sharing options...
lmstearn Posted April 28, 2023 Share Posted April 28, 2023 Bit of a greenhorn with this stuff - it looks from the docs as if you can get the (ordered) values into a an array buffer from TList.Create, and then push them into an empty FormList. Also ref the Alphabetic Sort. But it seems that existing elements in FormList can't be removed, unfortunately. @ghastley: Just curious, is it even possible to replace the entire FormList object with another "matching" object created from script? Link to comment Share on other sites More sharing options...
ghastley Posted April 28, 2023 Share Posted April 28, 2023 My question was really about what the sorted list would be used for. Most uses of FormList objects is to test an item for membership of a class. E.g Hearthfires will use a FormList for all the things that belong in a specific room. This can be a mixture of furniture, clutter, and misc items you could pick up. The only thing sequence is used for is to match workbench recipes to room content by their positions in the list to the index of the property array. In this case the array can be ordered in the CK to match the list sequence, so the list doesn’t need to be sorted, just to have a fixed order. If the intent here is to pick the “best” ammo then FormLists aren’t the place to start. The choice will be made by comparing what the player has available, not what exists in the game. FormLists are static, except that loading a mod that adds ammo will run a first-time script to add to the base game list, and that will mess up the order by appending the added ammo without regard to the sequence. xEdit scripts work on a single esp’s lists, and won’t affect the in-game result the way I suspect is wanted. Link to comment Share on other sites More sharing options...
IsharaMeradin Posted April 28, 2023 Author Share Posted April 28, 2023 The script I have works with the entire active load order and will populate multiple empty formlists for a variety of uses. Two of which are for arrows and bolts. Currently, the player can, with an MCM, choose what order they want the ammo to be in. Separate empty form lists are populated with the user designated order after the MCM has been exited. And then ammo stored in a container is compared against the ordered list. First matching one gets used. I was hoping to have a way to re-order by damage so that most users would just need to tweak a couple entries rather than scrolling down a long list of arrows while keeping track of what ones they have or have not selected. Unfortunately, the MCM menu option box covers up a majority of the option displays making it a little difficult to keep track. I am working on the logic for papyrus and will test a run-time variant as a separate mod to see how bad it might be timing wise. Avoiding form lists altogether and just look into the container directly via looping with SKSE's GetNthForm. No MCM, just cycle through get the highest damage value then cycle through again and grab the first one with the found damage value. If it is not too bad, I might go ahead with that route. I was looking, however, for a way to adapt what I already have without "re-inventing the wheel". Link to comment Share on other sites More sharing options...
IsharaMeradin Posted April 29, 2023 Author Share Posted April 29, 2023 Maybe re-inventing the wheel was a good idea. While the player cannot specify a specific order, the fetching of the highest damage arrow is rather quick. But because it pulls from a container (bag of holding type), the player can choose to not put in arrows that they do not want to be "auto-equipped". Downside, OnPlayerBowShot does not work with crossbows. Didn't think it would but nothing said that it wouldn't either. So, I'll still need to build a form list that can be used as an inventory event filter for the OnItemRemoved event, but that list can be built in game as bolts are added to the container. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now