Merge branch 'develop' into release

This commit is contained in:
alterNERDtive 2021-08-13 22:00:22 +02:00
commit 8f8eb81fbd
13 changed files with 1169 additions and 1000 deletions

View file

@ -1,9 +1,8 @@
# All files
[*]
guidelines = 80
end_of_line = lf
insert_final_newline = true
charset = utf-8-bom
# C# or VB files
[*.{cs,vb}]
[*.cs]
guidelines = 80, 120

3
.gitignore vendored
View file

@ -1,5 +1,6 @@
.vs/**
obj/**
bin/**
bin/**
site/**
bindEDplugin.csproj.user
*.zip

View file

@ -1,122 +1,140 @@
# 4.1 (2021-05-22)
## Added
* optional `~bindsFile` parameter for the `loadbinds` plugin context: use that
to specify a binds file instead of auto-detecting it from the currently active
preset. (#18)
## Changed
* Updated the README to reflect that you need to load the game once, and that
you need to have changed at least a single bind. (#19)
* Updated the README to reflect that you have to use a single preset for all
sections if youre playing Odyssey.
## Fixed
* Now only reading the first line of `startPreset.start` to work correctly with
Odyssey. (#15)
* Now correctly prioritizing `.4.0.binds` > `.3.0.binds` > `.binds`. (#20)
-----
# 4.0 (2021-05-19)
**Note**: If you do not own Odyssey, everything will work just as before!
I, too, do not own Odyssey. So while I have tried testing various things with
mock Odyssey binds files, please keep an eye out for bugs and [file an
issue](https://github.com/alterNERDtive/bindED/issues/new/) if you
encounter any. And check back for a potential 4.0.1 soon. TYVM!
**IMPORTANT**: Please backup your binds files before installing this release,
just in case. You can find them in
`%localappdata%\Frontier Developments\Elite Dangerous\Options\Bindings`.
Sadly for the time being Odyssey and Horizons will basically be separate games.
That also means they have separate binds files.
BindED will by default always use the last edited file, be that the base preset,
Horizons or Odyssey.
To keep hassle to a minimum, the recommended way to change binds is to do it
from Odyssey. When a change to the Odyssey file is detected, the plugin will
by default overwrite Horizons binds with it. To prevent that and keep entirely
separate binds, you can set `bindED.disableHorizonsSync#` (yes, including the
pound sign) to `true` in your VoiceAttack profile.
## Added
* Odyssey binds file support (`*.4.0.binds`). (#14)
* `bindED.disableHorizonsSync#` configuration option: Set this (to `true`) in
your VoiceAttack profile to disable automatically syncing Odyssey binds
changes to Horizons binds.
## Removed
* empty plugin context: Invoking the plugin without context no longer gives a
deprecation warning and will instead fail.
* binds file as plugin context: Invoking the plugin with a binds file as context
no longer gives a deprecation warning and will instead fail.
-----
# 3.1 (2021-01-29)
## Changed
* Invoking the `loadbinds` context will now force reset everything and reload
from scratch. (#5)
## Added
* The current layouts key map file is now monitored for changes. Should make
adding support for new layouts slightly less annoying. (#4)
-----
# 3.0 (2020-11-12)
I did a complete refactoring of everything to prepare for some juicy new
features! Sadly that also meant breaking backwards compatibility. On the plus
side, the things that no longer work like they did in Garys initial release
should basically never be used anyway.
## Removed
* You can no longer specify binding files to use by linking them into the plugin
directory.
* You can no longer specify binding files by using them as the plugin context.
## Changed
* Invoking the plugin with no context or with a binds file as context is now
deprecated and will be removed in a future version. Use the `loadbinds`
context instead.
## Added
* `en-gb` key map. Thank you A.Cyprus for the work on that!
* Bindings are now automagically read when VoiceAttack loads and when
`bindED.layout#` is changed.
* After the initial reading of bindings the plugin will monitor the bindings
directory for changes to a) the `StartPreset.start` file (preset has changed)
and b) the binds file(s) corresponding to the current preset. Changes are
automatically applied. (#3)
* The `listbinds` context will set the text variable `~bindED.bindsList` to a
list of bindings present in the current bindings file. (#1)
* The `missingbinds` context will create a report of missing binds (anything
that doesnt have keyboard binds) and save it to `~bindED.missingBinds`. (#2)
* The included `bindED-reports` profile runs a missing binds report and a binds
list report when you load it and saves them to your Desktop.
-----
# 2.0 (2020-09-23)
## Added
* Support for non-US keyboard layouts. `de-neo2` is included (because thats
what Im using), others can be added ([see the wiki for
instructions](https://github.com/alterNERDtive/bindED/wiki/Keyboard-Layouts)).
# 4.2 (2021-08-13)
## Added
* Pretty printed documentation! You can find it at
https://alterNERDtive.github.io/bindED.
* Troubleshooting guide! You can find it at
https://alterNERDtive.github.io/bindED/troubleshooting.
* `diagnostics` plugin context: writes current plugin state to the log for
troubleshooting.
* `bindED-diagnostics` profile: runs the `diagnostics` plugin context on load.
Should be the first troubleshooting step.
* Error message if multiple control presets are in use (Odyssey only).
## Fixed
* Now correctly loads presets that contain regex special characters (#28).
# 4.1 (2021-05-22)
## Added
* optional `~bindsFile` parameter for the `loadbinds` plugin context: use that
to specify a binds file instead of auto-detecting it from the currently active
preset. (#18)
## Changed
* Updated the README to reflect that you need to load the game once, and that
you need to have changed at least a single bind. (#19)
* Updated the README to reflect that you have to use a single preset for all
sections if youre playing Odyssey.
## Fixed
* Now only reading the first line of `startPreset.start` to work correctly with
Odyssey. (#15)
* Now correctly prioritizing `.4.0.binds` > `.3.0.binds` > `.binds`. (#20)
-----
# 4.0 (2021-05-19)
**Note**: If you do not own Odyssey, everything will work just as before!
I, too, do not own Odyssey. So while I have tried testing various things with
mock Odyssey binds files, please keep an eye out for bugs and [file an
issue](https://github.com/alterNERDtive/bindED/issues/new/) if you
encounter any. And check back for a potential 4.0.1 soon. TYVM!
**IMPORTANT**: Please backup your binds files before installing this release,
just in case. You can find them in
`%localappdata%\Frontier Developments\Elite Dangerous\Options\Bindings`.
Sadly for the time being Odyssey and Horizons will basically be separate games.
That also means they have separate binds files.
BindED will by default always use the last edited file, be that the base preset,
Horizons or Odyssey.
To keep hassle to a minimum, the recommended way to change binds is to do it
from Odyssey. When a change to the Odyssey file is detected, the plugin will
by default overwrite Horizons binds with it. To prevent that and keep entirely
separate binds, you can set `bindED.disableHorizonsSync#` (yes, including the
pound sign) to `true` in your VoiceAttack profile.
## Added
* Odyssey binds file support (`*.4.0.binds`). (#14)
* `bindED.disableHorizonsSync#` configuration option: Set this (to `true`) in
your VoiceAttack profile to disable automatically syncing Odyssey binds
changes to Horizons binds.
## Removed
* empty plugin context: Invoking the plugin without context no longer gives a
deprecation warning and will instead fail.
* binds file as plugin context: Invoking the plugin with a binds file as context
no longer gives a deprecation warning and will instead fail.
-----
# 3.1 (2021-01-29)
## Changed
* Invoking the `loadbinds` context will now force reset everything and reload
from scratch. (#5)
## Added
* The current layouts key map file is now monitored for changes. Should make
adding support for new layouts slightly less annoying. (#4)
-----
# 3.0 (2020-11-12)
I did a complete refactoring of everything to prepare for some juicy new
features! Sadly that also meant breaking backwards compatibility. On the plus
side, the things that no longer work like they did in Garys initial release
should basically never be used anyway.
## Removed
* You can no longer specify binding files to use by linking them into the plugin
directory.
* You can no longer specify binding files by using them as the plugin context.
## Changed
* Invoking the plugin with no context or with a binds file as context is now
deprecated and will be removed in a future version. Use the `loadbinds`
context instead.
## Added
* `en-gb` key map. Thank you A.Cyprus for the work on that!
* Bindings are now automagically read when VoiceAttack loads and when
`bindED.layout#` is changed.
* After the initial reading of bindings the plugin will monitor the bindings
directory for changes to a) the `StartPreset.start` file (preset has changed)
and b) the binds file(s) corresponding to the current preset. Changes are
automatically applied. (#3)
* The `listbinds` context will set the text variable `~bindED.bindsList` to a
list of bindings present in the current bindings file. (#1)
* The `missingbinds` context will create a report of missing binds (anything
that doesnt have keyboard binds) and save it to `~bindED.missingBinds`. (#2)
* The included `bindED-reports` profile runs a missing binds report and a binds
list report when you load it and saves them to your Desktop.
-----
# 2.0 (2020-09-23)
## Added
* Support for non-US keyboard layouts. `de-neo2` is included (because thats
what Im using), others can be added ([see the wiki for
instructions](https://github.com/alterNERDtive/bindED/wiki/Keyboard-Layouts)).

7
Makefile Normal file
View file

@ -0,0 +1,7 @@
.PHONY: docs deploy-docs
docs:
mkdocs build -c
deploy-docs:
mkdocs gh-deploy -cs

138
README.md
View file

@ -1,125 +1,27 @@
# bindED
This VoiceAttack plugin reads keybindings in Elite:Dangerous and stores them as
VoiceAttack variables. It has originally been written by Gary (the developer of
VoiceAttack variables. It was originally written by Gary (the developer of
VoiceAttack) [and published on the VoiceAttack
forums](https://forum.voiceattack.com/smf/index.php?topic=564.0).
forums](https://forum.voiceattack.com/smf/index.php?topic=564.0). You can find
the [original README here](https://alterNERDtive.github.io/bindED/ReadMe.txt)
for reference.
You can find the [original README here](ReadMe.txt).
I have basically done a complete rewrite the original source code at this point
and added a lot of features including automatic detection of the correct
bindings file and support for non-US keyboard layouts (see below for details).
## Documentation & Installation Guide
You can find [comprehensive documentation on Github
Pages](https://alterNERDtive.github.io/bindED).
I have taken the original source code and added automatic detection of the
correct bindings file and support for non-US keyboard layouts (see below for
details).
## Need Help / Want to Contribute?
Have a loot at [the troubleshooting
guide](https://alterNERDtive.github.io/bindED/troubleshooting). If your problem
persists, please [file an
issue](https://github.com/alterNERDtive/bindED/issues/new). Thanks! :)
## Installing
Grab `bindEDplugin.zip` from the [release
page](https://github.com/alterNERDtive/bindED/releases/latest) and extract it
into your VoiceAttacks `Apps` directory.
## Migrating from the Old Plugin
If you use this as a drop-in replacement for the initial version all commands
invoking the plugin will throw an error message. Gary has asked me to change the
plugins GUID, and the plugin with the old one will no longer be found.
_That is irrelevant in basically all cases and can safely be ignored_. Binds
will be read automatically when VoiceAttack starts, and when they change.
## Usage
Before starting VoiceAttack with the plugin installed, make sure that you have
loaded the game at least once! That will create the directory structure the
plugin is going to read from. You also need to have changed _any_ key in
controls options. You will probably have made some changes anyway.
For Horizons players, thats it! When VoiceAttack loads, bindED will
automatically detect your bindings. It will also keep a watchful eye on Elites
bindings folder and reload them when there is a change!
For Odyssey players, there is an additional caveat: you have to use the same
preset for all 4 sections (general, ship, SRV, foot). Sadly its not apparent
from the files which binds belong to which.
If something goes awry, you can still manually call the `loadbinds` plugin
context to force a refresh.
If you are not using a US QWERTY keyboard layout, see below.
### Horizons vs. Odyssey
**Note**: If you do not own Odyssey, everything will work just as before!
Sadly for the time being Odyssey and Horizons will basically be separate games.
That also means they have separate binds files.
BindED will by default always use the last edited file, be that the base preset,
Horizons or Odyssey.
To keep hassle to a minimum, the recommended way to change binds is to do it
from Odyssey. When a change to the Odyssey file is detected, the plugin will by
default overwrite Horizons binds with it. To prevent that and keep entirely
separate binds, you can set `bindED.disableHorizonsSync#` (yes, including the
pound sign) to `true` in your VoiceAttack profile. Then, after changing anything
from Horizons, youll have to tell the plugin to load the Horizons file (see
below).
### Specifying a Binds File to Load
This should generally not be necessary!
You can set the text variable `~bindsFile` to a specific file name (e.g.
`custom.3.0.binds`) before executing the `loadbinds` context to have that
specific binds file loaded.
Make sure to only use the _file name_ of an existing binds file, do _not_
specify the full path.
This should be a last resort effort for when the game introduces changes that
break the plugins auto detection.
## Support for non-US Keyboard Layouts
Shipped layouts:
* en-US
* en-GB
* de-neo2
If you are using any non-US layout you might have noticed that some binds dont
work. Elite internally uses keycode values (a number assigned to each key on the
keyboard) for its bindings but for some reason both displays and saves them as
keysyms (the label on the key), according to the UK QWERTY keyboard layout. That
means VoiceAttack cant just send the keysym it reads from a binding, it has to
translate it into the corresponding keycode.
The original plugin contained a `EDMap.txt` file that contains information on
that conversion _for the US keyboard layout_. If you are using any other layout
that information will be incorrect for any symbols that are on a different key
than they are on the US layout.
I have added the option to use maps for other keyboard layouts. In order to do
so you will have to set a text variable in VoiceAttack called `bindED.layout#`
to the layout you want to use. BindED will be notified of the variable changing
and reload your bindings with the appropriate key map. If the variable is not
set it will defaut to “en-us”, leaving the original behaviour intact.
I have included a map file for [Neo2](https://neo-layout.org)
(`EDMap-de-neo2.txt`) which is the layout that I am using personally. If you are
on a different layout, you will have to create a corresponding map file yourself
or prod me to add it. E.g. for the french AZERTY it would be `EDMap-fr-fr.text`
and set `bindED.layout#` to “fr-fr”. For US Dvorak, `EDmap-en-us-dvorak` and
“en-us-dvorak”. You can see where this is going.
For more information on [creating new supported keyboard layouts see the
Wiki](https://github.com/alterNERDtive/bindED/wiki/Keyboard-Layouts).
## Troubleshooting
If you run into any kinds of trouble with missing bindings the first step should
be to import and load the included `bindED-reports` profile. It will generate
both a list of bind names used by Elite and a report of binds that do not have a
keyboard shortcut assigned, and put them on your Desktop.
Need help beyond that? Please [file an
issue](https://github.com/alterNERDtive/bindED/issues/new) or [hop into
Discord](https://discord.gg/YeXh2s5UC6).
You can also [say “Hi” on Discord](https://discord.gg/YeXh2s5UC6) if that is
your thing.

Binary file not shown.

851
bindED.cs
View file

@ -1,418 +1,433 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
namespace bindEDplugin
{
public class bindEDPlugin
{
private static dynamic? _VA;
private static string? _pluginPath;
private static readonly string _bindingsDir = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData),
@"Frontier Developments\Elite Dangerous\Options\Bindings"
);
private static readonly Dictionary<string, int> _fileEventCount = new Dictionary<string, int>();
private static FileSystemWatcher BindsWatcher
{
get
{
if (_bindsWatcher == null)
{
_bindsWatcher = new FileSystemWatcher(_bindingsDir);
_bindsWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
_bindsWatcher.Changed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_bindsWatcher.Created += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_bindsWatcher.Renamed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
}
return _bindsWatcher!;
}
}
private static FileSystemWatcher? _bindsWatcher;
private static FileSystemWatcher MapWatcher
{
get
{
if (_mapWatcher == null)
{
_mapWatcher = new FileSystemWatcher(_pluginPath);
_mapWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
_mapWatcher.Changed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_mapWatcher.Created += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_mapWatcher.Renamed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
}
return _mapWatcher!;
}
}
private static FileSystemWatcher? _mapWatcher;
private static string? Layout
{
get => _layout ??= _VA?.GetText("bindED.layout#") ?? "en-us";
set
{
_layout = value;
KeyMap = null;
}
}
private static string? _layout;
private static Dictionary<string, int>? KeyMap
{
get => _keyMap ??= LoadKeyMap(Layout!);
set => _keyMap = value;
}
private static Dictionary<string, int>? _keyMap;
private static string? Preset
{
get => _preset ??= DetectPreset();
set
{
_preset = value;
Binds = null;
}
}
private static string? _preset;
private static Dictionary<string, List<string>>? Binds
{
get => _binds ??= ReadBinds(DetectBindsFile(Preset));
set => _binds = value;
}
private static Dictionary<string, List<string>>? _binds;
public static string VERSION = "4.1";
public static string VA_DisplayName() => $"bindED Plugin v{VERSION}-alterNERDtive";
public static string VA_DisplayInfo() => "bindED Plugin\r\n\r\n2016 VoiceAttack.com\r\n20202021 alterNERDtive";
public static Guid VA_Id() => new Guid("{524B4B9A-3965-4045-A39A-A239BF6E2838}");
public static void VA_Init1(dynamic vaProxy)
{
_VA = vaProxy;
_VA.TextVariableChanged += new Action<string, string, string, Guid?>(TextVariableChanged);
_pluginPath = Path.GetDirectoryName(_VA.PluginPath());
_VA.SetText("bindED.version", VERSION);
_VA.SetText("bindED.fork", "alterNERDtive");
try
{
LoadBinds(Binds);
}
catch (Exception e)
{
LogError(e.Message);
}
finally
{
BindsWatcher.EnableRaisingEvents = true;
MapWatcher.EnableRaisingEvents = true;
}
}
public static void VA_Invoke1(dynamic vaProxy)
{
_VA = vaProxy;
try
{
string context = _VA.Context.ToLower();
if (context == "listbinds")
{
ListBinds(Binds, _VA.GetText("bindED.separator") ?? "\r\n");
}
else if (context == "loadbinds")
{
// force reset everything
Layout = null;
Preset = null;
if (!String.IsNullOrWhiteSpace(_VA.GetText("~bindsFile")))
{
Binds = ReadBinds(Path.Combine(_bindingsDir, _VA.GetText("~bindsFile")));
}
LoadBinds(Binds);
}
else if (context == "missingbinds")
{
MissingBinds(Binds);
}
else
{
LogError($"Invalid plugin context {context}.");
}
}
catch (Exception e)
{
LogError(e.Message);
}
}
public static void VA_StopCommand() { }
public static void VA_Exit1(dynamic vaProxy) { }
public static void TextVariableChanged(string name, string from, string to, Guid? internalID)
{
if (name == "bindED.layout#")
{
LogInfo($"Keyboard layout changed to '{to}', reloading …");
Layout = to;
try
{
LoadBinds(Binds);
}
catch (Exception e)
{
LogError(e.Message);
}
}
}
private static void LogError(string message)
{
_VA!.WriteToLog($"ERROR | bindED: {message}", "red");
}
private static void LogInfo(string message)
{
_VA!.WriteToLog($"INFO | bindED: {message}", "blue");
}
private static void LogWarn(string message)
{
_VA!.WriteToLog($"WARN | bindED: {message}", "yellow");
}
public static void ListBinds(Dictionary<string, List<string>>? binds, string separator)
{
_VA!.SetText("~bindED.bindsList", string.Join(separator, binds!.Keys));
LogInfo("List of Elite binds saved to TXT variable '~bindED.bindsList'.");
}
private static void LoadBinds(Dictionary<string, List<string>>? binds)
{
foreach (KeyValuePair<string, List<string>> bind in binds!)
{
string value = string.Empty;
bool valid = true;
if (bind.Value.Count == 0)
{
//LogInfo($"No keyboard bind for '{bind.Key}' found, skipping …");
}
else
{
foreach (string key in bind.Value)
{
if (KeyMap!.ContainsKey(key))
{
value += $"[{KeyMap[key]}]";
}
else
{
valid = false;
LogError($"No valid key code for '{key}' found, skipping bind for '{bind.Key}' …");
}
}
if (valid)
{
_VA!.SetText(bind.Key, value);
}
}
}
LogInfo($"Elite binds '{(String.IsNullOrWhiteSpace(_VA!.GetText("~bindsFile")) ? Preset : _VA!.GetText("~bindsFile"))}' for layout '{Layout}' loaded successfully.");
}
private static void MissingBinds(Dictionary<string, List<string>>? binds)
{
List<string> missing = new List<string>(256);
foreach (KeyValuePair<string, List<string>> bind in binds!)
{
if (bind.Value.Count == 0)
{
missing.Add(bind.Key);
}
}
if (missing.Count > 0)
{
_VA!.SetText("~bindED.missingBinds", string.Join("\r\n", missing));
_VA!.SetBoolean("~bindED.missingBinds", true);
LogInfo("List of missing Elite binds saved to TXT variable '~bindED.missingBinds'.");
}
else
{
LogInfo($"No missing keyboard binds found.");
}
}
private static Dictionary<String, int> LoadKeyMap(string layout)
{
string mapFile = Path.Combine(_pluginPath, $"EDMap-{layout.ToLower()}.txt");
if (!File.Exists(mapFile))
{
throw new FileNotFoundException($"No map file for layout '{layout}' found.");
}
Dictionary<string, int> map = new Dictionary<string, int>(256);
foreach (String line in File.ReadAllLines(mapFile, System.Text.Encoding.UTF8))
{
String[] arItem = line.Split(";".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
if ((arItem.Count() == 2) && (!String.IsNullOrWhiteSpace(arItem[0])) && (!map.ContainsKey(arItem[0])))
{
ushort iKey;
if (ushort.TryParse(arItem[1], out iKey))
{
if (iKey > 0 && iKey < 256)
map.Add(arItem[0].Trim(), iKey);
}
}
}
if (map.Count == 0)
{
throw new Exception($"Map file for {layout} does not contain any elements.");
}
return map;
}
private static string DetectPreset()
{
string startFile = Path.Combine(_bindingsDir, "StartPreset.start");
if (!File.Exists(startFile))
{
throw new FileNotFoundException("No 'StartPreset.start' file found. Please run Elite: Dangerous at least once, then restart VoiceAttack.");
}
return File.ReadAllLines(startFile).First();
}
private static string DetectBindsFile(string? preset)
{
DirectoryInfo dirInfo = new DirectoryInfo(_bindingsDir);
FileInfo[] bindFiles = dirInfo.GetFiles()
.Where(i => Regex.Match(i.Name, $@"^{preset}\.[34]\.0\.binds$").Success)
.OrderByDescending(p => p.Name).ToArray();
if (bindFiles.Count() == 0)
{
bindFiles = dirInfo.GetFiles($"{preset}.binds");
if (bindFiles.Count() == 0)
{
throw new FileNotFoundException($"No bindings file found for preset '{preset}'. If this is a default preset, please change anything in Elites controls options.");
}
}
return bindFiles[0].FullName;
}
private static Dictionary<string, List<string>> ReadBinds(string file)
{
XElement rootElement;
rootElement = XElement.Load(file);
Dictionary<string, List<string>> binds = new Dictionary<string, List<string>>(512);
if (rootElement != null)
{
foreach (XElement c in rootElement.Elements().Where(i => i.Elements().Count() > 0))
{
List<string> keys = new List<string>();
foreach (var element in c.Elements().Where(i => i.HasAttributes))
{
if (element.Name == "Primary")
{
if (element.Attribute("Device").Value == "Keyboard" && !String.IsNullOrWhiteSpace(element.Attribute("Key").Value) && element.Attribute("Key").Value.StartsWith("Key_"))
{
foreach (var modifier in element.Elements().Where(i => i.Name.LocalName == "Modifier"))
{
keys.Add(modifier.Attribute("Key").Value);
}
keys.Add(element.Attribute("Key").Value);
}
}
if (keys.Count == 0 && element.Name == "Secondary") //nothing found in primary... look in secondary
{
if (element.Attribute("Device").Value == "Keyboard" && !String.IsNullOrWhiteSpace(element.Attribute("Key").Value) && element.Attribute("Key").Value.StartsWith("Key_"))
{
foreach (var modifier in element.Elements().Where(i => i.Name.LocalName == "Modifier"))
{
keys.Add(modifier.Attribute("Key").Value);
}
keys.Add(element.Attribute("Key").Value);
}
}
}
binds.Add($"ed{c.Name.LocalName}", keys);
}
}
return binds;
}
private static void FileChangedHandler(string name)
{
// so apparently these events all fire twice … lets make sure we only handle it once.
if (_fileEventCount.ContainsKey(name))
{
_fileEventCount[name] += 1;
}
else
{
_fileEventCount.Add(name, 1);
}
if (_fileEventCount[name] % 2 == 0)
{
try
{
// lets make semi-sure that the file isnt locked …
// FIXXME: solve this properly
Thread.Sleep(500);
// Going by name only is a bit naïve given were watching 2
// separate directories, but hey … worst case if something
// is doing unintended things is unnecessarily reloading the
// binds.
if (name == $"EDMap-{Layout!.ToLower()}.txt")
{
LogInfo($"Key map for layout '{Layout}' has changed, reloading …");
KeyMap = null;
LoadBinds(Binds);
}
else if (name == "StartPreset.start")
{
LogInfo("Controls preset has changed, reloading …");
Preset = null;
LoadBinds(Binds);
}
else if (Regex.Match(name, $@"{Preset}(\.[34]\.0)?\.binds$").Success)
{
LogInfo($"Bindings file '{name}' has changed, reloading …");
Binds = null;
LoadBinds(Binds);
// copy Odyssey -> Horizons
if (name == $"{Preset}.4.0.binds" && !_VA!.GetBoolean("bindED.disableHorizonsSync#"))
{
File.WriteAllText(
Path.Combine(_bindingsDir, $"{Preset}.3.0.binds"),
File.ReadAllText(Path.Combine(_bindingsDir, name))
.Replace("MajorVersion=\"4\" MinorVersion=\"0\">", "MajorVersion=\"3\" MinorVersion=\"0\">")
);
}
}
}
catch (Exception e)
{
LogError(e.Message);
}
}
}
}
}
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
namespace bindEDplugin
{
public class bindEDPlugin
{
private static dynamic? _VA;
private static string? _pluginPath;
private static readonly string _bindingsDir = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData),
@"Frontier Developments\Elite Dangerous\Options\Bindings"
);
private static readonly Dictionary<string, int> _fileEventCount = new Dictionary<string, int>();
private static FileSystemWatcher BindsWatcher
{
get
{
if (_bindsWatcher == null)
{
_bindsWatcher = new FileSystemWatcher(_bindingsDir);
_bindsWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
_bindsWatcher.Changed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_bindsWatcher.Created += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_bindsWatcher.Renamed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
}
return _bindsWatcher!;
}
}
private static FileSystemWatcher? _bindsWatcher;
private static FileSystemWatcher MapWatcher
{
get
{
if (_mapWatcher == null)
{
_mapWatcher = new FileSystemWatcher(_pluginPath);
_mapWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
_mapWatcher.Changed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_mapWatcher.Created += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
_mapWatcher.Renamed += (source, EventArgs) => { FileChangedHandler(EventArgs.Name); };
}
return _mapWatcher!;
}
}
private static FileSystemWatcher? _mapWatcher;
private static string? Layout
{
get => _layout ??= _VA?.GetText("bindED.layout#") ?? "en-us";
set
{
_layout = value;
KeyMap = null;
}
}
private static string? _layout;
private static Dictionary<string, int>? KeyMap
{
get => _keyMap ??= LoadKeyMap(Layout!);
set => _keyMap = value;
}
private static Dictionary<string, int>? _keyMap;
private static string? Preset
{
get => _preset ??= DetectPreset();
set
{
_preset = value;
Binds = null;
}
}
private static string? _preset;
private static Dictionary<string, List<string>>? Binds
{
get => _binds ??= ReadBinds(DetectBindsFile(Preset!));
set => _binds = value;
}
private static Dictionary<string, List<string>>? _binds;
public static string VERSION = "4.2";
public static string VA_DisplayName() => $"bindED Plugin v{VERSION}-alterNERDtive";
public static string VA_DisplayInfo() => "bindED Plugin\r\n\r\n2016 VoiceAttack.com\r\n20202021 alterNERDtive";
public static Guid VA_Id() => new Guid("{524B4B9A-3965-4045-A39A-A239BF6E2838}");
public static void VA_Init1(dynamic vaProxy)
{
_VA = vaProxy;
_VA.TextVariableChanged += new Action<string, string, string, Guid?>(TextVariableChanged);
_pluginPath = Path.GetDirectoryName(_VA.PluginPath());
_VA.SetText("bindED.version", VERSION);
_VA.SetText("bindED.fork", "alterNERDtive");
try
{
LoadBinds(Binds);
}
catch (Exception e)
{
LogError(e.Message);
}
finally
{
BindsWatcher.EnableRaisingEvents = true;
MapWatcher.EnableRaisingEvents = true;
}
}
public static void VA_Invoke1(dynamic vaProxy)
{
_VA = vaProxy;
try
{
string context = _VA.Context.ToLower();
if (context == "diagnostics")
{
LogInfo($"current keybord layout: {Layout}");
LogInfo($"current preset: {Preset}");
LogInfo($"detected binds file: {(new FileInfo(DetectBindsFile(Preset!))).Name}");
LogInfo($"detected binds file (full path): {DetectBindsFile(Preset!)}");
}
else if (context == "listbinds")
{
ListBinds(Binds, _VA.GetText("bindED.separator") ?? "\r\n");
}
else if (context == "loadbinds")
{
// force reset everything
Layout = null;
Preset = null;
if (!String.IsNullOrWhiteSpace(_VA.GetText("~bindsFile")))
{
Binds = ReadBinds(Path.Combine(_bindingsDir, _VA.GetText("~bindsFile")));
}
LoadBinds(Binds);
}
else if (context == "missingbinds")
{
MissingBinds(Binds);
}
else
{
LogError($"Invalid plugin context {context}.");
}
}
catch (Exception e)
{
LogError(e.Message);
}
}
public static void VA_StopCommand() { }
public static void VA_Exit1(dynamic vaProxy) { }
public static void TextVariableChanged(string name, string from, string to, Guid? internalID)
{
if (name == "bindED.layout#")
{
LogInfo($"Keyboard layout changed to '{to}', reloading …");
Layout = to;
try
{
LoadBinds(Binds);
}
catch (Exception e)
{
LogError(e.Message);
}
}
}
private static void LogError(string message)
{
_VA!.WriteToLog($"ERROR | bindED: {message}", "red");
}
private static void LogInfo(string message)
{
_VA!.WriteToLog($"INFO | bindED: {message}", "blue");
}
private static void LogWarn(string message)
{
_VA!.WriteToLog($"WARN | bindED: {message}", "yellow");
}
public static void ListBinds(Dictionary<string, List<string>>? binds, string separator)
{
_VA!.SetText("~bindED.bindsList", string.Join(separator, binds!.Keys));
LogInfo("List of Elite binds saved to TXT variable '~bindED.bindsList'.");
}
private static void LoadBinds(Dictionary<string, List<string>>? binds)
{
foreach (KeyValuePair<string, List<string>> bind in binds!)
{
string value = string.Empty;
bool valid = true;
if (bind.Value.Count == 0)
{
//LogInfo($"No keyboard bind for '{bind.Key}' found, skipping …");
}
else
{
foreach (string key in bind.Value)
{
if (KeyMap!.ContainsKey(key))
{
value += $"[{KeyMap[key]}]";
}
else
{
valid = false;
LogError($"No valid key code for '{key}' found, skipping bind for '{bind.Key}' …");
}
}
if (valid)
{
_VA!.SetText(bind.Key, value);
}
}
}
LogInfo($"Elite binds '{(String.IsNullOrWhiteSpace(_VA!.GetText("~bindsFile")) ? Preset : _VA!.GetText("~bindsFile"))}' for layout '{Layout}' loaded successfully.");
}
private static void MissingBinds(Dictionary<string, List<string>>? binds)
{
List<string> missing = new List<string>(256);
foreach (KeyValuePair<string, List<string>> bind in binds!)
{
if (bind.Value.Count == 0)
{
missing.Add(bind.Key);
}
}
if (missing.Count > 0)
{
_VA!.SetText("~bindED.missingBinds", string.Join("\r\n", missing));
_VA!.SetBoolean("~bindED.missingBinds", true);
LogInfo("List of missing Elite binds saved to TXT variable '~bindED.missingBinds'.");
}
else
{
LogInfo($"No missing keyboard binds found.");
}
}
private static Dictionary<String, int> LoadKeyMap(string layout)
{
string mapFile = Path.Combine(_pluginPath, $"EDMap-{layout.ToLower()}.txt");
if (!File.Exists(mapFile))
{
throw new FileNotFoundException($"No map file for layout '{layout}' found.");
}
Dictionary<string, int> map = new Dictionary<string, int>(256);
foreach (String line in File.ReadAllLines(mapFile, System.Text.Encoding.UTF8))
{
String[] arItem = line.Split(";".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
if ((arItem.Count() == 2) && (!String.IsNullOrWhiteSpace(arItem[0])) && (!map.ContainsKey(arItem[0])))
{
ushort iKey;
if (ushort.TryParse(arItem[1], out iKey))
{
if (iKey > 0 && iKey < 256)
map.Add(arItem[0].Trim(), iKey);
}
}
}
if (map.Count == 0)
{
throw new Exception($"Map file for {layout} does not contain any elements.");
}
return map;
}
private static string DetectPreset()
{
string startFile = Path.Combine(_bindingsDir, "StartPreset.start");
if (!File.Exists(startFile))
{
throw new FileNotFoundException("No 'StartPreset.start' file found. Please run Elite: Dangerous at least once, then restart VoiceAttack.");
}
IEnumerable<string> presets = File.ReadAllLines(startFile).Distinct();
if (presets.Count() > 1)
{
LogError($"You have selected multiple control presets ('{ String.Join("', '", presets) }'). "
+ $"Only binds from '{ presets.First() }' will be used. Please refer to the documentation for more information.");
}
return presets.First();
}
private static string DetectBindsFile(string preset)
{
DirectoryInfo dirInfo = new DirectoryInfo(_bindingsDir);
FileInfo[] bindFiles = dirInfo.GetFiles()
.Where(i => Regex.Match(i.Name, $@"^{Regex.Escape(preset)}\.[34]\.0\.binds$").Success)
.OrderByDescending(p => p.Name).ToArray();
if (bindFiles.Count() == 0)
{
bindFiles = dirInfo.GetFiles($"{preset}.binds");
if (bindFiles.Count() == 0)
{
throw new FileNotFoundException($"No bindings file found for preset '{preset}'. If this is a default preset, please change anything in Elites controls options.");
}
}
return bindFiles[0].FullName;
}
private static Dictionary<string, List<string>> ReadBinds(string file)
{
XElement rootElement;
rootElement = XElement.Load(file);
Dictionary<string, List<string>> binds = new Dictionary<string, List<string>>(512);
if (rootElement != null)
{
foreach (XElement c in rootElement.Elements().Where(i => i.Elements().Count() > 0))
{
List<string> keys = new List<string>();
foreach (var element in c.Elements().Where(i => i.HasAttributes))
{
if (element.Name == "Primary")
{
if (element.Attribute("Device").Value == "Keyboard" && !String.IsNullOrWhiteSpace(element.Attribute("Key").Value) && element.Attribute("Key").Value.StartsWith("Key_"))
{
foreach (var modifier in element.Elements().Where(i => i.Name.LocalName == "Modifier"))
{
keys.Add(modifier.Attribute("Key").Value);
}
keys.Add(element.Attribute("Key").Value);
}
}
if (keys.Count == 0 && element.Name == "Secondary") //nothing found in primary... look in secondary
{
if (element.Attribute("Device").Value == "Keyboard" && !String.IsNullOrWhiteSpace(element.Attribute("Key").Value) && element.Attribute("Key").Value.StartsWith("Key_"))
{
foreach (var modifier in element.Elements().Where(i => i.Name.LocalName == "Modifier"))
{
keys.Add(modifier.Attribute("Key").Value);
}
keys.Add(element.Attribute("Key").Value);
}
}
}
binds.Add($"ed{c.Name.LocalName}", keys);
}
}
return binds;
}
private static void FileChangedHandler(string name)
{
// so apparently these events all fire twice … lets make sure we only handle it once.
if (_fileEventCount.ContainsKey(name))
{
_fileEventCount[name] += 1;
}
else
{
_fileEventCount.Add(name, 1);
}
if (_fileEventCount[name] % 2 == 0)
{
try
{
// lets make semi-sure that the file isnt locked …
// FIXXME: solve this properly
Thread.Sleep(500);
// Going by name only is a bit naïve given were watching 2
// separate directories, but hey … worst case if something
// is doing unintended things is unnecessarily reloading the
// binds.
if (name == $"EDMap-{Layout!.ToLower()}.txt")
{
LogInfo($"Key map for layout '{Layout}' has changed, reloading …");
KeyMap = null;
LoadBinds(Binds);
}
else if (name == "StartPreset.start")
{
LogInfo("Controls preset has changed, reloading …");
Preset = null;
LoadBinds(Binds);
}
else if (Regex.Match(name, $@"{Preset}(\.[34]\.0)?\.binds$").Success)
{
LogInfo($"Bindings file '{name}' has changed, reloading …");
Binds = null;
LoadBinds(Binds);
// copy Odyssey -> Horizons
if (name == $"{Preset}.4.0.binds" && !_VA!.GetBoolean("bindED.disableHorizonsSync#"))
{
File.WriteAllText(
Path.Combine(_bindingsDir, $"{Preset}.3.0.binds"),
File.ReadAllText(Path.Combine(_bindingsDir, name))
.Replace("MajorVersion=\"4\" MinorVersion=\"0\">", "MajorVersion=\"3\" MinorVersion=\"0\">")
);
}
}
}
catch (Exception e)
{
LogError(e.Message);
}
}
}
}
}

View file

@ -54,6 +54,8 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<None Include="docs\index.md" />
<Content Include=".gitignore" />
<Content Include="EDMap-de-neo2.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -63,19 +65,22 @@
<Content Include="EDMap-en-us.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="ReadMe.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="docs\ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
<None Include="bindED-diagnostics-Profile.vap">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="bindED-reports-Profile.vap">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="CHANGELOG.md" />
<None Include="docs\troubleshooting.md" />
<None Include="LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="mkdocs.yml" />
<None Include="README.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View file

@ -1,335 +1,335 @@
----------------------------------------------------------------
About the bindED plugin - v1.0.0.1 (See end for version history)
----------------------------------------------------------------
The bindED plugin was created to copy the keyboard key bind information for Elite: Dangerous directly into VoiceAttack keypress variables. The variables
can then be used in conjunction with key press actions within VoiceAttack (the latest betas of VoiceAttack allow for keypresses based on variables).
The idea is to be able to change your key bindings from within Elite: Dangerous and have each change be reflected (semi-automatically) in VoiceAttack,
possibly saving some time and/or hair loss.
Thanks to Lavaeolus in the VoiceAttack user forum for starting up the EDMap.txt layout, as well as all those that participate in making plugins all they
can be ;)
--------------------------------------------------------------------------------------------------------------------------------
Setting up the bindED plugin
--------------------------------------------------------------------------------------------------------------------------------
Copy the entire bindED folder from the zip file to the VoiceAttack Apps folder (the whole folder, not just the contents). The folder is usually located
in C:\Program Files (x86)\VoiceAttack (An installer may be created later to make this easier... for now it's just a zip ;))
For most, this is all you'll need to do. For others having trouble or want to explore, read on:
Inside the bindED folder, you will find the plugin file itself (bindED.dll), which uses VoiceAttack's Plugin v4 interface... that just means that you
will need VoiceAttack beta version v1.5.12.32 or later to use the plugin.
You will also find a file called, 'EDMap.txt'. If you open this text file, you will see that it contains each of the Elite: Dangerous key bind types associated
with a numeric code. The code is simply the keyboard key code expressed as a number. For instance, the, 'A' key is 65, 'Z' is 90, 'Escape' is 27 and so on.
This is for English-US keyboards. Your keyboard layout may not line up exactly with this mapping, so adjustments may need to be made (you can edit this file
as you see fit, just as long as it follows the KeyName;Code structure that you see inside the file).
If you happen to create a layout that is different or find a mistake/omission in the English-US layout (this is new stuff, after all), please feel free to
upload your update to the VoiceAttack user forum for others to use: http://www.voiceattack.com/forum
To see the list of key codes, go here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
Note that the key codes are expressed in hexadecimal and will need to be converted to integer values.
--------------------------------------------------------------------------------------------------------------------------------
Using the bindED plugin
--------------------------------------------------------------------------------------------------------------------------------
To use the plugin, you will need to do a few things.
1) First, you will need to turn on plugin support in VoiceAttack. This is done from the Options screen.
2) Next, you will need to create a command in VoiceAttack to call the bindED plugin which (optionally) includes the Elite: Dangerous binds file that you want to use.
To do this, add a new command and then click on Other -> Advanced -> Execute an external plugin function.
3) In the new plugin action screen, select the, 'bindED' plugin from the Plugin list (bindED will be available if you had turned on plugin support and had installed
the bindED plugin correctly).
4) There are two routes you can take here. If you only have ONE binds file, and you want the bindED plugin to try to access your .binds file that is located
in C:\Users\YOUR_USER_NAME\AppData\Local\Frontier Developments\Elite Dangerous\Options\Bindings, simply leave the, 'Plugin Context' box blank.
The plugin will simply grab the NEWEST .binds file it finds in that directory and use it.
If you have MULTIPLE binds files for different situations, OR, the default behavior indicated above does not meet your need, you can specify which binds
file the bindED plugin is to access by typing or pasting the full path to the binds file that you want to use in the 'Plugin Context' box:
Selecting the, 'Wait for the plugin to fininsh...' option is up to you. Generally this should run very quicky, but if you have a situation where things need
to occur in a very specific order, select that option.
The full path is cut off in the image, but you get the idea ;)
5) Click OK, Done, etc.
The next time that this new command is executed (whether by itself to, 'reload' bindings or from a profile change) the VoiceAttack variables will be updated
with what is indicated in the bind file.
--------------------------------------------------------------------------------------------------------------------------------
How to use the keypress variables once they are updated
--------------------------------------------------------------------------------------------------------------------------------
This is where it gets a little tricky. There are a lot of words here, but as you'll see, there's not a whole lot going on.
First, you will need to be familiar with the Elite: Dangerous key binds in-game, as well as where those binds are stored in the configuration (.binds) files.
For this example, the landing gear toggle will be used. To toggle the landing gear, let's say the 'L' key is used in-game.
The configuration file (.binds) will store that information in an xml element called, 'LandingGearToggle'. Inside the binds file, you will see the entry
looks something like this:
<LandingGearToggle>
<Primary Device="Keyboard" Key="Key_L" />
<Secondary Device="{NoDevice}" Key="" />
</LandingGearToggle>
When VoiceAttack encounters this element, it creates and a TEXT variable called, 'edLandingGearToggle'. It's just simply the element name with, 'ed'
prefixed to it. Then, VoiceAttack notices that the key used is 'Key_L'. VoiceAttack gets the value indicated in the EDMap.txt file and puts it in the,
'edLandingGearToggle' variable (you don't necessarily have to know this... I threw it in there in case you were wondering ;))
So, in VoiceAttack (later betas), you can have a key (or keys) pressed based on a variable. At the bottom of the keypress screen, you will see an input box
that will allow you to specify what variable to use. Before, you would have, 'L' selected at the top of the screen. Now, you would just type in,
'edLandingGearToggle' (no quotes) in the variable input box.
NOTE: A current list (well... my current list) of variables that can be affected are listed at the end of this file.
Your command probably looks like this now:
When I say... 'Landing Gear'
Press L key and hold for 0.1 seconds and release
It would now look like this:
When I say... 'Landing Gear'
Press variable key(s) [edLandingGear] and hold for 0.1 seconds and release
If anybody out there wants to create an Elite: Dangerous variable mapping to what is seen in-game, that would be really cool ;)
--------------------------------------------------------------------------------------------------------------------------------
Advanced stuff and things to consider for those who want even more confusion.
--------------------------------------------------------------------------------------------------------------------------------
Note that you can have multiple plugin actions to load several (possibly custom) bind files at the same time if you need to. Each subsequently loaded bind file
will override the previous ones. You can also do this with one action by separating the bind paths with a semicolon. You may never need to do this, but it's
there if you want to do something with it.
Way off the chart... Something else to try that is left over from testing (and left in for anybody that wants to use it) is if you want to load a
binds file on plugin initialization (that is, when the plugin first loads (when VA starts up)... not when the plugin is invoked), create shortcuts to
the binds files and place them in the same folder as the plugin .dll. Each shortcut is processed in alphabetical order. This is one way to have an
automatic, global initialization of variables that do not require the plugin to be invoked. If this doesn't make any sense to you, do not worry ;)
If you are going to be creating profiles for others, it might be a good idea to put some extra steps in your commands in case your users do not have
the bindED plugin (either not installed, not configured properly or not initialized). Going to expand on the whole landing gear thing again in this example...
Begin Text Compare : [edLandingGearToggle] Has Been Set
Press variable key(s) [edLandingGearToggle] and hold for 0.1 seconds and release
Else
Press L key and hold for 0.1 seconds and release
Write '[Orange] Variable keypress not set. Using default keypress.' to log
End Condition
Note that if the variable HAS BEEN SET (not null), the keypress uses the variable. If the variable has not been set, you can have a fall-back keypress, or,
just show a warning.
One more thing... if you happen to have a '.binds' file for Star Citizen that has a good variety of key/key combinations (with modifiers... ctrl/alt/shift/win),
please let me know and I will create a bindSC ;)
--------------------------------------------------------------------------------------------------------------------------------
Variable List Reference
--------------------------------------------------------------------------------------------------------------------------------
Below is a current list of the variables that bindED can update (and you can use in your keypress actions). Remember, the element within the .binds file is
whatever the variable name is below, minus the, 'ed' prefix. So, 'edAutoBreakBuggyButton' below correlates to the 'AutoBreakBuggyButton' element in the .binds
file. Note that if no keys are set for the particular element, the variable value will be null (Not Set). This list can change at any time depending
on Frontier, and is only here to give you an idea of what's currently available without having to dig into your .binds file. Good luck, Captain. ;)
edAutoBreakBuggyButton
edBackwardKey
edBackwardThrustButton
edBackwardThrustButton_Landing
edBuggyPitchDownButton
edBuggyPitchUpButton
edBuggyPrimaryFireButton
edBuggyRollLeftButton
edBuggyRollRightButton
edBuggySecondaryFireButton
edBuggyToggleReverseThrottleInput
edBuggyTurretPitchDownButton
edBuggyTurretPitchUpButton
edBuggyTurretYawLeftButton
edBuggyTurretYawRightButton
edCamPitchDown
edCamPitchUp
edCamTranslateBackward
edCamTranslateDown
edCamTranslateForward
edCamTranslateLeft
edCamTranslateRight
edCamTranslateUp
edCamTranslateZHold
edCamYawLeft
edCamYawRight
edCamZoomIn
edCamZoomOut
edChargeECM
edCycleFireGroupNext
edCycleFireGroupPrevious
edCycleNextHostileTarget
edCycleNextPanel
edCycleNextSubsystem
edCycleNextTarget
edCyclePreviousHostileTarget
edCyclePreviousPanel
edCyclePreviousSubsystem
edCyclePreviousTarget
edDecreaseSpeedButtonMax
edDeployHardpointToggle
edDeployHeatSink
edDisableRotationCorrectToggle
edDownThrustButton
edDownThrustButton_Landing
edEjectAllCargo
edEjectAllCargo_Buggy
edEngineColourToggle
edFireChaffLauncher
edFocusCommsPanel
edFocusCommsPanel_Buggy
edFocusLeftPanel
edFocusLeftPanel_Buggy
edFocusRadarPanel
edFocusRadarPanel_Buggy
edFocusRightPanel
edFocusRightPanel_Buggy
edForwardKey
edForwardThrustButton
edForwardThrustButton_Landing
edGalaxyMapOpen
edGalaxyMapOpen_Buggy
edHeadlightsBuggyButton
edHeadLookPitchDown
edHeadLookPitchUp
edHeadLookReset
edHeadLookToggle
edHeadLookToggle_Buggy
edHeadLookYawLeft
edHeadLookYawRight
edHMDReset
edHyperspace
edHyperSuperCombination
edIncreaseEnginesPower
edIncreaseEnginesPower_Buggy
edIncreaseSpeedButtonMax
edIncreaseSystemsPower
edIncreaseSystemsPower_Buggy
edIncreaseWeaponsPower
edIncreaseWeaponsPower_Buggy
edLandingGearToggle
edLeftThrustButton
edLeftThrustButton_Landing
edMicrophoneMute
edMouseReset
edOpenOrders
edOrbitLinesToggle
edOrderAggressiveBehaviour
edOrderDefensiveBehaviour
edOrderFocusTarget
edOrderFollow
edOrderHoldFire
edOrderHoldPosition
edOrderRequestDock
edPause
edPhotoCameraToggle
edPhotoCameraToggle_Buggy
edPitchDownButton
edPitchDownButton_Landing
edPitchUpButton
edPitchUpButton_Landing
edPrimaryFire
edQuickCommsPanel
edQuickCommsPanel_Buggy
edRadarDecreaseRange
edRadarIncreaseRange
edRecallDismissShip
edResetPowerDistribution
edResetPowerDistribution_Buggy
edRightThrustButton
edRightThrustButton_Landing
edRollLeftButton
edRollLeftButton_Landing
edRollRightButton
edRollRightButton_Landing
edSecondaryFire
edSelectHighestThreat
edSelectTarget
edSelectTarget_Buggy
edSelectTargetsTarget
edSetSpeed100
edSetSpeed25
edSetSpeed50
edSetSpeed75
edSetSpeedMinus100
edSetSpeedMinus25
edSetSpeedMinus50
edSetSpeedMinus75
edSetSpeedZero
edShipSpotLightToggle
edShowPGScoreSummaryInput
edSteerLeftButton
edSteerRightButton
edSupercruise
edSystemMapOpen
edSystemMapOpen_Buggy
edTargetNextRouteSystem
edTargetWingman0
edTargetWingman1
edTargetWingman2
edToggleBuggyTurretButton
edToggleButtonUpInput
edToggleCargoScoop
edToggleCargoScoop_Buggy
edToggleDriveAssist
edToggleFlightAssist
edToggleReverseThrottleInput
edUI_Back
edUI_Down
edUI_Left
edUI_Right
edUI_Select
edUI_Toggle
edUI_Up
edUIFocus
edUIFocus_Buggy
edUpThrustButton
edUpThrustButton_Landing
edUseAlternateFlightValuesToggle
edUseBoostJuice
edUseShieldCell
edVerticalThrustersButton
edWeaponColourToggle
edWingNavLock
edYawLeftButton
edYawLeftButton_Landing
edYawRightButton
edYawRightButton_Landing
edYawToRollButton
--------------------------------------------------------------------------------------------------------------------------------
Version History
--------------------------------------------------------------------------------------------------------------------------------
v1.0 - Initial release
v1.0.0.1 - Fixed file sorting issue when accessing .binds files.
----------------------------------------------------------------
About the bindED plugin - v1.0.0.1 (See end for version history)
----------------------------------------------------------------
The bindED plugin was created to copy the keyboard key bind information for Elite: Dangerous directly into VoiceAttack keypress variables. The variables
can then be used in conjunction with key press actions within VoiceAttack (the latest betas of VoiceAttack allow for keypresses based on variables).
The idea is to be able to change your key bindings from within Elite: Dangerous and have each change be reflected (semi-automatically) in VoiceAttack,
possibly saving some time and/or hair loss.
Thanks to Lavaeolus in the VoiceAttack user forum for starting up the EDMap.txt layout, as well as all those that participate in making plugins all they
can be ;)
--------------------------------------------------------------------------------------------------------------------------------
Setting up the bindED plugin
--------------------------------------------------------------------------------------------------------------------------------
Copy the entire bindED folder from the zip file to the VoiceAttack Apps folder (the whole folder, not just the contents). The folder is usually located
in C:\Program Files (x86)\VoiceAttack (An installer may be created later to make this easier... for now it's just a zip ;))
For most, this is all you'll need to do. For others having trouble or want to explore, read on:
Inside the bindED folder, you will find the plugin file itself (bindED.dll), which uses VoiceAttack's Plugin v4 interface... that just means that you
will need VoiceAttack beta version v1.5.12.32 or later to use the plugin.
You will also find a file called, 'EDMap.txt'. If you open this text file, you will see that it contains each of the Elite: Dangerous key bind types associated
with a numeric code. The code is simply the keyboard key code expressed as a number. For instance, the, 'A' key is 65, 'Z' is 90, 'Escape' is 27 and so on.
This is for English-US keyboards. Your keyboard layout may not line up exactly with this mapping, so adjustments may need to be made (you can edit this file
as you see fit, just as long as it follows the KeyName;Code structure that you see inside the file).
If you happen to create a layout that is different or find a mistake/omission in the English-US layout (this is new stuff, after all), please feel free to
upload your update to the VoiceAttack user forum for others to use: http://www.voiceattack.com/forum
To see the list of key codes, go here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
Note that the key codes are expressed in hexadecimal and will need to be converted to integer values.
--------------------------------------------------------------------------------------------------------------------------------
Using the bindED plugin
--------------------------------------------------------------------------------------------------------------------------------
To use the plugin, you will need to do a few things.
1) First, you will need to turn on plugin support in VoiceAttack. This is done from the Options screen.
2) Next, you will need to create a command in VoiceAttack to call the bindED plugin which (optionally) includes the Elite: Dangerous binds file that you want to use.
To do this, add a new command and then click on Other -> Advanced -> Execute an external plugin function.
3) In the new plugin action screen, select the, 'bindED' plugin from the Plugin list (bindED will be available if you had turned on plugin support and had installed
the bindED plugin correctly).
4) There are two routes you can take here. If you only have ONE binds file, and you want the bindED plugin to try to access your .binds file that is located
in C:\Users\YOUR_USER_NAME\AppData\Local\Frontier Developments\Elite Dangerous\Options\Bindings, simply leave the, 'Plugin Context' box blank.
The plugin will simply grab the NEWEST .binds file it finds in that directory and use it.
If you have MULTIPLE binds files for different situations, OR, the default behavior indicated above does not meet your need, you can specify which binds
file the bindED plugin is to access by typing or pasting the full path to the binds file that you want to use in the 'Plugin Context' box:
Selecting the, 'Wait for the plugin to fininsh...' option is up to you. Generally this should run very quicky, but if you have a situation where things need
to occur in a very specific order, select that option.
The full path is cut off in the image, but you get the idea ;)
5) Click OK, Done, etc.
The next time that this new command is executed (whether by itself to, 'reload' bindings or from a profile change) the VoiceAttack variables will be updated
with what is indicated in the bind file.
--------------------------------------------------------------------------------------------------------------------------------
How to use the keypress variables once they are updated
--------------------------------------------------------------------------------------------------------------------------------
This is where it gets a little tricky. There are a lot of words here, but as you'll see, there's not a whole lot going on.
First, you will need to be familiar with the Elite: Dangerous key binds in-game, as well as where those binds are stored in the configuration (.binds) files.
For this example, the landing gear toggle will be used. To toggle the landing gear, let's say the 'L' key is used in-game.
The configuration file (.binds) will store that information in an xml element called, 'LandingGearToggle'. Inside the binds file, you will see the entry
looks something like this:
<LandingGearToggle>
<Primary Device="Keyboard" Key="Key_L" />
<Secondary Device="{NoDevice}" Key="" />
</LandingGearToggle>
When VoiceAttack encounters this element, it creates and a TEXT variable called, 'edLandingGearToggle'. It's just simply the element name with, 'ed'
prefixed to it. Then, VoiceAttack notices that the key used is 'Key_L'. VoiceAttack gets the value indicated in the EDMap.txt file and puts it in the,
'edLandingGearToggle' variable (you don't necessarily have to know this... I threw it in there in case you were wondering ;))
So, in VoiceAttack (later betas), you can have a key (or keys) pressed based on a variable. At the bottom of the keypress screen, you will see an input box
that will allow you to specify what variable to use. Before, you would have, 'L' selected at the top of the screen. Now, you would just type in,
'edLandingGearToggle' (no quotes) in the variable input box.
NOTE: A current list (well... my current list) of variables that can be affected are listed at the end of this file.
Your command probably looks like this now:
When I say... 'Landing Gear'
Press L key and hold for 0.1 seconds and release
It would now look like this:
When I say... 'Landing Gear'
Press variable key(s) [edLandingGear] and hold for 0.1 seconds and release
If anybody out there wants to create an Elite: Dangerous variable mapping to what is seen in-game, that would be really cool ;)
--------------------------------------------------------------------------------------------------------------------------------
Advanced stuff and things to consider for those who want even more confusion.
--------------------------------------------------------------------------------------------------------------------------------
Note that you can have multiple plugin actions to load several (possibly custom) bind files at the same time if you need to. Each subsequently loaded bind file
will override the previous ones. You can also do this with one action by separating the bind paths with a semicolon. You may never need to do this, but it's
there if you want to do something with it.
Way off the chart... Something else to try that is left over from testing (and left in for anybody that wants to use it) is if you want to load a
binds file on plugin initialization (that is, when the plugin first loads (when VA starts up)... not when the plugin is invoked), create shortcuts to
the binds files and place them in the same folder as the plugin .dll. Each shortcut is processed in alphabetical order. This is one way to have an
automatic, global initialization of variables that do not require the plugin to be invoked. If this doesn't make any sense to you, do not worry ;)
If you are going to be creating profiles for others, it might be a good idea to put some extra steps in your commands in case your users do not have
the bindED plugin (either not installed, not configured properly or not initialized). Going to expand on the whole landing gear thing again in this example...
Begin Text Compare : [edLandingGearToggle] Has Been Set
Press variable key(s) [edLandingGearToggle] and hold for 0.1 seconds and release
Else
Press L key and hold for 0.1 seconds and release
Write '[Orange] Variable keypress not set. Using default keypress.' to log
End Condition
Note that if the variable HAS BEEN SET (not null), the keypress uses the variable. If the variable has not been set, you can have a fall-back keypress, or,
just show a warning.
One more thing... if you happen to have a '.binds' file for Star Citizen that has a good variety of key/key combinations (with modifiers... ctrl/alt/shift/win),
please let me know and I will create a bindSC ;)
--------------------------------------------------------------------------------------------------------------------------------
Variable List Reference
--------------------------------------------------------------------------------------------------------------------------------
Below is a current list of the variables that bindED can update (and you can use in your keypress actions). Remember, the element within the .binds file is
whatever the variable name is below, minus the, 'ed' prefix. So, 'edAutoBreakBuggyButton' below correlates to the 'AutoBreakBuggyButton' element in the .binds
file. Note that if no keys are set for the particular element, the variable value will be null (Not Set). This list can change at any time depending
on Frontier, and is only here to give you an idea of what's currently available without having to dig into your .binds file. Good luck, Captain. ;)
edAutoBreakBuggyButton
edBackwardKey
edBackwardThrustButton
edBackwardThrustButton_Landing
edBuggyPitchDownButton
edBuggyPitchUpButton
edBuggyPrimaryFireButton
edBuggyRollLeftButton
edBuggyRollRightButton
edBuggySecondaryFireButton
edBuggyToggleReverseThrottleInput
edBuggyTurretPitchDownButton
edBuggyTurretPitchUpButton
edBuggyTurretYawLeftButton
edBuggyTurretYawRightButton
edCamPitchDown
edCamPitchUp
edCamTranslateBackward
edCamTranslateDown
edCamTranslateForward
edCamTranslateLeft
edCamTranslateRight
edCamTranslateUp
edCamTranslateZHold
edCamYawLeft
edCamYawRight
edCamZoomIn
edCamZoomOut
edChargeECM
edCycleFireGroupNext
edCycleFireGroupPrevious
edCycleNextHostileTarget
edCycleNextPanel
edCycleNextSubsystem
edCycleNextTarget
edCyclePreviousHostileTarget
edCyclePreviousPanel
edCyclePreviousSubsystem
edCyclePreviousTarget
edDecreaseSpeedButtonMax
edDeployHardpointToggle
edDeployHeatSink
edDisableRotationCorrectToggle
edDownThrustButton
edDownThrustButton_Landing
edEjectAllCargo
edEjectAllCargo_Buggy
edEngineColourToggle
edFireChaffLauncher
edFocusCommsPanel
edFocusCommsPanel_Buggy
edFocusLeftPanel
edFocusLeftPanel_Buggy
edFocusRadarPanel
edFocusRadarPanel_Buggy
edFocusRightPanel
edFocusRightPanel_Buggy
edForwardKey
edForwardThrustButton
edForwardThrustButton_Landing
edGalaxyMapOpen
edGalaxyMapOpen_Buggy
edHeadlightsBuggyButton
edHeadLookPitchDown
edHeadLookPitchUp
edHeadLookReset
edHeadLookToggle
edHeadLookToggle_Buggy
edHeadLookYawLeft
edHeadLookYawRight
edHMDReset
edHyperspace
edHyperSuperCombination
edIncreaseEnginesPower
edIncreaseEnginesPower_Buggy
edIncreaseSpeedButtonMax
edIncreaseSystemsPower
edIncreaseSystemsPower_Buggy
edIncreaseWeaponsPower
edIncreaseWeaponsPower_Buggy
edLandingGearToggle
edLeftThrustButton
edLeftThrustButton_Landing
edMicrophoneMute
edMouseReset
edOpenOrders
edOrbitLinesToggle
edOrderAggressiveBehaviour
edOrderDefensiveBehaviour
edOrderFocusTarget
edOrderFollow
edOrderHoldFire
edOrderHoldPosition
edOrderRequestDock
edPause
edPhotoCameraToggle
edPhotoCameraToggle_Buggy
edPitchDownButton
edPitchDownButton_Landing
edPitchUpButton
edPitchUpButton_Landing
edPrimaryFire
edQuickCommsPanel
edQuickCommsPanel_Buggy
edRadarDecreaseRange
edRadarIncreaseRange
edRecallDismissShip
edResetPowerDistribution
edResetPowerDistribution_Buggy
edRightThrustButton
edRightThrustButton_Landing
edRollLeftButton
edRollLeftButton_Landing
edRollRightButton
edRollRightButton_Landing
edSecondaryFire
edSelectHighestThreat
edSelectTarget
edSelectTarget_Buggy
edSelectTargetsTarget
edSetSpeed100
edSetSpeed25
edSetSpeed50
edSetSpeed75
edSetSpeedMinus100
edSetSpeedMinus25
edSetSpeedMinus50
edSetSpeedMinus75
edSetSpeedZero
edShipSpotLightToggle
edShowPGScoreSummaryInput
edSteerLeftButton
edSteerRightButton
edSupercruise
edSystemMapOpen
edSystemMapOpen_Buggy
edTargetNextRouteSystem
edTargetWingman0
edTargetWingman1
edTargetWingman2
edToggleBuggyTurretButton
edToggleButtonUpInput
edToggleCargoScoop
edToggleCargoScoop_Buggy
edToggleDriveAssist
edToggleFlightAssist
edToggleReverseThrottleInput
edUI_Back
edUI_Down
edUI_Left
edUI_Right
edUI_Select
edUI_Toggle
edUI_Up
edUIFocus
edUIFocus_Buggy
edUpThrustButton
edUpThrustButton_Landing
edUseAlternateFlightValuesToggle
edUseBoostJuice
edUseShieldCell
edVerticalThrustersButton
edWeaponColourToggle
edWingNavLock
edYawLeftButton
edYawLeftButton_Landing
edYawRightButton
edYawRightButton_Landing
edYawToRollButton
--------------------------------------------------------------------------------------------------------------------------------
Version History
--------------------------------------------------------------------------------------------------------------------------------
v1.0 - Initial release
v1.0.0.1 - Fixed file sorting issue when accessing .binds files.

BIN
docs/images/keypress.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

64
docs/index.md Normal file
View file

@ -0,0 +1,64 @@
# bindED
This VoiceAttack plugin reads keybindings in Elite:Dangerous and stores them as
VoiceAttack variables. It was originally written by Gary (the developer of
VoiceAttack) [and published on the VoiceAttack
forums](https://forum.voiceattack.com/smf/index.php?topic=564.0). You can find
the [original README here](ReadMe.txt) for reference.
I have basically done a complete rewrite the original source code at this point
and added a lot of features including automatic detection of the correct
bindings file and support for non-US keyboard layouts (see below for details).
## Installing
Grab `bindEDplugin.zip` from the [release
page](https://github.com/alterNERDtive/bindED/releases/latest) and extract it
into your VoiceAttacks `Apps` directory. If you have an older version already
installed, please delete the `bindED` subfolder first.
For Horizons players, thats it! When VoiceAttack loads, bindED will
automatically detect your bindings. It will also keep a watchful eye on Elites
bindings folder and reload them when there is a change!
For Odyssey players, there is an additional caveat: you have to use the same
preset for all 4 sections (general, ship, SRV, foot). Sadly its not apparent
from the files which of these sections a bind belongs to, so there is no simple
way to read multiple files properly.
## Using Binds In Commands
Each bind in Elite Dangerous has a name assigned in the binds file. The plugin
takes this name, prepends `ed` and turns it into a VoiceAttack variable.
To press the corresponding key in the game, you will have to create a new
`Key Press` action in a VoiceAttack command and use the desired variable named
after the ingame bind. In order to toggle your landing gear for example you will
have to create a `Key Press` action and use the `edLandingGearToggle` variable.
![[keypress.png]]
## Supported Keyboard Layouts
If you are using any non-US layout you might have noticed that some binds dont
work. You can set a text variable in VoiceAttack called `bindED.layout#` to the
layout you want to use. If the variable is not set it will defaut to `en-us`.
The following layouts are supported out of the box:
* en-US
* en-GB
* de-neo2
If you are on a different layout, you can either use only the keys that work
with the default layout or add support for your preferred layout yourself. See
[[Troubleshooting#Adding a Keyboard Layout]].
## Need Help / Want to Contribute?
If you run into any errors have a look at [[Troubleshooting]]. If your problem
persists, please [file an
issue](https://github.com/alterNERDtive/bindED/issues/new). Thanks! :)
You can also [say “Hi” on Discord](https://discord.gg/YeXh2s5UC6) if that is
your thing.

132
docs/troubleshooting.md Normal file
View file

@ -0,0 +1,132 @@
# Troubleshooting
## Diagnosing Plugin Issues
If something goes awry you can manually call the `loadbinds` plugin context or
restart VoiceAttack to force a refresh.
If that doesnt help and your problems persist the first step should be to
import and load the included `bindED-diagnostics` profile from the plugin
directory. It will output the current plugin state (keyboard layout, preset and
binds file used) to the VoiceAttack log. You should also include the output of
that if you file an issue or ask for help on Discord.
If that seems correct, theres the `bindED-reports` profile. It will generate
both a list of bind names used by Elite and a report of binds that do not have a
keyboard shortcut assigned, and put them on your Desktop. Note that those lists
are currently not filtered in any way and will contain _all_ binds regardless if
they are used in any profiles or if they can even have a keyboard key assigned
(e.g. axes).
## Migrating from the Old Plugin
If you use this as a drop-in replacement for the initial version of bindED made
by Gary all commands invoking the plugin will throw an error message. Gary has
asked me to change the plugins GUID, and the plugin with the old one will no
longer be found. _That is irrelevant in basically all cases and can safely be
ignored_.
Binds will be read automatically when VoiceAttack starts and whenever they
change. To get rid of the error message(s) remove the outdated plugin
invocation(s) from your commands.
## Make Sure You Actually Have Binds Files
Before starting VoiceAttack with the plugin installed, you need to load the game
at least once! That will create the directory structure the plugin is going to
read from. You also need to have changed _any_ key in controls options.
If you start Elite for the first time with VoiceAttack already running you will
have to either invoke the plugins `loadbinds` config manually or restart
VoiceAttack after you have loaded into the games main menu and changed any key
bind.
## Horizons vs. Odyssey
**Note**: If you do not own Odyssey, everything will work just as before!
Sadly for the time being Odyssey and Horizons will basically be separate games.
That also means they have separate binds files. BindED will always default to
using the file generated by Odyssey (`<preset>.4.0.binds`) if it exists.
To keep hassle to a minimum, the recommended way to change binds is to do it
from Odyssey. Whenever a change to the Odyssey file is detected, the plugin will
overwrite Horizons binds (`<preset>.3.0.binds`) with it. If you for some reason
want to keep entirely separate binds, you can set `bindED.disableHorizonsSync#`
(yes, including the pound sign) to `true` in your VoiceAttack profile. Whenever
you are playing Horizons you will have to tell the plugin to load the Horizons
file (see [[#Specifying a Binds File to Load]]).
## Specifying a Binds File to Load
You can set the text variable `~bindsFile` to a specific file name (e.g.
`custom.3.0.binds`) before executing the `loadbinds` context to have that
specific binds file loaded.
Make sure to only use the _file name_ of an existing binds file, do _not_
specify the full path.
This should be a last resort effort for when the game introduces changes that
break the plugins auto detection.
## Adding a Keyboard Layout
If you are using any non-US layout you might have noticed that some binds dont
work. The game itself supports a certain range of “standard” keyboard layouts
and falls back to UK QWERTY if you are using something else. The original plugin
was made for the US keyboard layout. So if you are using a layout that is not
natively supported by Elite and/or that has keys that dont exist on the US
layout, some keys will not work out of the box, e.g.:
* “VoiceAttack presses `p` but the game thinks its `v`!” (layout not natively
supported by Elite)
* “VoiceAttack presses `ß` but nothing happens!” (key not in the US key map)
I have included a map file for [Neo2](https://neo-layout.org)
(`EDMap-de-neo2.txt`) which is the layout that I am using personally and
A.Cyprus was kind enough to provide full support for en-GB. If you are on a
different layout, you will have to create a corresponding map file yourself. But
if you do Ill be happy to include it in future releases!
To add support for a new keyboard layout, you will have to create the
corresponding `EDMap-<id>.txt` file. Start by copying `EDMap-en-us.txt` in the
plugin directory and renaming it appropriately. The file contains a list of
`sym:code` pairs, one per line. The `sym` part is exactly like it is listed in
Elites `.binds` files, the `code` is the corresponding keycode VoiceAttack is
going to send.
You will have to go through the entire list and replace the existing `code`s
with the right ones for your layout. In order to do so I recommend following
these steps:
1. Open a reference for the UK QWERTY layout, [e.g. Wikipedias
image](https://en.wikipedia.org/wiki/QWERTY#/media/File:KB_United_Kingdom.svg).
1. Get a tool that can display keycodes for keys you press, [e.g. NirSofts
KeyboardStateView](https://www.nirsoft.net/utils/keyboard_state_view.html).
Or anything else that does it. Make sure it shows you _decimal_ keycodes
instead of (only) hex!
1. Make sure your PC is actually set to the layout you want to add.
1. Go through the file one by one.
1. Take note of its position on the keyboard. In this example we are using
the one in the top left next to `1` and my layout Neo2.
1. Find the correct key on the UK reference, for this example “Key_Grave”.
1. Press the corresponding key on your keyboard and observe the keycode it
produces. For Neo2 that was 186.
1. Change the `code` in the line to the correct keycode. For Neo2 the line
reads “Key_Grave:186”.
1. Repeat for the next line.
There are a bunch of symbols in the file that arent on a standard keyboard,
just ignore those. If you have extra keys that are not on the UK QWERTY layout,
youll have to go into the games controls options and try binding those keys.
If it works take the symbols they produce and go back to step 4 with them.
For the actual _letters_ youll have to assign the keycode of the key you are
pressing to the “Key_X” that is displayed ingame. So if the ingame controls
options say youve just pressed “H”, replace the “Key_H” line with the keycode
you get when you press the key you bound ingame. If the game assigns the letters
that are actually in that place on your layout, you wont have to do anything
for these.
Once you have tested and confirmed everything working, feel free to open a pull
request or issue and Ill include the new map!

26
mkdocs.yml Normal file
View file

@ -0,0 +1,26 @@
site_name: "bindED VoiceAttack plugin"
site_url: https://alterNERDtive.github.io/bindED
repo_url: https://github.com/alterNERDtive/bindED
edit_uri: "edit/devel/docs/"
site_description: "This VoiceAttack plugin reads keybindings in Elite:Dangerous and stores them as VoiceAttack variables."
site_author: "alterNERDtive"
remote_name: "ssh-origin"
theme:
name: readthedocs
prev_next_buttons_location: both
plugins:
- search
- roamlinks
markdown_extensions:
- toc:
permalink: True
- sane_lists
nav:
- 'index.md'
- 'troubleshooting.md'
- '⎋ Changelog': 'https://github.com/alterNERDtive/bindED/blob/release/CHANGELOG.md'
- '⎋ Report a Bug': 'https://github.com/alterNERDtive/bindED/issues/'