irswat Posted November 10, 2016 Share Posted November 10, 2016 (edited) OK. I'm in the process of attempting to write a voice command engine for Skyrim. The basic data flow is:A button is pressed in game and you speak into the microphone.Windows voice recognition translates voice to text.A tandem c++ app saves the text file and does some light formatting like adding literal null characters "/n".Skyrim quest script dumps the text file to a string, parses the string into individual words, and stores them in an array.The script then detects one of several commands. The one I am working on now is "Equip". It will probably be the hardest of the commands. Here is where I am hoping for some input.Currently I am designing the engine to expect input in the form of "Equip Materialtype Weapontype/Armortype Uniqueattributes." For example "Ebony blade of woe."Here is the code I am using to accomplish this: endCMD=SVE_DumpArray.Find("Quick", iCMDFound) CMDLength=endCMD-iCMDFound (while CMDParserCounter<=CMDLength) iToParse=SVE_DumpArray[iCMDFound+CMDParserCounter] iItemFound=MaterialList.Find(iToParse) if iItemFound>0 MaterialType=iItemFound elseif iItemFound<0 iItemFound=WeaponType.Find(iToParse) elseif iItemFound>0 WeaponType=iItemFound elseif iItemFound<0 iItemFound=ArmorType.Find(iToParse) elseif iItemFound>0 ArmorType=iItemFound elseif iItemFound<0 iItemFound=WeaponNames.Find(iToParse) elseif iItemFound>0 WeaponName=iItemFound elseif iItemFound<0 iItemFound=ArmorNames.Find(iToParse) elseif iItemFound>0 ArmorName=iItemFound else ItemNotFound==true endif endWhile if MaterialType Formulator[0]=MaterialType elseif WeaponType Formulator[1]=WeaponType elseif ArmorType Formulator[2]=ArmorType elseif WeaponName Formulator[3]=WeaponName elseif ArmorName Formulator[4]=ArmorName endif Formulator[5]=Formulator[0]+Formulator[1]+Formulator[2]+Formulator[3]+Formulator[4] In theory Formulator [5] should yield something like a weapon or armor name. Please keep in mind I forgot to add equip slot. I then will search a pre filled formarray.GetName() and try to determine the percentage of match there is between the highest matching formlist and if it is sufficiently high, say 90%, will assume this is the correct item being requested.for example A command might be "Equip Nightingale Bow quick". This will trigger the weapon type and weapon name to fill. The result will be Bow Nightingale which should yield a near perfect match to the Nightingale Bow. In theory anyhow.Does anyone have any better theoretical ideas about how to about determining what a person wants using simple, or even more advanced parsing techniques? Again keep in mind I've never done this before, but I am eager to learn! Edited November 10, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 10, 2016 Author Share Posted November 10, 2016 (edited) p.s. can you concatenate strings in skyrim as cited above?edit: you can concatenate strings using + operator. Edited November 10, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 10, 2016 Author Share Posted November 10, 2016 (edited) is there a string/array intersect function in SKSE?edit: have not discovered one as of yet. TODO Edited November 10, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 10, 2016 Author Share Posted November 10, 2016 Good news: I have a proof of concept done. ;fills string array with DUMP while (LoopCounter<=DumpLineCount) CurrentLine=GetLine(CurrentDump, LoopCounter, DumpLineCount) ;debug.trace("Current Line: " + CurrentLine) WordsOnCurrentLine=GetNumWords(CurrentLine) ;debug.trace("Number of words on current line: " + WordsOnCurrentLine) CurrentWordIndex=1 while (CurrentWordIndex<=WordsOnCurrentLine) ; ;Get word y on line x CurrentWord=GetWord(CurrentLine, WordsOnCurrentLine, CurrentWordIndex) ;debug.notification("Word " + CurrentDumpWordIndex + ": " + CurrentWord) debug.trace("Word " + CurrentDumpWordIndex + ": " + CurrentWord) ; ;store word y in array SVE_DumpArray[CurrentDumpWordIndex]=CurrentWord CurrentWordIndex+=1 CurrentDumpWordIndex+=1 endWhile debug.trace("Line " + LoopCounter + " complete") LoopCounter=LoopCounter+1 endWhile LoopCounter=0 debug.trace("SVE DUMP COMPLETE.") ;right now it is only parsing the last line. while (FindCommandCounter<=7) currentCMD=CommandList[FindCommandCounter] iCMDFound=SVE_DumpArray.Find(currentCMD) debug.trace("Searching SVE Dump for executable") if (iCMDFound>=0) CurrentCMD=SVE_DumpArray[iCMDFound] debug.trace("Command found: " + SVE_DumpArray[iCMDFound] + " in index " + iCMDFound) FindCommandCounter+=1 else FindCommandCounter+=1 endif if CurrentCMD=="Thuum" ;search next three indices for thuum words against Thuum formlist ;do all three words belong to the same thuum? ;how many words does the player know? ;shout elseif CurrentCMD=="Equip" debug.notification("Equip CMD detected") endCMD=SVE_DumpArray.Find("QUICK", iCMDFound) CMDLength=endCMD-iCMDFound while (CMDParserCounter<=CMDLength) break=false iToParse=SVE_DumpArray[iCMDFound + CMDParserCounter] iItemFound=MaterialList.Find(iToParse) if ((iItemFound>=0) && (break==false)) MaterialType=SVE_DumpArray[iCMDFound + CMDParserCounter] break=true debug.notification("Material type detected in index " + CMDParserCounter) elseif iItemFound<0 iItemFound=WeaponTypes.Find(iToParse) endif if ((iItemFound>=0) && (break==false)) WeaponType=SVE_DumpArray[iCMDFound + CMDParserCounter] debug.notification("Weapon type detected in index " + CMDParserCounter) break=true elseif iItemFound<0 iItemFound=ArmorTypes.Find(iToParse) endif if ((iItemFound>=0) && (break==false)) ArmorType=SVE_DumpArray[iCMDFound + CMDParserCounter] debug.notification("Armor type detected in index " + CMDParserCounter) break=true elseif iItemFound<0 iItemFound=WeaponNames.Find(iToParse) endif if ((iItemFound>=0) && (break==false)) WeaponName=SVE_DumpArray[iCMDFound + CMDParserCounter] debug.notification("Unique weapon modifier detected in index " + CMDParserCounter) break=true elseif iItemFound<0 iItemFound=ArmorNames.Find(iToParse) endif if ((iItemFound>=0) && (break==false)) ArmorName=SVE_DumpArray[iCMDFound + CMDParserCounter] debug.notification("Unique armor modifier detected in index " + CMDParserCounter) break=true else ItemNotFound==true endif CMDParserCounter+=1 endWhile if MaterialType Formulator[0]=MaterialType endif if WeaponType Formulator[1]=WeaponType endif if ArmorType Formulator[2]=ArmorType endif if WeaponName Formulator[3]=WeaponName endif if ArmorName Formulator[4]=ArmorName endif Formulator[5]=Formulator[0]+" "+Formulator[1]+" "+Formulator[2]+" "+Formulator[3]+" "+Formulator[4] debug.notification(Formulator[5]) ;weapon or armor? ;armor/clothing ;should look for keywords like armor, breastplate, helmet, boots, gauntlets, cloaks, etc. ;this will be called piece ;modifier type will be called piecetype, this will be mostly materials ;expecting form "equip ebony armor", "equip dragonbone helmet", etc ;weapon ;name (staff of magus) ;material ;type (e.g. dagger, sword, greatsword, staff, bow, gloves) ;equipping hand ;expecting form "ebony bow and arrows", "or ebony bow and ebony arrows" ;expecting form "equip dragonsting dagger in left hand" ;expecting form "equip staff of magus in right hand", etc. elseif CurrentCMD=="Unequip" elseif CurrentCMD=="Use" ;potion formlist elseif CurrentCMD=="Cast" ;spell formlist elseif CurrentCMD=="Map" int MapKey = Input.GetMappedKey("Quick Map",0x00) Input.TapKey(MapKey) elseif CurrentCMD=="Save" int SaveKey = Input.GetMappedKey("Quicksave",0x00) Input.TapKey(SaveKey) debug.trace("save") elseif CurrentCMD=="Exit" ;Debug.QuitGame() endif endWhile The code detects Equip Staff of Magnus, and then parses the line into weapon type and special weapon modifier ("of magnus"). Next step is to fill a formlist with weaponforms. Anyone want to help me write a tes5edit script that will fill formlists dynamically? Ideally I could have WeaponFormList, and a seperate WeaponNamesFormList that mirrors the former. Then I'll simply do WeaponCommand=Formulator[5]. I will then search WeaponsNameFormList for a match on WeaponCommand, when it is found I'll get the index, then I'll get the weapon form from that index from the weaponformlist, parse the equipping hand if applicable, and voila, I'm ready to start issuing commands.The implications of this are a dynamic and robust voice command engine that is compatible with 3rd party mods. Pretty cool stuff. Link to comment Share on other sites More sharing options...
Recommended Posts