irswat Posted November 14, 2016 Share Posted November 14, 2016 (edited) 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 November 14, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 15, 2016 Author Share Posted November 15, 2016 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 More sharing options...
IsharaMeradin Posted November 15, 2016 Share Posted November 15, 2016 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 More sharing options...
IsharaMeradin Posted November 15, 2016 Share Posted November 15, 2016 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 More sharing options...
irswat Posted November 15, 2016 Author Share Posted November 15, 2016 (edited) 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 November 15, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 16, 2016 Author Share Posted November 16, 2016 (edited) 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 November 16, 2016 by irswat Link to comment Share on other sites More sharing options...
IsharaMeradin Posted November 16, 2016 Share Posted November 16, 2016 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 More sharing options...
irswat Posted November 17, 2016 Author Share Posted November 17, 2016 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 More sharing options...
IsharaMeradin Posted November 17, 2016 Share Posted November 17, 2016 If you look in xEdit at the records you want to put on your list, you will see a Signature entry in the Record Header. This is what the xEdit script is looking at when grouping them to process. Link to comment Share on other sites More sharing options...
Recommended Posts