Jump to content

xEdit script assistance needed


IsharaMeradin

Recommended Posts

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

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

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

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

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

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

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...