diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23ba39d..98cb402 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1093,8 +1093,7 @@ up to date in EDDN.
* Added `open [rat;] dispatch board` command. Opens the web dispatch board in
your default browser.
* Added proper handling for multiple ratsignals hitting at once. That’s mainly
- an IRC client config thing,
- [see the docs](docs/RatAttack.md#getting-case-data-from-irc).
+ an IRC client config thing, [see the docs](docs/configuration/RatAttack.md).
* Renamed `RatAttack.getInfoFromRatsignal` to
`RatAttack.announceCaseFromRatsignal`. Removed the “open case?” voice input
prompt.
diff --git a/plugins/EliteAttack/EliteAttack.csproj b/plugins/EliteAttack/EliteAttack.csproj
index 9fe30f5..839a74f 100644
--- a/plugins/EliteAttack/EliteAttack.csproj
+++ b/plugins/EliteAttack/EliteAttack.csproj
@@ -21,6 +21,7 @@
DEBUG;TRACE
prompt
4
+ ..\build\alterNERDtive\EliteAttack.xml
none
@@ -29,6 +30,7 @@
TRACE
prompt
4
+ ..\build\alterNERDtive\EliteAttack.xml
@@ -51,4 +53,4 @@
-
+
\ No newline at end of file
diff --git a/plugins/EliteAttack/Properties/AssemblyInfo.cs b/plugins/EliteAttack/Properties/AssemblyInfo.cs
index 11fe575..cedd4c4 100644
--- a/plugins/EliteAttack/Properties/AssemblyInfo.cs
+++ b/plugins/EliteAttack/Properties/AssemblyInfo.cs
@@ -1,4 +1,23 @@
-using System.Reflection;
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/plugins/RatAttack-cli/Properties/AssemblyInfo.cs b/plugins/RatAttack-cli/Properties/AssemblyInfo.cs
index cb8a8ea..3bd89ba 100644
--- a/plugins/RatAttack-cli/Properties/AssemblyInfo.cs
+++ b/plugins/RatAttack-cli/Properties/AssemblyInfo.cs
@@ -1,4 +1,23 @@
-using System.Reflection;
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/plugins/RatAttack-cli/RatAttack-cli.csproj b/plugins/RatAttack-cli/RatAttack-cli.csproj
index 0b495d3..2960a73 100644
--- a/plugins/RatAttack-cli/RatAttack-cli.csproj
+++ b/plugins/RatAttack-cli/RatAttack-cli.csproj
@@ -25,6 +25,7 @@
DEBUG;TRACE
prompt
4
+ ..\build\alterNERDtive\RatAttack-cli.xml
AnyCPU
@@ -34,6 +35,7 @@
TRACE
prompt
4
+ ..\build\alterNERDtive\RatAttack-cli.xml
diff --git a/plugins/RatAttack/Properties/AssemblyInfo.cs b/plugins/RatAttack/Properties/AssemblyInfo.cs
index 6079baf..1fd4faf 100644
--- a/plugins/RatAttack/Properties/AssemblyInfo.cs
+++ b/plugins/RatAttack/Properties/AssemblyInfo.cs
@@ -1,4 +1,23 @@
-using System.Reflection;
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/plugins/RatAttack/RatAttack.csproj b/plugins/RatAttack/RatAttack.csproj
index 1475205..3a3e126 100644
--- a/plugins/RatAttack/RatAttack.csproj
+++ b/plugins/RatAttack/RatAttack.csproj
@@ -25,6 +25,7 @@
DEBUG;TRACE
prompt
4
+ ..\build\alterNERDtive\RatAttack.xml
none
@@ -33,6 +34,7 @@
TRACE
prompt
4
+ ..\build\alterNERDtive\RatAttack.xml
@@ -57,4 +59,4 @@
-
+
\ No newline at end of file
diff --git a/plugins/SpanshAttack/Properties/AssemblyInfo.cs b/plugins/SpanshAttack/Properties/AssemblyInfo.cs
index 311a704..7fe5579 100644
--- a/plugins/SpanshAttack/Properties/AssemblyInfo.cs
+++ b/plugins/SpanshAttack/Properties/AssemblyInfo.cs
@@ -1,4 +1,23 @@
-using System.Reflection;
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/plugins/SpanshAttack/SpanshAttack.csproj b/plugins/SpanshAttack/SpanshAttack.csproj
index a279b46..4c736e5 100644
--- a/plugins/SpanshAttack/SpanshAttack.csproj
+++ b/plugins/SpanshAttack/SpanshAttack.csproj
@@ -21,6 +21,7 @@
DEBUG;TRACE
prompt
4
+ ..\build\alterNERDtive\SpanshAttack.xml
pdbonly
@@ -29,6 +30,7 @@
TRACE
prompt
4
+ ..\build\alterNERDtive\SpanshAttack.xml
@@ -51,4 +53,4 @@
-
+
\ No newline at end of file
diff --git a/plugins/VoiceAttack-base/GlobalSuppressions.cs b/plugins/VoiceAttack-base/GlobalSuppressions.cs
index 9a06d8e..32cfcb3 100644
--- a/plugins/VoiceAttack-base/GlobalSuppressions.cs
+++ b/plugins/VoiceAttack-base/GlobalSuppressions.cs
@@ -1,10 +1,28 @@
-// This file is used by Code Analysis to maintain SuppressMessage
-// attributes that are applied to this project.
-// Project-level suppressions either have no target or are given
-// a specific target and scoped to a namespace, type, member, etc.
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
using System.Diagnostics.CodeAnalysis;
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "just cause", Scope = "namespace", Target = "~N:alterNERDtive")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "just cause", Scope = "namespace", Target = "~N:alterNERDtive.edts")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "just cause", Scope = "namespace", Target = "~N:alterNERDtive.util")]
diff --git a/plugins/VoiceAttack-base/Properties/AssemblyInfo.cs b/plugins/VoiceAttack-base/Properties/AssemblyInfo.cs
index aabd5aa..42518aa 100644
--- a/plugins/VoiceAttack-base/Properties/AssemblyInfo.cs
+++ b/plugins/VoiceAttack-base/Properties/AssemblyInfo.cs
@@ -1,4 +1,23 @@
-using System.Reflection;
+//
+// Copyright 2019–2022 alterNERDtive.
+//
+// This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see <https://www.gnu.org/licenses/>.
+//
+
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -10,7 +29,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VoiceAttack-base")]
-[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyCopyright("Copyright © 2020–2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
diff --git a/plugins/VoiceAttack-base/SettingsDialog.xaml.cs b/plugins/VoiceAttack-base/SettingsDialog.xaml.cs
index 3134a3b..5e1ebd8 100644
--- a/plugins/VoiceAttack-base/SettingsDialog.xaml.cs
+++ b/plugins/VoiceAttack-base/SettingsDialog.xaml.cs
@@ -56,7 +56,7 @@ namespace alterNERDtive
tab.IsEnabled = BasePlugin.IsProfileActive(profile);
StackPanel panel = new StackPanel();
- util.Configuration.OptDict options = config.GetOptions(profile);
+ util.Configuration.OptDict options = util.Configuration.GetOptions(profile);
foreach (dynamic option in options.Values)
{
diff --git a/plugins/VoiceAttack-base/VoiceAttack-base.csproj b/plugins/VoiceAttack-base/VoiceAttack-base.csproj
index b478461..df39c70 100644
--- a/plugins/VoiceAttack-base/VoiceAttack-base.csproj
+++ b/plugins/VoiceAttack-base/VoiceAttack-base.csproj
@@ -24,6 +24,7 @@
DEBUG;TRACE
prompt
4
+ ..\build\alterNERDtive\base.xml
none
@@ -32,6 +33,7 @@
TRACE
prompt
4
+ ..\build\alterNERDtive\base.xml
@@ -76,4 +78,4 @@
-
+
\ No newline at end of file
diff --git a/plugins/VoiceAttack-base/base.cs b/plugins/VoiceAttack-base/base.cs
index 73d7b08..d308260 100644
--- a/plugins/VoiceAttack-base/base.cs
+++ b/plugins/VoiceAttack-base/base.cs
@@ -119,8 +119,6 @@ namespace alterNERDtive
/// The VoiceAttack proxy object.
public static void VA_Invoke1(dynamic vaProxy)
{
- VA = vaProxy;
-
string context = vaProxy.Context.ToLower();
Log.Debug($"Running context '{context}' …");
try
@@ -128,49 +126,49 @@ namespace alterNERDtive
switch (context)
{
case "startup":
- Context_Startup();
+ Context_Startup(vaProxy);
break;
case "config.dialog":
// config
- Context_Config_Dialog();
+ Context_Config_Dialog(vaProxy);
break;
case "config.dump":
- Context_Config_Dump();
+ Context_Config_Dump(vaProxy);
break;
case "config.getvariables":
- Context_Config_SetVariables();
+ Context_Config_SetVariables(vaProxy);
break;
case "config.list":
- Context_Config_List();
+ Context_Config_List(vaProxy);
break;
case "config.setup":
- Context_Config_Setup();
+ Context_Config_Setup(vaProxy);
break;
case "config.versionmigration":
- Context_Config_VersionMigration();
+ Context_Config_VersionMigration(vaProxy);
break;
case "edsm.bodycount":
// EDSM
- Context_EDSM_BodyCount();
+ Context_EDSM_BodyCount(vaProxy);
break;
case "edsm.distancebetween":
- Context_EDSM_DistanceBetween();
+ Context_EDSM_DistanceBetween(vaProxy);
break;
case "eddi.event":
// EDDI
- Context_Eddi_Event();
+ Context_Eddi_Event(vaProxy);
break;
case "spansh.outdatedstations":
// Spansh
- Context_Spansh_OutdatedStations();
+ Context_Spansh_OutdatedStations(vaProxy);
break;
case "log.log":
// log
- Context_Log();
+ Context_Log(vaProxy);
break;
case "update.check":
// update
- Context_Update_Check();
+ Context_Update_Check(vaProxy);
break;
default:
// invalid
@@ -248,7 +246,7 @@ namespace alterNERDtive
string name = match.Groups["name"].Value;
Log.Debug($"Configuration has changed, '{id}.{name}': '{from}' → '{to}'");
- dynamic o = Config.GetOption(id, name);
+ dynamic o = Configuration.GetOption(id, name);
// When loaded from profile but not explicitly set, will be null.
// Then load default.
diff --git a/plugins/VoiceAttack-base/util.cs b/plugins/VoiceAttack-base/util.cs
index a2366e4..1a98ba9 100644
--- a/plugins/VoiceAttack-base/util.cs
+++ b/plugins/VoiceAttack-base/util.cs
@@ -28,241 +28,510 @@ using System.Linq;
namespace alterNERDtive.util
{
+ ///
+ /// Log levels that can be used when writing to the VoiceAttack log.
+ ///
+ public enum LogLevel
+ {
+ ///
+ /// Log level for error messages. Errors cause execution to abort.
+ ///
+ ERROR,
+
+ ///
+ /// Log level for warning messages. Warnings should not cause execution
+ /// to abort.
+ ///
+ WARN,
+
+ ///
+ /// Log level for notices. Notices should be noteworthy.
+ ///
+ NOTICE,
+
+ ///
+ /// Log level for informational messages. These should not be
+ /// noteworthy.
+ ///
+ INFO,
+
+ ///
+ /// Log level for debug messages. They should be useful only for
+ /// debugging.
+ ///
+ DEBUG,
+ }
+
+ ///
+ /// Interface describing an object that can be send through a Pipe. Needs
+ /// serializing to and deserializing from .
+ ///
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "file contains collection of utility classes")]
+ public interface IPipable
+ {
+ ///
+ /// Parses object data from a serialized string.
+ ///
+ /// The serialized string.
+ public void ParseString(string serialization);
+
+ ///
+ /// Serializes the object to a string.
+ ///
+ /// The serialized string.
+ public string ToString();
+ }
+
+ ///
+ /// Contains the configuration for a plugin/profile and handles
+ /// configuration-related VoiceAttack interactions.
+ ///
public class Configuration
{
- private readonly dynamic VA;
- private readonly string ID;
- private readonly VoiceAttackLog Log;
- private readonly VoiceAttackCommands Commands;
- private static readonly Dictionary> Defaults = new Dictionary>
+ private static readonly Dictionary> Defaults = new ()
{
{
"alterNERDtive-base",
- new OptDict{
+ new OptDict
+ {
/*{ new Option("delays.keyPressDuration", (decimal)0.01, voiceTrigger: "key press duration",
description: "The time keys will be held down for.") },*/
- { new Option("delays.quitToDesktop", (decimal)10.0, voiceTrigger: "quit to desktop delay",
- description: "The delay before restarting the game after hitting “Exit to Desktop”, in seconds.\nDefault: 10.0. (Used by the `restart from desktop` command)") },
- { new Option("eddi.quietMode", true, voiceTrigger: "eddi quiet mode",
- description: "Make EDDI shut up. Disables all built-in speech responders.") },
- { new Option("elite.pasteKey", "v", voiceTrigger: "elite paste key",
- description: "The key used to paste in conjunction with CTRL. The physical key in your layout that would be 'V' on QWERTY.") },
- { new Option("enableAutoUpdateCheck", true, voiceTrigger: "auto update check",
- description: "Automatically check Github for profiles updates when the profile loads.") },
- { new Option("log.logLevel", "NOTICE", voiceTrigger: "log level", validValues: new List{ "ERROR", "WARN", "NOTICE", "INFO", "DEBUG" },
- description: "The level of detail for logging to the VoiceAttack log.\nValid levels are \"ERROR\", \"WARN\", \"NOTICE\", \"INFO\" and \"DEBUG\".\nDefault: \"NOTICE\".") },
+ {
+ new Option(
+ name: "delays.quitToDesktop",
+ defaultValue: 10.0M,
+ voiceTrigger: "quit to desktop delay",
+ description: "The delay before restarting the game after hitting “Exit to Desktop”, in seconds.\nDefault: 10.0. (Used by the `restart from desktop` command)")
+ },
+ {
+ new Option(
+ name: "eddi.quietMode",
+ defaultValue: true,
+ voiceTrigger: "eddi quiet mode",
+ description: "Make EDDI shut up. Disables all built-in speech responders.")
+ },
+ {
+ new Option(
+ name: "elite.pasteKey",
+ defaultValue: "v",
+ voiceTrigger: "elite paste key",
+ description: "The key used to paste in conjunction with CTRL. The physical key in your layout that would be 'V' on QWERTY.")
+ },
+ {
+ new Option(
+ name: "enableAutoUpdateCheck",
+ defaultValue: true,
+ voiceTrigger: "auto update check",
+ description: "Automatically check Github for profiles updates when the profile loads.")
+ },
+ {
+ new Option(
+ name: "log.logLevel",
+ defaultValue: "NOTICE",
+ voiceTrigger: "log level",
+ validValues: new List { "ERROR", "WARN", "NOTICE", "INFO", "DEBUG" },
+ description: "The level of detail for logging to the VoiceAttack log.\nValid levels are \"ERROR\", \"WARN\", \"NOTICE\", \"INFO\" and \"DEBUG\".\nDefault: \"NOTICE\".")
+ },
}
},
{
"EliteAttack",
- new OptDict{
- { new Option("announceEdsmSystemStatus", true, voiceTrigger: "edsm system status",
- description: "Pull system data from EDSM and compare it against your discovery scan.") },
- { new Option("announceJumpsInRoute", true, voiceTrigger: "route jump count",
- description: "Give a jump count on plotting a route.") },
- { new Option("announceMappingCandidates", true, voiceTrigger: "mapping candidates",
- description: "Announce bodies worth mapping when you have finished scanning a system.\n(Terraformables, Water Worlds, Earth-Like Worlds and Ammonia Worlds that have not been mapped yet.)") },
- { new Option("announceOutdatedStationData", true, voiceTrigger: "outdated stations",
- description: "Announce stations with outdated data in the online databases.") },
- { new Option("outdatedStationThreshold", 365, voiceTrigger: "outdated station threshold",
- description: "The threshold for station data to count as “outdated”, in days.\nDefault: 365.") },
- { new Option("announceR2RMappingCandidates", false, voiceTrigger: "road to riches",
- description: "Announce bodies worth scanning if you are looking for some starting cash on the Road to Riches.") },
- { new Option("announceRepairs", true, voiceTrigger: "repair reports",
- description: "Report on AFMU repairs.") },
- { new Option("announceSynthesis", true, voiceTrigger: "synthesis reports",
- description: "Report on synthesis.") },
- { new Option("autoHonkNewSystems", true, voiceTrigger: "auto honk new systems",
- description: "Automatically honk upon entering a system if it is your first visit.") },
- { new Option("autoHonkAllSystems", false, voiceTrigger: "auto honk all systems",
- description: "Automatically honk upon entering a system, each jump, without constraints.") },
- { new Option("scannerFireGroup", 0, voiceTrigger: "scanner fire group",
- description: "The fire group your discovery scanner is assigned to.\nDefault: 0 (the first one).") },
- { new Option("usePrimaryFireForDiscoveryScan", false, voiceTrigger: "discovery scan on primary fire",
- description: "Use primary fire for honking instead of secondary.") },
- { new Option("autoRefuel", true, voiceTrigger: "auto refuel",
- description: "Automatically refuel after docking at a station.") },
- { new Option("autoRepair", true, voiceTrigger: "auto repair",
- description: "Automatically repair after docking at a station.") },
- { new Option("autoRestock", true, voiceTrigger: "auto restock",
- description: "Automatically restock after docking at a station.") },
- { new Option("autoHangar", true, voiceTrigger: "auto move to hangar",
- description: "Automatically move the ship to the hangar after docking at a station.") },
- { new Option("autoStationServices", true, voiceTrigger: "auto enter station services",
- description: "Automatically enter the Station Services menu after docking at a station.") },
- { new Option("autoRetractLandingGear", true, voiceTrigger: "auto retract landing gear",
- description: "Automatically retract landing gear when lifting off a planet / undocking from a station.") },
- { new Option("autoDisableSrvLights", true, voiceTrigger: "auto disable s r v lights",
- description: "Automatically turn SRV lights off when deploying one.") },
- { new Option("flightAssistOff", false, voiceTrigger: "flight assist off",
- description: "Permanent Flight Assist off mode. You should really do that, it’s great.") },
- { new Option("hyperspaceDethrottle", true, voiceTrigger: "hyper space dethrottle",
- description: "Throttle down after a jump and when dropping from SC. Like the SC Assist module does.") },
- { new Option("limpetCheck", true, voiceTrigger: "limpet check",
- description: "Do a limpet check when undocking, reminding you if you forgot to buy some.") },
+ new OptDict
+ {
+ {
+ new Option(
+ name: "announceEdsmSystemStatus",
+ defaultValue: true,
+ voiceTrigger: "edsm system status",
+ description: "Pull system data from EDSM and compare it against your discovery scan.")
+ },
+ {
+ new Option(
+ name: "announceJumpsInRoute",
+ defaultValue: true,
+ voiceTrigger: "route jump count",
+ description: "Give a jump count on plotting a route.")
+ },
+ {
+ new Option(
+ name: "announceMappingCandidates",
+ defaultValue: true,
+ voiceTrigger: "mapping candidates",
+ description: "Announce bodies worth mapping when you have finished scanning a system.\n(Terraformables, Water Worlds, Earth-Like Worlds and Ammonia Worlds that have not been mapped yet.)")
+ },
+ {
+ new Option(
+ name: "announceOutdatedStationData",
+ defaultValue: true,
+ voiceTrigger: "outdated stations",
+ description: "Announce stations with outdated data in the online databases.")
+ },
+ {
+ new Option(
+ name: "outdatedStationThreshold",
+ defaultValue: 365,
+ voiceTrigger: "outdated station threshold",
+ description: "The threshold for station data to count as “outdated”, in days.\nDefault: 365.")
+ },
+ {
+ new Option(
+ name: "announceR2RMappingCandidates",
+ defaultValue: false,
+ voiceTrigger: "road to riches",
+ description: "Announce bodies worth scanning if you are looking for some starting cash on the Road to Riches.")
+ },
+ {
+ new Option(
+ name: "announceRepairs",
+ defaultValue: true,
+ voiceTrigger: "repair reports",
+ description: "Report on AFMU repairs.")
+ },
+ {
+ new Option(
+ name: "announceSynthesis",
+ defaultValue: true,
+ voiceTrigger: "synthesis reports",
+ description: "Report on synthesis.")
+ },
+ {
+ new Option(
+ name: "autoHonkNewSystems",
+ defaultValue: true,
+ voiceTrigger: "auto honk new systems",
+ description: "Automatically honk upon entering a system if it is your first visit.")
+ },
+ {
+ new Option(
+ name: "autoHonkAllSystems",
+ defaultValue: false,
+ voiceTrigger: "auto honk all systems",
+ description: "Automatically honk upon entering a system, each jump, without constraints.")
+ },
+ {
+ new Option(
+ name: "scannerFireGroup",
+ defaultValue: 0,
+ voiceTrigger: "scanner fire group",
+ description: "The fire group your discovery scanner is assigned to.\nDefault: 0 (the first one).")
+ },
+ {
+ new Option(
+ name: "usePrimaryFireForDiscoveryScan",
+ defaultValue: false,
+ voiceTrigger: "discovery scan on primary fire",
+ description: "Use primary fire for honking instead of secondary.")
+ },
+ {
+ new Option(
+ name: "autoRefuel",
+ defaultValue: true,
+ voiceTrigger: "auto refuel",
+ description: "Automatically refuel after docking at a station.")
+ },
+ {
+ new Option(
+ name: "autoRepair",
+ defaultValue: true,
+ voiceTrigger: "auto repair",
+ description: "Automatically repair after docking at a station.")
+ },
+ {
+ new Option(
+ name: "autoRestock",
+ defaultValue: true,
+ voiceTrigger: "auto restock",
+ description: "Automatically restock after docking at a station.")
+ },
+ {
+ new Option(
+ name: "autoHangar",
+ defaultValue: true,
+ voiceTrigger: "auto move to hangar",
+ description: "Automatically move the ship to the hangar after docking at a station.")
+ },
+ {
+ new Option(
+ name: "autoStationServices",
+ defaultValue: true,
+ voiceTrigger: "auto enter station services",
+ description: "Automatically enter the Station Services menu after docking at a station.")
+ },
+ {
+ new Option(
+ name: "autoRetractLandingGear",
+ defaultValue: true,
+ voiceTrigger: "auto retract landing gear",
+ description: "Automatically retract landing gear when lifting off a planet / undocking from a station.")
+ },
+ {
+ new Option(
+ name: "autoDisableSrvLights",
+ defaultValue: true,
+ voiceTrigger: "auto disable s r v lights",
+ description: "Automatically turn SRV lights off when deploying one.")
+ },
+ {
+ new Option(
+ name: "flightAssistOff",
+ defaultValue: false,
+ voiceTrigger: "flight assist off",
+ description: "Permanent Flight Assist off mode. You should really do that, it’s great.")
+ },
+ {
+ new Option(
+ name: "hyperspaceDethrottle",
+ defaultValue: true,
+ voiceTrigger: "hyper space dethrottle",
+ description: "Throttle down after a jump and when dropping from SC. Like the SC Assist module does.")
+ },
+ {
+ new Option(
+ name: "limpetCheck",
+ defaultValue: true,
+ voiceTrigger: "limpet check",
+ description: "Do a limpet check when undocking, reminding you if you forgot to buy some.")
+ },
}
},
{
"RatAttack",
- new OptDict{
- { new Option("autoCloseCase", false, voiceTrigger: "auto close fuel rat case",
- description: "Automatically close a rat case when sending “fuel+” via voice command or ingame chat.") },
- { new Option("autoCopySystem", true, voiceTrigger: "auto copy rat case system",
- description: "Automatically copy the client’s system to the clipboard when you open a rat case.") },
- { new Option("announceNearestCMDR", false, voiceTrigger: "nearest commander to fuel rat case",
- description: "Announce the nearest commander to incoming rat cases.") },
- { new Option("CMDRs", "", voiceTrigger: "fuel rat commanders",
- description: "All your CMDRs that are ready to take rat cases.\nUse ‘;’ as separator, e.g. “Bud Spencer;Terrence Hill”.") },
- { new Option("announcePlatform", false, voiceTrigger: "platform for fuel rat case",
- description: "Announce the platform for incoming rat cases.") },
- { new Option("platforms", "PC", voiceTrigger: "fuel rat platforms", validValues: new List{ "PC", "Xbox", "Playstation" },
- description: "The platform(s) you want to get case announcements for (PC, Xbox, Playstation).\nUse ‘;’ as separator, e.g. “PC;Xbox”.") },
- { new Option("announceSystemInfo", true, voiceTrigger: "system information for fuel rat case",
- description: "System information provided by Mecha.")},
- { new Option("confirmCalls", true, voiceTrigger: "fuel rat call confirmation",
- description: "Only make calls in #fuelrats after vocal confirmation to prevent mistakes.") },
- { new Option("onDuty", true, voiceTrigger: "fuel rat duty",
- description: "On duty, receiving case announcements via TTS.") },
+ new OptDict
+ {
+ {
+ new Option(
+ name: "autoCloseCase",
+ defaultValue: false,
+ voiceTrigger: "auto close fuel rat case",
+ description: "Automatically close a rat case when sending “fuel+” via voice command or ingame chat.")
+ },
+ {
+ new Option(
+ "autoCopySystem",
+ defaultValue: true,
+ voiceTrigger: "auto copy rat case system",
+ description: "Automatically copy the client’s system to the clipboard when you open a rat case.")
+ },
+ {
+ new Option(
+ name: "announceNearestCMDR",
+ defaultValue: false,
+ voiceTrigger: "nearest commander to fuel rat case",
+ description: "Announce the nearest commander to incoming rat cases.")
+ },
+ {
+ new Option(
+ name: "CMDRs",
+ defaultValue: string.Empty,
+ voiceTrigger: "fuel rat commanders",
+ description: "All your CMDRs that are ready to take rat cases.\nUse ‘;’ as separator, e.g. “Bud Spencer;Terrence Hill”.")
+ },
+ {
+ new Option(
+ name: "announcePlatform",
+ defaultValue: false,
+ voiceTrigger: "platform for fuel rat case",
+ description: "Announce the platform for incoming rat cases.")
+ },
+ {
+ new Option(
+ name: "platforms",
+ defaultValue: "PC",
+ voiceTrigger: "fuel rat platforms",
+ validValues: new List { "PC", "Xbox", "Playstation" },
+ description: "The platform(s) you want to get case announcements for (PC, Xbox, Playstation).\nUse ‘;’ as separator, e.g. “PC;Xbox”.")
+ },
+ {
+ new Option(
+ name: "announceSystemInfo",
+ defaultValue: true,
+ voiceTrigger: "system information for fuel rat case",
+ description: "System information provided by Mecha.")
+ },
+ {
+ new Option(
+ name: "confirmCalls",
+ defaultValue: true,
+ voiceTrigger: "fuel rat call confirmation",
+ description: "Only make calls in #fuelrats after vocal confirmation to prevent mistakes.")
+ },
+ {
+ new Option(
+ name: "onDuty",
+ defaultValue: true,
+ voiceTrigger: "fuel rat duty",
+ description: "On duty, receiving case announcements via TTS.")
+ },
}
},
{
"SpanshAttack",
- new OptDict{
- { new Option("announceJumpsLeft", ";1;3;5;10;15;20;30;50;75;100;", voiceTrigger: "announce jumps left",
- description: "Estimated jumps left to announce when reached.\nNEEDS to have leading and trailing “;”.") },
- { new Option("announceWaypoints", true, voiceTrigger: "waypoint announcements",
- description: "Announce each waypoint by name.") },
- { new Option("autoJumpAfterScooping", true, voiceTrigger: "auto jump after scooping",
- description: "Automatically jump out when fuel scooping is complete.") },
- { new Option("autoPlot", true, voiceTrigger: "auto plot",
- description: "Automatically plot to the next waypoint after supercharging.") },
- { new Option("clearOnShutdown", true, voiceTrigger: "clear neutron route on shutdown",
- description: "Clear an active neutron route when the game is shut down.") },
- { new Option("copyWaypointToClipboard", false, voiceTrigger: "copy neutron waypoints to clipboard",
- description: "Copy each neutron waypoint into the Windows clipboard.") },
- { new Option("defaultToLadenRange", false, voiceTrigger: "default to laden range",
- description: "Default to the current ship’s laden range as reported by EDDI instead of prompting for input.") },
- { new Option("timeTrip", false, voiceTrigger: "time neutron route",
- description: "Keep track of how long a neutron route takes you to complete.") },
+ new OptDict
+ {
+ {
+ new Option(
+ name: "announceJumpsLeft",
+ defaultValue: ";1;3;5;10;15;20;30;50;75;100;",
+ voiceTrigger: "announce jumps left",
+ description: "Estimated jumps left to announce when reached.\nNEEDS to have leading and trailing “;”.")
+ },
+ {
+ new Option(
+ name: "announceWaypoints",
+ defaultValue: true,
+ voiceTrigger: "waypoint announcements",
+ description: "Announce each waypoint by name.")
+ },
+ {
+ new Option(
+ name: "autoJumpAfterScooping",
+ defaultValue: true,
+ voiceTrigger: "auto jump after scooping",
+ description: "Automatically jump out when fuel scooping is complete.")
+ },
+ {
+ new Option(
+ name: "autoPlot",
+ defaultValue: true,
+ voiceTrigger: "auto plot",
+ description: "Automatically plot to the next waypoint after supercharging.")
+ },
+ {
+ new Option(
+ name: "clearOnShutdown",
+ defaultValue: true,
+ voiceTrigger: "clear neutron route on shutdown",
+ description: "Clear an active neutron route when the game is shut down.")
+ },
+ {
+ new Option(
+ name: "copyWaypointToClipboard",
+ defaultValue: false,
+ voiceTrigger: "copy neutron waypoints to clipboard",
+ description: "Copy each neutron waypoint into the Windows clipboard.")
+ },
+ {
+ new Option(
+ "defaultToLadenRange",
+ defaultValue: false,
+ voiceTrigger: "default to laden range",
+ description: "Default to the current ship’s laden range as reported by EDDI instead of prompting for input.")
+ },
+ {
+ new Option(
+ name: "timeTrip",
+ defaultValue: false,
+ voiceTrigger: "time neutron route",
+ description: "Keep track of how long a neutron route takes you to complete.")
+ },
}
},
{
"StreamAttack",
- new OptDict{
- { new Option("outputDir", @"%appdata%\StreamAttack\", voiceTrigger: "StreamAttack output directory",
- description: "The directory the status files are written to.") }
+ new OptDict
+ {
+ {
+ new Option(
+ name: "outputDir",
+ defaultValue: @"%appdata%\StreamAttack\",
+ voiceTrigger: "StreamAttack output directory",
+ description: "The directory the status files are written to.")
+ },
}
- }
+ },
};
- public abstract class Option
- {
-#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
- public string Name { get; }
- public dynamic DefaultValue { get; }
- public List? ValidValues { get; }
- public string VoiceTrigger { get; }
- public string TtsDescription { get; }
- public string Description { get; }
- public Type Type { get; }
+#pragma warning disable SA1306 // Field names should begin with lower-case letter
+ private readonly dynamic VA;
+ private readonly string ID;
+#pragma warning restore SA1306 // Field names should begin with lower-case letter
+ private readonly VoiceAttackLog log;
+ private readonly VoiceAttackCommands commands;
- public string? TypeString { get; }
- public override string ToString() => DefaultValue!.ToString();
-#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
- }
- public class Option : Option
- {
- public new string Name { get; }
- public new T DefaultValue { get; }
- public new List? ValidValues { get; }
- public new string VoiceTrigger { get; }
- public new string TtsDescription { get => ttsDescription ?? VoiceTrigger; }
- private readonly string? ttsDescription;
- public new string Description { get => description ?? "No description available."; }
- private readonly string? description;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The VoiceAttack proxy object.
+ /// The VoiceAttack log.
+ /// The VoiceAttack commands object.
+ /// The profile ID.
+ public Configuration(dynamic vaProxy, VoiceAttackLog log, VoiceAttackCommands commands, string id)
+ => (this.VA, this.log, this.commands, this.ID)
+ = (vaProxy, log, commands, id);
- public new Type Type { get => typeof(T); }
- public new string? TypeString
- {
- get
- {
- string? type = null;
- if (typeof(T) == typeof(bool))
- {
- type = "boolean";
- }
- else if (typeof(T) == typeof(DateTime))
- {
- type = "date";
- }
- else if (typeof(T) == typeof(decimal))
- {
- type = "decimal";
- }
- else if (typeof(T) == typeof(int))
- {
- type = "int";
- }
- else if (typeof(T) == typeof(short))
- {
- type = "smallint";
- }
- else if (typeof(T) == typeof(string))
- {
- type = "text";
- }
- return type;
- }
- }
-
- public Option(string name, T defaultValue, string voiceTrigger, List? validValues = null, string? ttsDescription = null, string? description = null)
- => (Name, DefaultValue, VoiceTrigger, ValidValues, this.ttsDescription, this.description) = (name, defaultValue, voiceTrigger, validValues, ttsDescription, description);
-
-
- public static implicit operator (string, Option)(Option o) => (o.Name, o);
- public static explicit operator T(Option o) => o.DefaultValue;
- }
- public class OptDict : Dictionary
- {
- public OptDict() : base() { }
- public OptDict(int capacity) : base(capacity) { }
-
- public void Add((TKey,TValue) tuple)
- {
- base.Add(tuple.Item1, tuple.Item2);
- }
- }
-
- public Configuration(dynamic vaProxy, VoiceAttackLog log, VoiceAttackCommands commands, string id) => (VA, Log, Commands, ID) = (vaProxy, log, commands, id);
-
- public dynamic GetDefault(string name)
- {
- return GetDefault(ID, name);
- }
+ ///
+ /// Gets the default value of an option.
+ ///
+ /// The profile ID.
+ /// The name of the option.
+ /// The default value of the option.
public static dynamic GetDefault(string id, string name)
{
- return Defaults[id][name];
+ return ((Option)Defaults[id][name]).DefaultValue;
}
- public Option GetOption(string name)
- {
- return GetOption(ID, name);
- }
- public Option GetOption(string id, string name)
+ ///
+ /// Gets an Option.
+ ///
+ /// The profile ID.
+ /// The name of the Option.
+ /// The Option.
+ public static Option GetOption(string id, string name)
{
return Defaults[id][name];
}
- public OptDict GetOptions(string id)
+ ///
+ /// Gets all Options for a give profile.
+ ///
+ /// The profile ID.
+ /// The
+ public static OptDict GetOptions(string id)
{
return Defaults[id];
}
- public void SetVoiceTriggers(System.Type type)
+ ///
+ /// Checks if a given option has a default value.
+ ///
+ /// The profile ID.
+ /// The name of the option.
+ /// Wether the option has a default value.
+ public static bool HasDefault(string id, string name)
{
- List triggers = new List();
- foreach (Dictionary options in Defaults.Values)
+ return Defaults[id].ContainsKey(name);
+ }
+
+ ///
+ /// Gets the default value of an option by name.
+ ///
+ /// The name of the option.
+ /// The value of the option.
+ public dynamic GetDefault(string name)
+ {
+ return GetDefault(this.ID, name);
+ }
+
+ ///
+ /// Gets an Option by name.
+ ///
+ /// The name of the Option.
+ /// The Option.
+ public Option GetOption(string name)
+ {
+ return GetOption(this.ID, name);
+ }
+
+ ///
+ /// Sets the voice triggers for the voice commands that set options from
+ /// VoiceAttack.
+ ///
+ /// The data type to set voice triggers for.
+ /// Thrown when duplicate triggers are found.
+ public void SetVoiceTriggers(Type type)
+ {
+ List triggers = new ();
+ foreach (Dictionary options in Defaults.Values)
{
foreach (dynamic option in options.Values)
{
@@ -272,30 +541,39 @@ namespace alterNERDtive.util
{
throw new ArgumentException($"Voice trigger '{option.VoiceTrigger}' is not unique, aborting …");
}
+
triggers.Add(option.VoiceTrigger);
}
}
}
+
if (triggers.Count > 0)
{
string triggerString = string.Join(";", triggers);
- VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
- Log.Debug($"Voice triggers for {type.Name}: '{triggerString}'");
+ this.VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
+ this.log.Debug($"Voice triggers for {type.Name}: '{triggerString}'");
}
else
{
// make sure we don’t accidentally have weird things happening with empty config voice triggers
string triggerString = $"tenuiadafesslejüsljlejutlesuivle{type.Name}";
- VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
- Log.Debug($"No voice triggers found for {type.Name}");
+ this.VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
+ this.log.Debug($"No voice triggers found for {type.Name}");
}
}
+ ///
+ /// Sets VoiceAttack variables required for handling the reporting and
+ /// setting of a configuration option.
+ ///
+ /// The command’s VoiceAttack proxy object.
+ /// The voice trigger for the option in question.
+ /// Thrown when the voice trigger is missing/null.
public void SetVariablesForTrigger(dynamic vaProxy, string trigger)
{
_ = trigger ?? throw new ArgumentNullException("trigger");
- foreach (KeyValuePair> options in Defaults)
+ foreach (KeyValuePair> options in Defaults)
{
try
{
@@ -305,34 +583,49 @@ namespace alterNERDtive.util
vaProxy.SetText("~description", option.Description);
break;
}
- catch (InvalidOperationException) { }
+ catch (InvalidOperationException)
+ {
+ // trigger doesn’t exist in this profile, skip
+ }
}
}
+ ///
+ /// Checks if a given option has a default value.
+ ///
+ /// The name of the option.
+ /// Whether the option has a default value.
public bool HasDefault(string name)
{
- return HasDefault(ID, name);
- }
- public static bool HasDefault(string id, string name)
- {
- return Defaults[id].ContainsKey(name);
+ return HasDefault(this.ID, name);
}
+ ///
+ /// Loads stored value of all options from the currently active
+ /// VoiceAttack profile.
+ ///
public void LoadFromProfile()
{
foreach (KeyValuePair> options in Defaults)
{
- LoadFromProfile(options.Key);
+ this.LoadFromProfile(options.Key);
}
}
+
+ ///
+ /// Loads stored options for a given profile ID from the currently
+ /// active VoiceAttack profile.
+ ///
+ /// The profile ID.
+ /// Thrown when encountering an incompatible data type.
public void LoadFromProfile(string id)
{
foreach (dynamic option in Defaults[id].Values)
{
string name = $"{id}.{option.Name}";
string type = option.TypeString ?? throw new InvalidDataException($"Invalid data type for option '{name}': '{option}'");
- Log.Debug($"Loading value for option '{name}' from profile …");
- Commands.Run("alterNERDtive-base.loadVariableFromProfile", wait: true, parameters: new dynamic[] { new string[] { $"{name}#", type } });
+ this.log.Debug($"Loading value for option '{name}' from profile …");
+ this.commands.Run("alterNERDtive-base.loadVariableFromProfile", wait: true, parameters: new dynamic[] { new string[] { $"{name}#", type } });
}
}
@@ -340,17 +633,18 @@ namespace alterNERDtive.util
{
return ApplyDefault(ID, name);
}
+
public dynamic? ApplyDefault(string id, string name)
{
if (!HasDefault(id, name))
{
- Log.Warn($"No default configuration value found for '{id}.{name}'");
+ log.Warn($"No default configuration value found for '{id}.{name}'");
return null;
}
dynamic option = Defaults[id][name];
dynamic value = option.DefaultValue;
- Log.Debug($"Loading default configuration value, '{id}.{name}': '{value}' …");
+ log.Debug($"Loading default configuration value, '{id}.{name}': '{value}' …");
string variable = $"{id}.{name}#";
if (value is bool)
{
@@ -410,7 +704,7 @@ namespace alterNERDtive.util
}
public void DumpConfig(string id)
{
- Log.Notice($"===== {id} configuration: =====");
+ log.Notice($"===== {id} configuration: =====");
foreach (string name in Defaults[id].Keys)
{
DumpConfig(id, name);
@@ -420,7 +714,7 @@ namespace alterNERDtive.util
{
dynamic defaultValue = ((dynamic)Defaults[id][name]).DefaultValue;
dynamic value = GetConfig(id, name);
- Log.Notice($"{id}.{name}# = {value}{(value == defaultValue ? " (default)" : "")}");
+ log.Notice($"{id}.{name}# = {value}{(value == defaultValue ? " (default)" : "")}");
}
public dynamic GetConfig(string id, string name)
{
@@ -462,27 +756,27 @@ namespace alterNERDtive.util
string variable = $"{id}.{name}#";
if (value is bool)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new bool[] { value } }); ;
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new bool[] { value } }); ;
}
else if (value is DateTime)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new DateTime[] { value } });
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new DateTime[] { value } });
}
else if (value is decimal)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new decimal[] { value } });
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new decimal[] { value } });
}
else if (value is int)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new int[] { value } });
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new int[] { value } });
}
else if (value is short)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new short[] { value } });
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}" }, new short[] { value } });
}
else if (value is string)
{
- Commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}", value } });
+ commands.Run("alterNERDtive-base.saveVariableToProfile", wait: true, parameters: new dynamic[] { new string[] { $"{variable}", value } });
}
else
{
@@ -499,7 +793,7 @@ namespace alterNERDtive.util
}
public void ListConfig(string id)
{
- Log.Notice($"===== {id} settings: =====");
+ log.Notice($"===== {id} settings: =====");
foreach (string name in Defaults[id].Keys)
{
ListConfig(id, name);
@@ -508,33 +802,178 @@ namespace alterNERDtive.util
public void ListConfig(string id, string name)
{
dynamic option = Defaults[id][name];
- Log.Notice($"“{option.VoiceTrigger}”: {option.Description}");
+ log.Notice($"“{option.VoiceTrigger}”: {option.Description}");
}
- }
- public class PythonProxy
- {
- public static Process SetupPythonScript(string path, string arguments)
+ ///
+ /// A plugin/profile option. Abstract base class for typed Options.
+ ///
+ public abstract class Option
{
- Process p = new Process();
- p.StartInfo.FileName = path;
- p.StartInfo.Arguments = arguments;
- p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- p.StartInfo.RedirectStandardOutput = true;
- p.StartInfo.RedirectStandardError = true;
- p.StartInfo.UseShellExecute = false;
- p.StartInfo.CreateNoWindow = true;
- return p;
- }
- }
+ ///
+ /// Gets the name of the Option.
+ ///
+ public abstract string Name { get; }
- public enum LogLevel
- {
- ERROR,
- WARN,
- NOTICE,
- INFO,
- DEBUG
+ ///
+ /// Gets the voice trigger phrase for the Option.
+ ///
+ public abstract string VoiceTrigger { get; }
+
+ ///
+ /// Gets the (optional) text to speech description for the Option.
+ /// Usually the voice trigger is used in confirmation text to
+ /// speech, this can be used to override it.
+ ///
+ public abstract string TtsDescription { get; }
+
+ ///
+ /// Gets the description of the Option.
+ ///
+ public abstract string Description { get; }
+
+ ///
+ /// Gets the type string for the option as used by VoiceAttack.
+ ///
+ public abstract string? TypeString { get; }
+ }
+
+ ///
+ /// A typed plugin/profile option.
+ ///
+ /// The data type of the option.
+ public class Option : Option
+ {
+ private readonly string? ttsDescription;
+ private readonly string? description;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the option.
+ /// The default value for the option.
+ /// The voice trigger for the option.
+ /// The (optional) list of valid values for the otpion.
+ /// The (optional) TTS description of the option.
+ /// The descrption of the option.
+ public Option(string name, T defaultValue, string voiceTrigger, List? validValues = null, string? ttsDescription = null, string? description = null)
+ => (this.Name, this.DefaultValue, this.VoiceTrigger, this.ValidValues, this.ttsDescription, this.description)
+ = (name, defaultValue, voiceTrigger, validValues, ttsDescription, description);
+
+ ///
+ public override string Name { get; }
+
+ ///
+ /// Gets the default value for the Option.
+ ///
+ public T DefaultValue { get; }
+
+ ///
+ /// Gets the (optional) list of valid values for the Option.
+ ///
+ public List? ValidValues { get; }
+
+ ///
+ public override string VoiceTrigger { get; }
+
+ ///
+ public override string TtsDescription { get => this.ttsDescription ?? this.VoiceTrigger; }
+
+ ///
+ public override string Description { get => this.description ?? "No description available."; }
+
+ ///
+ /// Gets the data type of the Option.
+ ///
+ public Type Type { get => typeof(T); }
+
+ ///
+ public override string? TypeString
+ {
+ get
+ {
+ string? type = null;
+ if (typeof(T) == typeof(bool))
+ {
+ type = "boolean";
+ }
+ else if (typeof(T) == typeof(DateTime))
+ {
+ type = "date";
+ }
+ else if (typeof(T) == typeof(decimal))
+ {
+ type = "decimal";
+ }
+ else if (typeof(T) == typeof(int))
+ {
+ type = "int";
+ }
+ else if (typeof(T) == typeof(short))
+ {
+ type = "smallint";
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ type = "text";
+ }
+ return type;
+ }
+ }
+
+ ///
+ /// Converts an to a of and .
+ ///
+ /// The Option to convert.
+ public static implicit operator (string, Option)(Option o) => (o.Name, o);
+
+ ///
+ /// Converts an to the contained default value.
+ ///
+ /// The option to convert.
+ public static explicit operator T(Option o) => o.DefaultValue;
+
+ ///
+ public override string ToString() => this.DefaultValue!.ToString();
+ }
+
+ ///
+ /// A Dictionary containing s. Used in
+ /// conjunction with ’s implicit conversion to a
+ /// tuple to make adding Options less painful.
+ ///
+ /// The key type.
+ /// The value type.
+ public class OptDict : Dictionary
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OptDict()
+ : base()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The initial capacity.
+ public OptDict(int capacity)
+ : base(capacity)
+ {
+ }
+
+ ///
+ /// Adds a to the list.
+ ///
+ /// The Tuple to be added.
+ public void Add((TKey, TValue) tuple)
+ {
+ this.Add(tuple.Item1, tuple.Item2);
+ }
+ }
}
public class VoiceAttackCommands
@@ -710,46 +1149,59 @@ namespace alterNERDtive.util
public void Debug(string message) => Log(message, LogLevel.DEBUG);
}
- public interface IPipable
+ public class PythonProxy
{
- public void ParseString(string serialization);
- public string ToString();
+ public static Process SetupPythonScript(string path, string arguments)
+ {
+ Process p = new Process();
+ p.StartInfo.FileName = path;
+ p.StartInfo.Arguments = arguments;
+ p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ p.StartInfo.RedirectStandardOutput = true;
+ p.StartInfo.RedirectStandardError = true;
+ p.StartInfo.UseShellExecute = false;
+ p.StartInfo.CreateNoWindow = true;
+ return p;
+ }
}
- public class PipeServer where Thing : IPipable, new()
+ public class PipeServer
+ where Thing : IPipable, new()
{
- private readonly string PipeName;
- private readonly SignalHandler Handler;
- private readonly VoiceAttackLog Log;
+ private readonly string pipeName;
+ private readonly SignalHandler handler;
+ private readonly VoiceAttackLog log;
- private bool Running = false;
+ private bool running = false;
- private NamedPipeServerStream? Server;
+ private NamedPipeServerStream? server;
public PipeServer(VoiceAttackLog log, string name, SignalHandler handler)
- => (Log, PipeName, Handler) = (log, name, handler);
+ => (this.log, this.pipeName, this.handler) = (log, name, handler);
public delegate void SignalHandler(Thing thing);
public PipeServer Run()
{
- Log.Debug($"Starting '{PipeName}' pipe …");
- if (!Running)
+ this.log.Debug($"Starting '{this.pipeName}' pipe …");
+ if (!this.running)
{
- Running = true;
- WaitForConnection();
+ this.running = true;
+ this.WaitForConnection();
}
+
return this;
}
public PipeServer Stop()
{
- Log.Debug($"Stopping '{PipeName}' pipe …");
- if (Running)
+ this.log.Debug($"Stopping '{this.pipeName}' pipe …");
+ if (this.running)
{
- Running = false;
- Server!.Close();
+ this.running = false;
+ this.server!.Close();
}
+
return this;
}
@@ -757,12 +1209,17 @@ namespace alterNERDtive.util
{
try
{
- Server = new NamedPipeServerStream(PipeName, PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
- Server.BeginWaitForConnection(OnConnect, Server);
+ this.server = new NamedPipeServerStream(
+ this.pipeName,
+ PipeDirection.In,
+ NamedPipeServerStream.MaxAllowedServerInstances,
+ PipeTransmissionMode.Message,
+ PipeOptions.Asynchronous);
+ this.server.BeginWaitForConnection(this.OnConnect, this.server);
}
catch (Exception e)
{
- Log.Error($"Error setting up pipe: {e.Message}");
+ this.log.Error($"Error setting up pipe: {e.Message}");
}
}
@@ -772,19 +1229,19 @@ namespace alterNERDtive.util
try
{
server.EndWaitForConnection(ar);
- WaitForConnection();
- using StreamReader reader = new StreamReader(server);
- Thing thing = new Thing();
+ this.WaitForConnection();
+ using StreamReader reader = new (server);
+ Thing thing = new ();
thing.ParseString(reader.ReadToEnd());
- Handler(thing);
+ this.handler(thing);
}
catch (ObjectDisposedException)
{
- Log.Debug($"'{PipeName}' pipe has been closed.");
+ this.log.Debug($"'{this.pipeName}' pipe has been closed.");
}
catch (Exception e)
{
- Log.Error($"Error reading pipe: {e.Message}");
+ this.log.Error($"Error reading pipe: {e.Message}");
}
}
}