Jump to content

Dynamically create/filled formlist script ala SkyProc(Tes5edit?) for revolutionary mod


irswat

Recommended Posts

I recently began coding a voice command engine for skyrim. This is not like the voice engines in the past that relied on hotkeys, macros, and had limited functionality. No. This mod will be robust and dynamic, meaning it will support 3rd party mods from the get go. I already have a significant portion of the parsing and command engine completed, and it's coming along rapidly.

Half the functionality relies on a parsing engine that breaks a command into types (e.g. material types, weapon/armor type, equipping slot, unique identifiers, etc). The other half of the engine relies on robust and dynamically created formlists of a users weapons, armors, arrows, shouts, spells, potions based upon their load order.

In order to expedite the release on this I need help. I need someone who is able to make either a SkyProc patcher, or a lightweight app based on the tes5edit framework, that will load a users load order, and then dynamically create and fill formlists as aforementioned. The formlists need to then be injected into a blank esp. Pretty easy. If you are interested and want to see proof of concept, please pm me, or leave a message here.

Also if anyone has any experience working with either Windows voice to text, or the CMU Sphinx library, and is interested in contributing insights and/or help with the voice to text component of this mod, please contact me. The basic idea is a robust voice recognition engine like that built into windows, that can output text files, like "Equip Nightingale Bow Quick", "Save", "Open Map", "Use five healing potions", etc.

The papyrus script I have dumps the text file to a string, and then parses it as necessary.

Swat

Edited by irswat
Link to comment
Share on other sites

FRUITFUL NIGHT:

I equipped my first item via a parsed text file tonight. The command was "Equip Imperial Sword Both Hands".
I trained windows 10 speech recognition to recognize the words that are necessary like Thuum, and Equip.
Two major challenges to this working:

1.) I need to write a small c++ file that will launch windows speech recognition, and then SKSE, and will run alongside both to autosave the text in notepad when someone presses the skyrim voice engine command key (left control/MCM configurable). The text file will need to be formatted very slightly so each line begins with a command, and ends with a delimiter (by default: QUICK, but hopefully will be configurable by MCM), followed by a literal carriage return '/n'.

2.) I need someone to do this formlist generator/injector. I suppose I could do it myself, but I still have alot of coding ahead of me, plus this would require picking up a new scripting language. Just better someone who knows there stuff <*coughcough*matortheeternal*cough*> help me!

Link to comment
Share on other sites

I've done some limited xEdit scripting (well to be honest, the first draft was written for me) but I understand it enough now that I *might* be able to create a formlist of objects. However, I have no idea how to make it a separate utility such as DynDOLOD. What I could do, could only run from within xEdit proper.

 

I have formlists built in my xEdit script, so I know it can be done. The question is if I can adapt it to your needs. :P

Link to comment
Share on other sites

I have not tested this. All I did was remove other stuff from the working xEdit script that I do have. The following will populate existing formlists (one for bolts and one for arrows). There is no need to compile, simply save it to the "xEdit > Edit Scripts" folder. Make sure this works as-is and then we can look at including other items for other lists as needed.

 

 

 

{Describe script here}
unit MyPatch

const
	sModMaster = 'MyMod.esp' //master file name
	sModPatch  = 'MyModPatch.esp' //patch file name
	
var
	ModMaster, ModPatch: IInterface;

procedure AddObjectToList(list: string; comp: IInterface);
var
  g, flst, entries, entry: IInterface;
  i: integer;
begin
  // create new patch plugin if doesn't exist yet
  if not Assigned(ModPatch) then begin
    ModPatch := AddNewFileName(sModPatch);
    // 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);
      AddMasterIfMissing(ModPatch, GetFileName(comp));
      SetEditValue(entry, Name(comp));
    end;
  end;
end;

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;
  
  // determine arrow or bolt by 'bolt' word in the name
  if Pos('bolt', LowerCase(GetElementEditValues(e, 'FULL'))) <> 0 then
    list := 'MyBoltList' //replace with actual Editor ID name of the target formlist
  else
    list := 'MyArrowList'; //replace with actual Editor ID name of the target formlist
    
  AddObjectToList(list, e);
end;

function Initialize: integer;
var
  i, j: integer;
  f, g, r: IInterface;
begin
  // locating master file and optionally patch file if exists
  for i := 0 to Pred(FileCount) do begin
    f := FileByIndex(i);
    if SameText(GetFileName(f), sModMaster) then
      ModMaster := f
    else if SameText(GetFileName(f), sModPatch) then
      ModPatch := f;
  end;

  // can't do anything without master
  if not Assigned(ModMaster) then begin
    MessageDlg(sModMaster + ' must be loaded in TES5Edit', mtInformation, [mbOk], 0);
    Result := 1;
    Exit;
  end;
	
  // processing all files in load order
  for i := 0 to Pred(FileCount) do begin
    f := FileByIndex(i);
    AddMessage('Processing ' + GetFileName(f) + '...');

    // AMMO records
    g := GroupBySignature(f, 'AMMO');
    if Assigned(g) then
      for j := 0 to Pred(ElementCount(g)) do begin
        r := ElementByIndex(g, j);
        if IsMaster(r) then
          ProcessAmmo(WinningOverride(r));
          ProcessItems(WinningOverride(r));
        end;

  end;
end;   

 

There are some entries that need to be changed in order to work with your particular setup. You need to set your mod's plugin name as the master file and give a name to use as a patch plugin file. You also need to create the formlists for bolts and arrows and change their entries on the script too. If you do not want to skip the projectiles that I skipped, simply delete the relevant section.

Link to comment
Share on other sites

Ishara you are my hero! I need to adapt this to WeaponFormList, ArmorFormlist, ShoutFormlist, SpellsFormList, and PotionsFormList, but this is enough to work with. I'll ask mator how I can translate this into a plugin like he does using the xedit framework. Thank you so much! You will get all credit as usual.

Once I modify your code I will somehow incorporate its functionality into a C++ GUI. Click on the button (Create/generate formlists) it will do that. Then I will run a little helper script like the one below to dump a text file, which can then dynamically fill a string array with the words that make up the various form list names. Pretty neat eh?

 

Scriptname SVE_ParserEngine extends quest
;this code is to assist in filling the string arrays for the SVE Voice Command Engine Parser
;first formlists are created and filled with tes5edit script
;second these formlists are injected into a plugin for the SVE Command Engine
;third this helper is run which generates a dump of the names of all forms in the formlists
;these names are then parsed to delete duplicate words, and added to a string arrays
;these string arrays are what is used in the parser engine to compare commands to


Event OnInit()
	RegisterForSingleUpdate(1.0)
endEvent

Event OnUpdate()
	Main()
endEvent

function Main()
	int CurrentArrayLength
	int i=0

	i=0
	debug.trace "Dumping Weapon Names Form List"
	CurrentArrayLength=WeaponFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile

	i=0
	debug.trace "Dumping Armor Names Form List"
	CurrentArrayLength=ArmorFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile

	i=0
	debug.trace "Dumping Potions Form List"
	CurrentArrayLength=PotionsFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile

	i=0
	debug.trace "Dumping Spells Form List"
	CurrentArrayLength=SpellsFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile

	i=0
	debug.trace "Dumping Shouts Form List"
	CurrentArrayLength=ShoutsFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile

	i=0
	debug.trace "Dumping Arrows Form List"
	CurrentArrayLength=ArrowsFormList.Length
	while (i<CurrentArrayLength)
		debug.trace("= "" + WeaponFormList.GetAt(i).GetName + """)
	endWhile
	
endFunction

 

 

Edited by irswat
Link to comment
Share on other sites

Tonight I plan on running this on a limited load order which adds some bolts and arrows. I'll confirm that they are added to the form list, and will add unique item modifiers to the string array of words associated with arrows. I'll then try passing some equip commands through a text file to the parser and see if it works. If it does I'll report back my results.

I need to comb through your code to better understand it. It looks like Paschal sort of.

Edited by irswat
Link to comment
Share on other sites

Just noticed I left an extra line in.

Near the bottom locate and remove the following:

ProcessItems(WinningOverride(r));

 

When the script starts, the function labeled Initialize runs. During that function the ammo records are grouped together and the procedure called ProcessAmmo is called on each one. While the ProcessAmmo procedure is running it filters out ammo that I did not want on my list(s), anything that reaches the end calls the procedure AddObjectToList. The AddObjectToList procedure creates the patch file if necessary and adds the passed in items to the passed in formlist.

Link to comment
Share on other sites

thanks for explaining. I had a bit of an emergency today which has zapped me of all creative will and intent. I'll try to do this over the next couple of days. I may need help figuring out how to adapt this to weapons, armors, etc, but I think the key is the flag: like FLST, AMMO, etc. I think weapon is WEAP, armor is ARMR, what about spells and shouts? Potions?

Link to comment
Share on other sites

  • Recently Browsing   0 members

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