Compare commits

...

109 commits

Author SHA1 Message Date
f034ae4220
4.5! 2022-09-19 18:08:43 +02:00
d97499778c
docs: clarified when StreamAttack updates information
fixes #155
2022-09-15 15:17:39 +02:00
d78cf17b6b
docs: added reloading the profile / restarting VA to installation instructions
fixes #154
2022-09-15 15:15:41 +02:00
0d44828422
docs: clarified that neutron router plugin is optional
fixes #153
2022-09-15 15:13:20 +02:00
022c3ac25c
fixed VERSION 2022-09-15 15:08:54 +02:00
fd0bbaa3a9
added StyleCop.ruleset to make that one shut up 2022-09-15 15:07:15 +02:00
cc2694fc49
RatAttack: support for new Horizons 3.8 / 4.0 / Odyssey RATSIGNALs
fixes #159
2022-09-15 15:06:49 +02:00
9db6785ef1
RatAttack: fixed RATSIGNAL regex
Apparently `^` can be part of both CMDR names and IRC nicks.
2022-09-10 03:43:19 +02:00
1135efd761
EliteAttack: fixed potential race condition in the discovery scan event command queue 2022-09-06 00:29:51 +02:00
7c361e9bf7
EliteAttack: added option to exclude settlements from the outdated stations list
fixes #119
2022-09-05 19:21:12 +02:00
54cd1e6fa2
EliteAttack: target nearest […] commands now log the result 2022-08-31 10:26:12 +02:00
3775d3f911
docs: added troubleshooting section about Geforce Now 2022-08-23 22:26:11 +02:00
alterNERDtive
1357bde807
Merge pull request #151 from alterNERDtive/feature/auto-pull-request 2022-07-13 22:26:07 +02:00
db85c2b793
auto-pull-request workflow: fixed branch name 2022-07-13 22:25:13 +02:00
4f9e4799bf
workflows: open pr on branch push 2022-07-13 22:23:45 +02:00
5af0aff95c
workflows: fixed release author 2022-07-13 21:15:35 +02:00
e2789623cd
cleaned up dependency mess 2022-07-12 00:40:55 +02:00
c615af5324
updated CHANGELOG 2022-07-11 19:52:39 +02:00
Ant
4e9a9b4517
Black out the grey censory block 2022-07-11 19:45:29 +02:00
Ant
38ea143ea7
SpanshAttack.md: fix column width 2022-07-11 19:45:25 +02:00
Ant
0ee7701590
EliteAttack: hyperlink path fix - DOH 2022-07-11 19:45:21 +02:00
Ant
05a56d5d85
index.md: correction to hyperlink markdown syntax 2022-07-11 19:45:05 +02:00
Ant
2ec9c2afbe
EliteAttack.md: correction to hyperlink markdown syntax 2022-07-11 19:44:49 +02:00
Ant
3cbfe62ae1
docs markdown fixes & author privacy on screenshot 2022-07-11 19:44:19 +02:00
dependabot[bot]
9cc9965d7c Bump Newtonsoft.Json from 6.0.4 to 13.0.1 in /plugins/VoiceAttack-base
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.4 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.4...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-08 17:52:13 +02:00
acf43b42a0 added .github as solution items 2022-07-08 17:52:09 +02:00
55fa3579cf fixed gh-pages workflow 2022-07-08 17:45:39 +02:00
d1af3ebdcc Merge branch 'release' into devel 2022-07-04 01:32:38 +02:00
b10e2bb2c4 added ko-fi / sponsors 2022-07-04 01:32:29 +02:00
91c47efbef fixed create-release workflow
fixes #148
2022-07-04 00:33:28 +02:00
1cdd9c98bd EliteAttack: fixed Docked event for Oddity settlements
fixes #145
2022-06-16 12:10:49 +02:00
35c26e930f mics fixes 2022-06-16 12:06:28 +02:00
bb8d1067b5 RatAttack: made case list thread safe
My specific setup leads to lots of incoming cases at once when starting VA after boot, when the IRC backlog is parsed.

Probably not relevant in the wild, but still a huge 🤦.
2022-06-03 16:45:12 +02:00
6ec8d7c35e stylecop compliance, round 3 of >3 2022-06-02 17:41:49 +02:00
55f10a1117 all plugins: fixed possible race condition introduced in 4.4 2022-06-02 15:28:17 +02:00
35057b3f35 dependabot 2022-06-01 09:12:01 +02:00
a2f6cc864c auto-deploy gh-pages 2022-06-01 09:11:57 +02:00
9beb6dfa41 Merge branch 'devel' into release 2022-05-31 19:31:28 +02:00
2dae2abc4c workflows: fixed(?) release glob 2022-05-31 19:29:08 +02:00
99305cfb5d 4.4! 2022-05-31 19:23:00 +02:00
bc5addd22a base: removed delays.keyPressDuration option since that isn’t actually used rn 2022-05-30 23:38:21 +02:00
64a096dae7 StyleCop compliance, round 2 of 3 … 2022-05-30 23:22:48 +02:00
33a4fb8e3d base: made config GUI buttons wider 2022-05-30 21:59:52 +02:00
3cbca1e542 base: config GUI now .Activate()s immediately 2022-05-30 21:58:46 +02:00
55a031686c auto draft a release on tag push 2022-05-30 21:28:09 +02:00
02a401a047 housekeeping 2022-05-30 21:25:08 +02:00
e36358be9f stylecop compliance, round 1 2022-05-29 22:16:17 +02:00
999d2b6883 alterNERDtive-base: configuration GUI now has an “Apply” button 2022-05-29 21:32:43 +02:00
e73b8c04f9 bump to c# 10 because why not 2022-05-29 21:14:54 +02:00
931ee4c4e7 RatAttack: added warning when running VA as Admin
fixes #138
2022-05-29 11:13:49 +02:00
751f80461f RatAttack-cli: added error message for running VA as Admin
see #138

Writing to other users’ pipes will cause an exception. You should not run VA elevated.

Needs a warning in VA too.
2022-05-29 10:51:51 +02:00
c9d39f6cc7 docs: typos 2022-05-28 15:09:00 +02:00
f8dd1ff464 docs: added upgrading EDDI event handlers to upgrading.md 2022-05-28 15:05:08 +02:00
f7328e31e2 bumped VERSION
I feel like I should do that whenever I bump it in base.cs …
2022-05-28 14:27:44 +02:00
2b0d2245e2 RatAttack: fixed superfluous “ly” output in distance to commands
fixes #140
2022-05-28 14:19:35 +02:00
dc42912cbe EliteAttack: fixed auto station services option
fixes #142
2022-05-28 14:19:08 +02:00
b3ad1ae799 switched for make to msbuild
Build file still private, since it relies on my local folder structure.
2022-05-27 20:20:41 +02:00
5421478d6e SpanshAttack: fixed getting current jump range from EDDI
Not waiting for the plugin context to finish running caused it to pretty consistently fail on the first attempt, and return the last value for attempts after that.
2022-05-22 12:49:06 +02:00
d771d0b403 docs: fixed weird character in general.md 2022-05-22 12:46:50 +02:00
b371523910 EliteAttack: added options from #133 i forgot
* auto retract landing gear
* auto disable SRV lights

fixes #133
2022-05-19 13:16:00 +02:00
8572d0ec4c CHANGELOG: typo 2022-05-19 12:54:22 +02:00
c00c1d9bbe Merge branch 'devel' into release 2022-05-19 12:49:30 +02:00
0c0cf068c4 4.3! 2022-05-19 12:46:31 +02:00
42faaa20d5 SpanshAttack: no longer pretends there’s a route if there isn’t
fixes #104
2022-05-19 12:40:38 +02:00
da9a6f6107 SpanshAttack: moved jumps announcement to end of jump instead of start
fixes #124
2022-05-19 12:19:47 +02:00
a5b22a8908 EliteAttack: workaround for bugged Body scanned EDDI events
fixes #121
2022-05-19 12:13:26 +02:00
20599fef88 EliteAttack: added logging to find nearest […] command
fixes #96
2022-05-19 12:08:00 +02:00
99438b4ed3 RatAttack: no longer sliently swallows calls if confirmation is disabled
fixes #128
2022-05-19 12:00:53 +02:00
368b7ca771 alterNERDtive-base: no longer cuts off EDDI’s “update available” TTS on startup
fixes #120
2022-05-19 11:58:47 +02:00
5442738a21 RatAttack: clarified call confirmation TTS
fixes #101

Also clarified that there _is_ call confirmation in the documentation.
2022-05-19 11:55:02 +02:00
d6cd7da11c docs: reordered setup instructions to clarify that the profile example already comes with a startup command
fixes #126
2022-05-19 11:48:44 +02:00
2f4015001f docs: clarified not having to install bundled dependencies manually
fixes #127
2022-05-19 11:44:08 +02:00
81b83bba2d docs: now mentions reloading the custom profile after initially adding its startup command
fixes #129
2022-05-19 11:41:48 +02:00
2544be701c RatAttack: added option for auto copying a client’s system on opening a case
Also updated the documentation to mention copying happens in the first place; fixes #130
2022-05-19 11:39:09 +02:00
9a8d7b646f CHANGELOG: forgot to add last fix 2022-05-19 11:33:05 +02:00
999650e84f docs: added HCS compatibility bit to troubleshooting.md
fixes #132
2022-05-19 11:31:23 +02:00
f69e3c03bd EliteAttack: make all docking actions optional
fixes #133
2022-05-19 11:18:52 +02:00
e81f3961ea alterNERDtive-base: fixed settings dialog closing prematurely 2022-05-18 13:55:35 +02:00
8a3b17d674 added rudimentary settings UI
Accessible by plugin context `config.dialog` or saying `customize settings`.

fixes #80
2022-05-18 13:50:14 +02:00
d038c58595 RatAttack: accounting for . in the lang part of a RATSIGNAL 2022-04-19 11:45:56 +02:00
08aaa3db8a RatAttack: fixed edge case with off duty + nearest CMDR
No longer announces the nearest CMDR if that option is turned off while
also being off duty alltogether. There was a missing condition there …
2022-04-18 13:36:30 +02:00
7cad49a6c2 general: added open documentation command 2022-04-18 12:48:31 +02:00
2997b8335c docs: added EDDI quiet mode info to requirements.md 2022-03-04 15:25:28 +01:00
5bf21a48ba docs: added tidbit about galaxy map vs. interface bindings conflict 2021-09-29 10:26:27 +02:00
28854968fd RatAttack: now gives feedback after asking for call confirmation 2021-09-18 14:26:14 +02:00
00923f3da5 EliteAttack: now throttling down on FSD engage, too 2021-09-18 14:23:42 +02:00
fa4e9535ba EliteAttack: added INFO logging for carrier events 2021-09-11 16:38:30 +02:00
302fd9f512 docs: mild un-doxxing 2021-08-26 15:05:39 +02:00
0e757eaf76 Merge branch 'devel' of https://github.com/alterNERDtive/VoiceAttack-profiles into devel 2021-08-26 14:59:34 +02:00
7bc58f19ef docs: fixed some typos/inconsistencies/mistakerinos 2021-08-26 14:51:11 +02:00
4836273007 RatAttack: allow , in lang strings 2021-07-19 19:45:16 +02:00
adcb01bead docs: added link to CHANGELOG on Github 2021-07-04 21:00:57 +02:00
61e7f00c1f RatAttack: fixed “Not Set” case number when asking for details about an invalid case 2021-07-04 03:58:02 +02:00
a57daf35a7 CHANGELOG: typo corrected 2021-06-23 09:45:02 +02:00
e12bb8a2ea docs: added disclaimer on Oddity development 2021-06-23 09:12:11 +02:00
f00fd22234 docs: update installation instructions 2021-06-23 09:06:17 +02:00
2715fa2a48 RatAttack: fixed RATSIGNAL parsing for system information containing /
e.g. “Herbig AE/BE star”
2021-06-18 11:37:05 +02:00
36abf8b53b sorted out the utf-8 crap 2021-06-14 11:50:15 +02:00
bcbde1dd47 mkdocs: mark external links 2021-06-14 11:24:41 +02:00
ec87397a92 .editorconfig: default to utf-8 2021-06-14 11:21:46 +02:00
4483945f6a SpanshAttack: OBOE on last jump
Introduced when I switched to announcing jumps on FSD engage. Or rather side effect of correcting the OBOE in the Spansh plugin + that change.
2021-06-14 09:12:17 +02:00
3261c11d70 docs: removed the 32/64 bit thing
VoiceAttack is now 64bit by default, if anyone insists on running the 32bit one they can sort it out for themselves :)
2021-06-14 09:10:24 +02:00
7c1c75ec98 RatAttack: turns out there can be & in a lang identifier 2021-06-01 20:51:20 +02:00
f6b5621bb2 docs: typo 2021-05-27 12:32:42 +02:00
a1ddb987e0 RatAttack: No longer trying to get nearest CMDR for the new “Unconfirmed” system info. 2021-05-26 17:17:15 +02:00
3c46b20a23 Merge branch 'devel' into release 2021-05-23 20:36:24 +02:00
b633d8791c 4.2.3! 2021-05-23 20:32:41 +02:00
a5a3740799 updated .editorconfig 2021-05-23 20:28:45 +02:00
1d6ee3953e updated .editorconfig 2021-05-23 20:26:54 +02:00
63 changed files with 3898 additions and 2092 deletions

View file

@ -1,5 +1,14 @@
[*] [*]
guidelines = 80 guidelines = 80
end_of_line = lf
insert_final_newline = true
charset = utf-8-bom
[*.cs] [*.cs]
guidelines = 80, 120 guidelines = 80, 120
# IDE0021: Use block body for constructors
csharp_style_expression_bodied_constructors = when_on_single_line
# IDE0024: Use block body for operators
csharp_style_expression_bodied_operators = when_on_single_line

2
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,2 @@
github: alterNERDtive
ko_fi: alterNERDtive

12
.github/dependabot.yaml vendored Normal file
View file

@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
target-branch: "develop"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
schedule:
interval: "daily"

View file

@ -0,0 +1,20 @@
name: Pull Request on Branch Push
on:
push:
branches-ignore:
- devel
- release
jobs:
auto-pull-request:
name: Open pull request
runs-on: ubuntu-latest
steps:
- name: pull-request-action
uses: vsoch/pull-request-action@1.0.19
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_BRANCH: "devel"
PULL_REQUEST_DRAFT: true
PASS_IF_EXISTS: true

23
.github/workflows/create-release.yaml vendored Normal file
View file

@ -0,0 +1,23 @@
name: Create release on tag push
on:
push:
tags:
- 'releases/*.*'
- 'releases/*.*.*'
jobs:
build:
name: Create draft release
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Draft release
uses: ncipollo/release-action@v1
with:
bodyFile: "CHANGELOG.md"
draft: true
token: ${{ secrets.RELEASE_TOKEN }}

22
.github/workflows/gh-pages.yaml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Deploy github pages on tag push
on:
push:
tags:
- 'release/*.*'
- 'release/*.*.*'
jobs:
build:
name: Deploy documentation
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@nomaterial
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REQUIREMENTS: requirements.txt

1
.gitignore vendored
View file

@ -358,3 +358,4 @@ MigrationBackup/
/Makefile /Makefile
/site /site
/src /src
/build.csproj

View file

@ -1,3 +1,188 @@
# 4.5 (2022-09-19)
This might very well be the last release of this. With how the “Horizons 4.0”
launch went, Frontiers communication around it, and just _how_ horrible 4.0 is,
I currently do not see me being motivated to actually port stuff to 4.0. And,
lets face it, 3.8 will go EoL eventually.
Anyway, I still have some programming pet projects around this stuff that I
might continue with and adapt this for, so I might sneak some fixes / updates
in, and Im not going to say never because who knows how Ill feel about this in
the future. Obviously theres also still the option to _pay_ me to do stuff =p
### Fixed
* Race condition in all plugins that might lead to commands using command-scoped
variables (`~<name>`) not working as intended. This was introduced in
refactoring work that was done for 4.4.
* Documentation proof read and fixed by @ACyprus. Thanks!
### Changed
* Some behind the scenes things regarding how builds work. This will make it
possible to build this entirely on Github (= less potential for human error)
once I have dealt with #62 (see #143 as well).
## EliteAttack 8.5
### Added
* `target nearest […]` commands now log the result (with log level “INFO”).
* `include outdated settlements` option: Include Odyssey settlements in the
outdated stations list. Default: true.
### Fixed
* `Docked` event now handles Odyssey settlements properly (they have no hangar).
(#145)
* Fixed potential race condition with the discovery scan event command queue.
Might have an impact on #64.
## RatAttack 6.4
### Fixed
* Support for new Horizons 3 / Horizons 4 / Odyssey RATSIGNALs. (#159)
* Made case list thread safe. Probably only ever impacted my own specific setup,
but still a huge 🤦.
* Apparently `^` can be part of both CMDR names and IRC nicks (fixed RATSIGNAL
regex).
-----
# 4.4 (2022-05-31)
### Added
* The Configuration GUI now has an “Apply” button.
### Fixed
* Configuration GUI now `.Activate()`s immediately to prevent it from hiding
behind other windows.
## EliteAttack 8.4
### Added
* `auto retract landing gear` setting: Automatically retract landing gear when
lifting off a planet / undocking from a station. Default: true. (#133)
* `auto disable s r v lights` setting: Automatically turn SRV lights off when
deploying one. Default: true. (#133)
### Fixed
* `auto enter station services` option. (#142)
## RatAttack 6.3.1
* Added error message to the CLI tool for running VoiceAttack with elevated
privileges which will cause an `UnAuthorizedAccessException` trying to
communicate with the plugin. (#138)
* Added warning to VoiceAttack when running it with elevated privileges. (#138)
## SpashAttack 7.2.2
* Fixed getting current jump range from EDDI; no longer fails on the first try,
no longer sometimes reports the last requested range instead of current.
-----
# 4.3 (2022-05-19)
**NOTE**: Further development is on hold and Odyssey compatibility will not be
worked on for the time being. See [the corresponding issue on
Github](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/113). Feel
free to file issues for anything that is broken on Odyssey and it will be worked
on when it is worked on.
That said, there is now a settings UI! Its not pretty, its basic, but it does
the job.
* Updated documentation for the switch to 64-bit as the standard VoiceAttack
distribution format.
* Updated included `bindED` plugin.
### Added
* `customize settings` command: Brings up a rudimentary settings UI. (#80)
* `open documentation` command: Opens the profiles documentatin in your
default browser.
* Section about HCS compatibility in “Troubleshooting” documentation (#130).
### Fixed
* Log level settings description no longer contains literal `\n`s.
* Documentation now mentions that you have to reload your custom profile after
creating the `startup` command for the first time. (#129)
* Clarified not having to install bundled dependencies manually. (#127)
* Reordered setup instructions to clarify that the profile example already comes
with a `startup` command. (#126)
* No longer cuts off EDDIs “update available” TTS on VoiceAttack start. (#120)
* Implemented workaround for EDDIs `Body scanned` event sometimes not setting
variables correctly. Might drop some events, will log to `DEBUG`. (#121)
## EliteAttack 8.3
### Added
* `INFO` logging for carrier events.
* `auto refuel` setting: Automatically refuel after docking at a station.
Default: true. (#133)
* `auto repair` setting: Automatically repair after docking at a station.
Default: true. (#133)
* `auto move to hangar` setting: Automatically move the ship to the hanger after
docking at a station. Default: true. (#133)
* `auto enter station services`: Automatically enter the Station Services menu
after docking at a station. Default: true. (#133)
* `find nearest […]` command now logs the result to the VoiceAttack log (log
level `INFO`). (#96)
## RatAttack 6.3
### Added
* Now gives feedback after asking for call confirmation: “Call aborted.” /
“Calling <…>.”.
* `auto copy rat case system` setting: Automatically copy the clients system to
the clipboard when you open a rat case. Default: true.
* Now asks for call confirmation more clearly: “send call \<call\> to fuel
rats?” instead of an easily misinterpreted “call \<call\>?”. The question
inflection is more or less pronounced depending on the TTS voice used and has
led to confusion. (#101)
### Fixed
* No longer trying to get nearest CMDR for the new “Unconfirmed” system info.
* Fixed RATSIGNAL parsing for locales containing `&`.
* Fixed RATSIGNAL parsing for system information containing `/` (e.g. “Herbig
AE/BE star”).
* “Not Set” case number when asking for details about an invalid case.
* Fixed RATSIGNAL parsing for locales containing `,`.
* No longer determines (nor logs) nearest CMDR if announcing nearest CMDR is
turned off while also being off duty.
* No longer silently swallows calls if call confirmation is disabled. (#128)
## SpanshAttack 7.2.1
### Fixed
* OBOE on last jump.
* Moved jumps left announcement from getting the next waypoint (now happens at
the start of a jump) to the `Jumped` event (happens after a jump). (#124)
* Will no longer go to neutron jump mode if no route has been found (= there is
only a single waypoint). (#104)
-----
# 4.2.3 (2021-05-23)
Updated included bindED plugin to version 4.1 that fixes Odyssey compatibility.
No actual code / profile changes.
-----
# 4.2.2 (2021-05-19) # 4.2.2 (2021-05-19)
Now comes with Odyssey-compatible bindED. Now comes with Odyssey-compatible bindED.
@ -131,7 +316,7 @@ also the default string used to detect them in AdiIRC. Youll need to listen t
* Now uses Mechas system information in the RATSIGNAL in case announcements. * Now uses Mechas system information in the RATSIGNAL in case announcements.
* `system information for fuel rat case` configuration option: System * `system information for fuel rat case` configuration option: System
information provided by Mecha. Default: true. information provided by Mecha. Default: true.
* `latest rat case details` command: : Will give you the case data for the * `latest rat case details` command: Will give you the case data for the
latest incoming case. latest incoming case.
### Changed ### Changed
@ -950,8 +1135,7 @@ up to date in EDDN.
* Added `open [rat;] dispatch board` command. Opens the web dispatch board in * Added `open [rat;] dispatch board` command. Opens the web dispatch board in
your default browser. your default browser.
* Added proper handling for multiple ratsignals hitting at once. Thats mainly * Added proper handling for multiple ratsignals hitting at once. Thats mainly
an IRC client config thing, an IRC client config thing, [see the docs](docs/configuration/RatAttack.md).
[see the docs](docs/RatAttack.md#getting-case-data-from-irc).
* Renamed `RatAttack.getInfoFromRatsignal` to * Renamed `RatAttack.getInfoFromRatsignal` to
`RatAttack.announceCaseFromRatsignal`. Removed the “open case?” voice input `RatAttack.announceCaseFromRatsignal`. Removed the “open case?” voice input
prompt. prompt.

25
Directory.build.props Normal file
View file

@ -0,0 +1,25 @@
<Project>
<PropertyGroup>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- StyleCop Analyzers configuration -->
<PropertyGroup>
<CodeAnalysisRuleSet>$(SolutionDir)StyleCop.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers.Error" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.CSharp.Async.Rules" Version="6.1.41">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<AdditionalFiles Include="$(SolutionDir)stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project>

View file

@ -1,10 +1,17 @@
# Elite Dangerous VoiceAttack Profiles # # Elite Dangerous VoiceAttack Profiles #
These are various profiles for [VoiceAttack](https://voiceattack.com) (VA) I use These are various profiles for [VoiceAttack](https://voiceattack.com) (VA) I use
to enhance my Elite experience. They give me important info, facilitate to enhance my Elite experience. They give me important info, facilitate
day-to-day gaming and do some special things for [Fuel day-to-day gaming and do some special things for [Fuel
Rats](https://fuelrats.com) and [Hull Seals](https://hullseals.space) work. Rats](https://fuelrats.com) and [Hull Seals](https://hullseals.space) work.
**NOTE**: Further development is on hold and Odyssey compatibility will not be
worked on for the time being. See [the corresponding issue on
Github](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/113). This
might or might not change after the Horizons/Odyssey merge when console release
is upon us. Feel free to file issues for anything that is broken on Odyssey and
it will be worked on when it is worked on.
## Available Profiles ## ## Available Profiles ##
* EliteAttack: The main Elite VA profile. Anything related to “just” playing the * EliteAttack: The main Elite VA profile. Anything related to “just” playing the
@ -30,3 +37,6 @@ issue](https://github.com/alterNERDtive/VoiceAttack-profiles/issues). Thanks! :)
You can also [say “Hi” on Discord](https://discord.gg/kXtXm54) if that is your You can also [say “Hi” on Discord](https://discord.gg/kXtXm54) if that is your
thing. thing.
[![GitHub Sponsors](https://img.shields.io/github/sponsors/alterNERDtive?style=for-the-badge)](https://github.com/sponsors/alterNERDtive)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/S6S1DLYBS)

2
StyleCop.ruleset Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="StyleCop.Analyzers rules with default action" Description="StyleCop.Analyzers with default action. Rules with IsEnabledByDefault = false are disabled." ToolsVersion="17.0" />

View file

@ -1 +1 @@
4.2.2 4.5

View file

@ -1,4 +1,4 @@
# EliteAttack # EliteAttack
This is my personal VoiceAttack profile for Elite: Dangerous. It started out This is my personal VoiceAttack profile for Elite: Dangerous. It started out
ages ago as a modification of [MalicVRs public ages ago as a modification of [MalicVRs public
@ -35,7 +35,7 @@ the edit window ready to send. They will _not_ hit Enter on their own.
## Navigation ## Navigation
There are so many navigation-focused commands now, they deserve there own There are so many navigation-focused commands now, they deserve their own
category. Basically anything that helps you plot anywhere. A lot of those are category. Basically anything that helps you plot anywhere. A lot of those are
powered by awesome EDDI so I dont have to do the work myself! powered by awesome EDDI so I dont have to do the work myself!
@ -72,10 +72,9 @@ Basically anything that is related to directly doing something with your ship.
* `[close;deploy;extend;open;retract;] [cargo scoop;hard points; landing gear] [up;down;]`: * `[close;deploy;extend;open;retract;] [cargo scoop;hard points; landing gear] [up;down;]`:
Overly complicated command to handle everything related to Cargo Scoop, Hard Overly complicated command to handle everything related to Cargo Scoop, Hard
Points, Landing Gear. You get the gist, I guess. Works in SRV too. Points, Landing Gear. You get the gist, I guess. Works in SRV too.
* `[dis;]engage silent running`: Handles silent running.
* `[disco;discovery scan]`: Executes a discovery scan. Expects the Discovery * `[disco;discovery scan]`: Executes a discovery scan. Expects the Discovery
Scanner in your first fire group, secondary fire. [You can change Scanner in your first fire group, secondary fire. [You can change
that](/configuration/EliteAttack/#settings). that](configuration/EliteAttack.md#settings).
* `[dis;]engage silent running`: Turns silent running on and off. * `[dis;]engage silent running`: Turns silent running on and off.
* `[head;spot;] lights [on;off]`: Turns your lights on and off. Works in SRV * `[head;spot;] lights [on;off]`: Turns your lights on and off. Works in SRV
too, kinda; turning lights off there relies on the state updating fast enough, too, kinda; turning lights off there relies on the state updating fast enough,
@ -96,7 +95,7 @@ Basically anything that is related to directly doing something with your ship.
given “when ready” will wait for mass lock to clear and your FSD to cool down given “when ready” will wait for mass lock to clear and your FSD to cool down
first. first.
## SRV controls ## SRV Controls
Things relevant to your SRV, but not your ship. Things relevant to your SRV, but not your ship.
@ -107,13 +106,12 @@ Things relevant to your SRV, but not your ship.
## Targeting ## Targeting
Well … targeting stuff, I guess. Not really sure why I made that its own Well … targeting stuff, I guess. Not really sure why I made that its own
category, but oh well :) category, but oh well :)
* `target the [drive;drives;power plant;frame shift drive;f s d;shield * `target the [drive;drives;power plant;frame shift drive;f s d;shield
generator]`:* generator]`: Targets the given submodule on your current target, or your next
Targets the given submodule on your current target, or your next target if you target if you dont have one currently. Does not persist between targets.
dont have one currently. Does not persist between targets.
* `clear sub [module;system] target`: Clears the current submodule target. * `clear sub [module;system] target`: Clears the current submodule target.
* `target next system`: Selects the next system on your route. * `target next system`: Selects the next system on your route.
* `target wing man [1;2;3]`: Targets your wingmen. * `target wing man [1;2;3]`: Targets your wingmen.
@ -167,6 +165,10 @@ The commands in here do random more or less useful things.
## Events ## Events
This is a list of ingame Events, [provided by
EDDI](https://github.com/EDCD/EDDI/wiki/VoiceAttack-Integration), that the
profile will act on.
### AFMU Repairs ### AFMU Repairs
Reports on the module that has been repaired, and if it has been fully or Reports on the module that has been repaired, and if it has been fully or
@ -226,8 +228,8 @@ scripts).
### Docked ### Docked
Automatically refuels, repairs, optionally rearms, then gets your ship into the Automatically refuels, repairs, rearms, then gets your ship into the hangar and
hangar and opens station services. opens station services. Can be individually disabled.
### Docking Denied ### Docking Denied
@ -268,7 +270,7 @@ the (rough) range you still have on the fumes left in your tank.
### Material Threshold ### Material Threshold
Warns you when a monitored material falls below its minimum stock level and Warns you when a monitored material falls below its minimum stock level and
tells you when you reach your desired level or fill up. tells you when you reach your desired level or fill up.
You will have to set minimum and desired amounts in EDDIs material monitor You will have to set minimum and desired amounts in EDDIs material monitor
@ -304,7 +306,7 @@ Reports on the synthesis type and quality.
### System Scan Complete ### System Scan Complete
Lists you all bodies EDDI considers worth mapping in the current system. Lists all bodies EDDI considers worth mapping in the current system.
### Undocked ### Undocked

View file

@ -1,4 +1,4 @@
# RatAttack # RatAttack
This profile facilitates [Fuel Ratting](https://www.fuelrats.com). It aims to This profile facilitates [Fuel Ratting](https://www.fuelrats.com). It aims to
eliminate as much of the required manual task and attention switching as eliminate as much of the required manual task and attention switching as
@ -50,6 +50,9 @@ are several commands you can run on this list, giving it a case number:
announcements to announcements to
VoiceAttack](../configuration/RatAttack/#getting-case-data-from-irc). VoiceAttack](../configuration/RatAttack/#getting-case-data-from-irc).
Opening a case will automatically copy the clients system to the clipboard for
easy route plotting. This can be disabled.
### Making Calls ### ### Making Calls ###
There are a bunch of calls you can make for a case, the most common are modelled There are a bunch of calls you can make for a case, the most common are modelled
@ -63,7 +66,7 @@ client or use the “General IRC Integration”, see below.
login to the game or have to take off from your current login to the game or have to take off from your current
station/port/outpost/planet. station/port/outpost/planet.
* `call jumps [left;]`: Calls jumps for the currently open case based on a * `call jumps [left;]`: Calls jumps for the currently open case based on a
neutron trip (requires Spanshattack) or a plotted ingame route. neutron trip (requires SpanshAttack) or a plotted ingame route.
* `call friend [positive;negative] [in pg;in private group;in solo;in main menu;sysconf;system confirmed;]`: * `call friend [positive;negative] [in pg;in private group;in solo;in main menu;sysconf;system confirmed;]`:
Friend request confirmations, with all the Friend request confirmations, with all the
things you might want to / should call with it. things you might want to / should call with it.
@ -78,6 +81,9 @@ client or use the “General IRC Integration”, see below.
calls, and make sure you will never have to call “client destroyed”, would calls, and make sure you will never have to call “client destroyed”, would
you? you?
By default, VoiceAttack will ask for confirmation before sending calls to the
`#fuelrats` channel.
### Closing a Case ### Closing a Case
* `[close;clear] rat case`: Closes the currently open rat case. * `[close;clear] rat case`: Closes the currently open rat case.

View file

@ -1,10 +1,10 @@
# SpanshAttack # SpanshAttack
This profile uses the This profile uses the
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) plugin to plot [ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) plugin to
neutron jumps using [spansh](https://spansh.co.uk/plotter). It fully does plot neutron jumps using [Spansh](https://spansh.co.uk/plotter). It does
everything you need from within the game and VoiceAttack, you wont have to everything you need fully from within the game and VoiceAttack, you wont have
visit the site at any point. to visit the site at any point.
## Plotting a Route ## Plotting a Route
@ -23,7 +23,7 @@ visit the site at any point.
### Plotting to a System Unknown to the Neutron Router ### Plotting to a System Unknown to the Neutron Router
The router can only plot a route to a system that is in its database (obviously The router can only plot a route to a system that is in its database (obviously
can also only give you way points that are). If your target system is not, there can also only give you waypoints that are). If your target system is not, there
are several levels of fallback handling to find a system that is. are several levels of fallback handling to find a system that is.
1. Check `Next system` coordinates provided by EDDI. If the system is in EDSM, 1. Check `Next system` coordinates provided by EDDI. If the system is in EDSM,
@ -55,7 +55,7 @@ Additionally, you can use the `SpanshAttack.copyNextNeutronWaypoint`
/ `[get;copy] next neutron [waypoint;way point]` command to copy the next / `[get;copy] next neutron [waypoint;way point]` command to copy the next
neutron waypoint to the clipboard. neutron waypoint to the clipboard.
### Skipping a waypoint ### Skipping a Waypoint
Sometimes, especially in very neutron-sparse areas of the galaxy, the plotter Sometimes, especially in very neutron-sparse areas of the galaxy, the plotter
will give you weird jumps. E.g. I recently got neutron → 37 ly → neutron → 440 will give you weird jumps. E.g. I recently got neutron → 37 ly → neutron → 440

View file

@ -1,4 +1,4 @@
# StreamAttack # StreamAttack
This profile uses the [EDDI](https://github.com/EDCD/EDDI) plugin to write This profile uses the [EDDI](https://github.com/EDCD/EDDI) plugin to write
a bunch of information about your commander, your current location and your ship a bunch of information about your commander, your current location and your ship
@ -23,6 +23,10 @@ Default folder is `%appdata%\StreamAttack\`.
### Elite ### Elite
Please do note that information in the output files is only updated when a
journal event that contains the information is detected. E.g. the distance to
your jump target is not constantly calculated, but only updated after a jump.
#### Commander #### Commander
* `Elite\cmdr\name`: The current commanders name. * `Elite\cmdr\name`: The current commanders name.
@ -45,4 +49,4 @@ Default folder is `%appdata%\StreamAttack\`.
* `Elite\ship\build`: Your current ships loadout (link to coriolis). * `Elite\ship\build`: Your current ships loadout (link to coriolis).
* `Elite\ship\full`: `“<name>” | <model> | <build>`. * `Elite\ship\full`: `“<name>” | <model> | <build>`.
* `Elite\ship\model`: Your current ships model. * `Elite\ship\model`: Your current ships model.
* `Elite\ship\name`: Your current ships name. * `Elite\ship\name`: Your current ships name.

View file

@ -1,4 +1,4 @@
# EliteAttack # EliteAttack
## Settings ## Settings
@ -8,8 +8,20 @@ Toggles:
without constraints. Default: false. without constraints. Default: false.
* `auto honk new systems`: Automatically honk upon entering a system if it is * `auto honk new systems`: Automatically honk upon entering a system if it is
your first visit. Default: true. your first visit. Default: true.
* `auto restock`: Automatically restock after docking at a station. You will * `auto refuel`: Automatically refuel after docking at a station. Default:
always refuel, repair and enter the Station Services menu. Default: true. true.
* `auto repair`: Automatically repair after docking at a station. Default:
true.
* `auto restock`: Automatically restock after docking at a station. Default:
true.
* `auto move to hangar`: Automatically move the ship to the hangar after docking
at a station. Default: true.
* `auto enter station services`: Automatically enter the Station Services menu
after docking at a station. Default: true.
* `auto retract landing gear`: Automatically retract landing gear when lifting
off a planet / undocking from a station. Default: true. (#133)
* `auto disable s r v lights`: Automatically turn SRV lights off when deploying
one. Default: true. (#133)
* `edsm system status`: Pull system data from EDSM and compare it * `edsm system status`: Pull system data from EDSM and compare it
against your discovery scan. Default: true. against your discovery scan. Default: true.
* `discovery scan on primary fire`: Use primary fire for honking instead of * `discovery scan on primary fire`: Use primary fire for honking instead of
@ -25,6 +37,8 @@ Toggles:
Ammonia Worlds that have not been mapped yet.) Default: true. Ammonia Worlds that have not been mapped yet.) Default: true.
* `outdated stations`: Announce stations with outdated data in the online * `outdated stations`: Announce stations with outdated data in the online
databases. Default: true. databases. Default: true.
* `include outdated settlements` option: Include Odyssey settlements in the
outdated stations list. Default: true.
* `repair reports`: Report on AFMU repairs. Default: true. * `repair reports`: Report on AFMU repairs. Default: true.
* `road to riches`: Announce bodies worth scanning if you are looking for some * `road to riches`: Announce bodies worth scanning if you are looking for some
starting cash on the Road to Riches. Default: false. starting cash on the Road to Riches. Default: false.

View file

@ -1,4 +1,4 @@
# RatAttack # RatAttack
## Getting Case Data From IRC ## Getting Case Data From IRC
@ -69,7 +69,7 @@ You can use this for a single CMDR, too. A less convoluted setup for announcing
the distance to your location in that case is on the list™ but does not have an the distance to your location in that case is on the list™ but does not have an
ETA yet. ETA yet.
Currently there is no way to specify a platform for each CMDR. Currently there is no way to specify a platform for each CMDR separately.
## Sending Text to FuelRats IRC ## Sending Text to FuelRats IRC
@ -88,6 +88,8 @@ Toggles:
* `auto close fuel rat case`: Automatically close a rat case when sending * `auto close fuel rat case`: Automatically close a rat case when sending
“fuel+” via voice command or ingame chat. Default: false. “fuel+” via voice command or ingame chat. Default: false.
* `auto copy rat case system` : Automatically copy the clients system to the
clipboard when you open a rat case. Default: true.
* `fuel rat call confirmation`: Only make calls in #fuelrats after vocal * `fuel rat call confirmation`: Only make calls in #fuelrats after vocal
confirmation to prevent mistakes. Default: true. confirmation to prevent mistakes. Default: true.
* `fuel rat duty`: On duty, receiving case announcements via TTS. Default: true. * `fuel rat duty`: On duty, receiving case announcements via TTS. Default: true.

View file

@ -13,7 +13,7 @@ For any ships that you regularly use for neutron jumping, e.g. long range Fuel
Rat ships, I recommend telling SpanshAttack about the range they are supposed to Rat ships, I recommend telling SpanshAttack about the range they are supposed to
have with full fuel and your preferred amount of cargo/limpets. have with full fuel and your preferred amount of cargo/limpets.
In oder to do that, copy the `SpanshAttack.getShipRange` command from In order to do that, copy the `SpanshAttack.getShipRange` command from
SpanshAttack or the example profile to your custom profile and add your ships. SpanshAttack or the example profile to your custom profile and add your ships.
Any ship listed in there will automatically have its jump range used instead of Any ship listed in there will automatically have its jump range used instead of
EDDIs reported laden range or VoiceAttack prompting you to manually supply it. EDDIs reported laden range or VoiceAttack prompting you to manually supply it.

View file

@ -1,4 +1,4 @@
# General Configuration # General Configuration
## Settings ## Settings
@ -7,12 +7,15 @@ configuration is stored in a bunch of VoiceAttack variables which in turn are
stored in your custom profile. You could even have different custom profiles stored in your custom profile. You could even have different custom profiles
with their own distinct settings. with their own distinct settings.
You change the configuration via voice commands: The easiest way to change settings is to say `customize settings`. That will
bring up a rudimentary settings UI.
You change also change the configuration directly via voice commands:
* For toggles (booleans): `customize setting [enable;disable] <trigger phrase>` * For toggles (booleans): `customize setting [enable;disable] <trigger phrase>`
* For everything else: `customize setting set <trigger phrase>` * For everything else: `customize setting set <trigger phrase>`
You can find a list of trigger phrases here, or you can say You can find a list of trigger phrases on this page, or you can say
`customize setting list [options;settings]` for a list of voice triggers and `customize setting list [options;settings]` for a list of voice triggers and
what they do. If you dont remember your settings or just want a nice list, what they do. If you dont remember your settings or just want a nice list,
`customize setting report [options;settings]` will print that to the `customize setting report [options;settings]` will print that to the
@ -22,9 +25,9 @@ VoiceAttack log. If you want to reset everything back to default state, say
The “customize setting” prefix is kind of a leftover from times long gone and The “customize setting” prefix is kind of a leftover from times long gone and
does not quite fit anymore. Might change in a future version. does not quite fit anymore. Might change in a future version.
Since the settings are saved to your custom profile they will not be preserved Since the settings are saved to your custom profile they will not necessarily be
when you switch profiles. Once you switch back, the correct settings for the preserved when you switch profiles. Once you switch back, the correct settings
profile are re-loaded. for the profile are re-loaded.
### General Settings for All Profiles ### General Settings for All Profiles
@ -48,9 +51,10 @@ Other settings:
## Note on Non-Standard Keyboard Layouts ## Note on Non-Standard Keyboard Layouts
Because Elites keyboard handling is … weird youll have to set the key to use Because Elites keyboard handling is … weird youll have to set the key to use
for pasting text into Elite:Dangerous if you are not using a “standard”QWERT[YZ] for pasting text into Elite Dangerous if you are not using a keyboard layout
layout. You will have to change it to the key that is physically in the place that the game supports by default. You will have to change it to the key that
where `V` would be on QWERTY, e.g. `P` for [Neo2](https://neo-layout.org). is physically in the place where `V` would be on QWERTY, e.g. `P` for
[Neo2](https://neo-layout.org).
To set the key, say “customize setting set elite paste key”. To set the key, say “customize setting set elite paste key”.
@ -70,4 +74,4 @@ appropriate handlers in my profiles are called, too. That is done by invoking
the `eddi.Event` plugin context of the `alterNERDtive-base` plugin. Otherwise the `eddi.Event` plugin context of the `alterNERDtive-base` plugin. Otherwise
stuff _will_ break. stuff _will_ break.
![[EDDI-event.png]] ![[EDDI-event.png]]

View file

@ -2,7 +2,7 @@
## Configuration ## Configuration
The base profile provides voice commands for changing the profiles’ settings. The base profile provides voice commands for changing the profiles settings.
See [the configuration section](../configuration/general#settings). See [the configuration section](../configuration/general#settings).
## Chat ## Chat
@ -10,7 +10,7 @@ See [the configuration section](../configuration/general#settings).
* `paste text`: Pastes the contents of your current clipboard. Note that this * `paste text`: Pastes the contents of your current clipboard. Note that this
command is supposed to be used for pasting _into Elite_ and hence uses the command is supposed to be used for pasting _into Elite_ and hence uses the
configured paste key. If youre using a non-standard layout that means that configured paste key. If youre using a non-standard layout that means that
you can _not_ use this command to paste text into other applications. you _cannot_ use this command to paste text into other applications.
## Updating ## Updating
@ -31,10 +31,11 @@ See [the configuration section](../configuration/general#settings).
and places it on your Desktop. Note that this currently uses bindEDs built-in and places it on your Desktop. Note that this currently uses bindEDs built-in
report which will output _any_ bind that does not have a keyboard key set, report which will output _any_ bind that does not have a keyboard key set,
including axis binds and binds that are not actually used by the profiles. including axis binds and binds that are not actually used by the profiles.
* `open documentation`: Opens the documentation in your default browser.
* `open EDDI options;configure EDDI`: Displays EDDIs configuration window. * `open EDDI options;configure EDDI`: Displays EDDIs configuration window.
* `open elite bindings folder`: Opens Elites bindings folder * `open elite bindings folder`: Opens Elites bindings folder
(`%localappdata%\Frontier Developments\Elite Dangerous\Options\Bindings`) (`%localappdata%\Frontier Developments\Elite Dangerous\Options\Bindings`)
* `reload elite key binds`: Forces a reload of your Elite binds. Should not be * `reload elite key binds`: Forces a reload of your Elite binds. Should not be
necessary. necessary.
* `shut up EDDI`: Immediately interrupts any current and pending speech on * `shut up EDDI`: Immediately interrupts any current and pending speech on
EDDIs end. EDDIs end.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -1,10 +1,17 @@
# Elite Dangerous VoiceAttack Profiles # Elite Dangerous VoiceAttack Profiles
These are various profiles for [VoiceAttack](https://voiceattack.com) (VA) I use These are various profiles for [VoiceAttack](https://voiceattack.com) (VA) I use
to enhance my Elite experience. They give me important info, facilitate to enhance my Elite experience. They give me important info, facilitate
day-to-day gaming and do some special things for [Fuel day-to-day gaming and do some special things for [Fuel
Rats](https://fuelrats.com) and [Hull Seals](https://hullseals.space) work. Rats](https://fuelrats.com) and [Hull Seals](https://hullseals.space) work.
**NOTE**: Further development is on hold and Odyssey compatibility will not be
worked on for the time being. See [the corresponding issue on
Github](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/113). This
might or might not change after the Horizons/Odyssey merge. Feel free to file
issues for anything that is broken on Odyssey and it will be worked on when it
is worked on.
## Available Profiles ## Available Profiles
* [EliteAttack](EliteAttack): The main Elite VA profile. Anything related to * [EliteAttack](EliteAttack): The main Elite VA profile. Anything related to
@ -24,10 +31,13 @@ Well, you are in the right place. You can find comprehensive documentation right
here. here.
If you run into any errors, please make sure you are running the latest version If you run into any errors, please make sure you are running the latest version
of the profiles and all requirements. of the profiles and all [requirements](requirements.md).
If your problem persists, please [file an If your problem persists, please [file an
issue](https://github.com/alterNERDtive/VoiceAttack-profiles/issues). Thanks! :) issue](https://github.com/alterNERDtive/VoiceAttack-profiles/issues). Thanks! :)
You can also [say “Hi” on Discord](https://discord.gg/kXtXm54) if that is your You can also [say “Hi” on Discord](https://discord.gg/kXtXm54) if that is your
thing. thing.
[![GitHub Sponsors](https://img.shields.io/github/sponsors/alterNERDtive?style=for-the-badge)](https://github.com/sponsors/alterNERDtive)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/S6S1DLYBS)

View file

@ -6,26 +6,24 @@ Grab the [most recent VoiceAttack beta
version](https://voiceattack.com/#download-1) off the official site and install version](https://voiceattack.com/#download-1) off the official site and install
it. it.
**Not on 32- vs. 64-bit:** If you are using any TTS voices that do not have a
64-bit version, choose 32-bit VoiceAttack. If you are planning on using
something that _requires_ 64-bit (e.g. a plugin interfacing with the latest MS
Flight Simulator), choose the 64-bit version. Otherwise it doesnt really
matter, and personally I default to 64-bit if available.
If you are using the standalone version you should probably download the If you are using the standalone version you should probably download the
executable installer. If you are using the Steam version of VoiceAttack, you executable installer.
will have to download the zipped folder and replace your installed version with
its contents.
![[VoiceAttack-download.png]] ![[VoiceAttack-download.png]]
If you are using the Steam version of VoiceAttack, you will have to download the
zipped folder and replace your installed version with its contents.
[Here is the first hit on a Google search for finding the installation [Here is the first hit on a Google search for finding the installation
folder](https://steamcommunity.com/sharedfiles/filedetails/?id=760447682). It is folder](https://steamcommunity.com/sharedfiles/filedetails/?id=760447682). It is
for a completely unrelated game, but the steps are the same. for a completely unrelated game, but the steps are the same.
You can potentially also install the latest non-beta version; but I often use You can potentially also install the latest non-beta version; but I often use
features that have only just been added to the beta, so some things might break features that have only just been added to the beta, so some things might break
for you if you are not using that. for you if you are not using that. There might not be a beta version at the time
youre reading this, just get the full release then.
### Enable Plugin Support
Make sure you have plugin support enabled: Make sure you have plugin support enabled:
@ -40,13 +38,14 @@ While you are there, you might also want to enable the automatic update checks
## Install EDDI ## Install EDDI
Install [the latest release](https://github.com/EDCD/EDDI/releases/latest) from Install [the latest release](https://github.com/EDCD/EDDI/releases/latest) from
Github (The `EDDI-X.Y.Z.exe` under “Assets”). You will need to [install it as a Github (The `EDDI-X.Y.Z.exe` file under “Assets”). You will need to [install it
VoiceAttack as a VoiceAttack
plugin](https://github.com/EDCD/EDDI/wiki/VoiceAttack-Integration#using-eddi-with-voiceattack). plugin](https://github.com/EDCD/EDDI/wiki/VoiceAttack-Integration#using-eddi-with-voiceattack).
If you do not want to install it into the VoiceAttack installation folder (or If you do not want to install it into the VoiceAttack installation folder (or
already have it installed somewhere else) you can also just create a symbolic already have it installed somewhere else) you can instead put it elsewhere and
link. Open a CMD prompt (Windows key + `R`, enter “cmd”, hit `Enter`) and do create a symbolic link. Open a command prompt (Windows key + `R`, enter “cmd”,
hit `Enter`) and do
```cmd ```cmd
>cd x:\path\to\VoiceAttack\Apps >cd x:\path\to\VoiceAttack\Apps
@ -54,18 +53,15 @@ link. Open a CMD prompt (Windows key + `R`, enter “cmd”, hit `Enter`) and do
``` ```
If you have installed the non-Steam version of VoiceAttack to the default folder If you have installed the non-Steam version of VoiceAttack to the default folder
within “ProgramFiles” you will have to run the CMD prompt as admin (Windows key within “Program Files” you will have to run the command prompt as admin (Windows
+ `R`, enter “cmd”, hit `Control` + `Shift` + `Enter`). key + `R`, enter “cmd”, hit `Control` + `Shift` + `Enter`).
## Install ED-NeutronRouter ## Install ED-NeutronRouter
**Make sure to [grab the pre-release Grab [the latest release release from
1.02](https://github.com/sc-pulgan/ED-NeutronRouter/releases/tag/1.02)** since Github](https://github.com/sc-pulgan/ED-NeutronRouter/releases/latest) (The
1.01 has a bug with a hardcoded 50ly jump range (the `EDNeutronRouter.vX.YZ.zip` under “Assets”). You will have to extract the
`EDNeutronRouter.vX.YZ.zip` under “Assets”). contents of the release .zip file to your VoiceAttack Apps folder:
You will have to extract the contents of the release .zip file to your
VoiceAttack Apps folder:
1. Go into VoiceAttack settings. ![[VoiceAttack-settings.png]] 1. Go into VoiceAttack settings. ![[VoiceAttack-settings.png]]
1. Click the folder set as “Apps Folder”. ![[VoiceAttack-apps.png]] 1. Click the folder set as “Apps Folder”. ![[VoiceAttack-apps.png]]
@ -84,6 +80,7 @@ Leave the “Apps” folder open in Windows Explorer, you will need it for the n
Acquire [the latest Acquire [the latest
release](https://github.com/alterNERDtive/VoiceAttack-profiles/releases/latest) release](https://github.com/alterNERDtive/VoiceAttack-profiles/releases/latest)
from Github (the `alterNERDtive-voiceattack-profiles.vax` file under “Assets”). from Github (the `alterNERDtive-voiceattack-profiles.vax` file under “Assets”).
Put it into VoiceAttacks “Import” folder:
1. Go into VoiceAttack settings. ![[VoiceAttack-settings.png]] 1. Go into VoiceAttack settings. ![[VoiceAttack-settings.png]]
1. Click the folder set as “Apps Folder”. ![[VoiceAttack-apps.png]] 1. Click the folder set as “Apps Folder”. ![[VoiceAttack-apps.png]]
@ -100,37 +97,17 @@ the profiles that you want to change and add voice triggers or hotkeys.
You can either use an existing profile, create a new one or use the provided You can either use an existing profile, create a new one or use the provided
profile example as a basis. Regardless of which way you choose, make sure to profile example as a basis. Regardless of which way you choose, make sure to
read the [[#Include Profiles]] section and follow the instructions there! read the [Include Profiles](#include-profiles) section and follow the instructions there!
### Create a New Profile ### Option 1: Create a New Profile Or Use an Existing One
1. Click the “Profile Actions” button, then “Create New Profile”. 1. Click the “Profile Actions” button, then “Create New Profile”.
![[VoiceAttack-new-profile.png]] ![[VoiceAttack-new-profile.png]]
1. Give it a name and add some commands if you want to. 1. Give it a name and add some commands if you want to.
1. Hit “Done” to create the new profile. 1. Hit “Done” to create the new profile.
### Use the Profile Example Alternatively you can keep using your existing profile. You will still have to
follow the rest of the instructions in this case.
1. Click the “Profile Actions” button, then “Import Profile”.
![[VoiceAttack-import-profile.png]]
1. Navigate to your VoiceAttack Apps folder (see above), go into the
“alterNERDtive” subfolder, choose the profile example and hit “Open”.
![[VoiceAttack-import-profile-open.png]]
Once you are done with the setup and configuration process, you can find a bunch
of example commands with comments on how to do things in this profile. Make sure
to also rename it to something more exciting than “Custom Profile Example”!
### Include Profiles
In order to use my profiles with your custom profile, you will need to take two
additional steps:
1. Include the profiles in your custom profile. That will make all commands
available when your custom profile is active.
1. Create a startup command for your custom profile. You can use it to do
anything you want when your profile loads, but it will also have to run the
startup command for my profiles.
#### Create a Startup Command #### Create a Startup Command
@ -144,7 +121,7 @@ will now have to create the startup command. Hit the “New Command” button.
![[VoiceAttack-edit-new-command.png]] ![[VoiceAttack-edit-new-command.png]]
You can name it anything you want but I recommend calling it “startup” or You can name it anything you want but I recommend calling it “startup” or
similar, and to deactivate the “when i say”checkbox in the command options to similar, and to deactivate the “when I say” checkbox in the command options to
make sure you do not accidentally run it via voice. make sure you do not accidentally run it via voice.
![[VoiceAttack-edit-startup.png]] ![[VoiceAttack-edit-startup.png]]
@ -168,6 +145,33 @@ You can add anything else you want your profile to do when it loads below this
action. You do not have to set any configuration options, this can be done way action. You do not have to set any configuration options, this can be done way
more elegantly! More on this [later on](general.md#settings). more elegantly! More on this [later on](general.md#settings).
After adding the startup command you will have to right click VoiceAttacks
title bar and choose “Reload Active Profile” or restart VoiceAttack to see it
executed for the first time.
### Option 2: Use the Profile Example
1. Click the “Profile Actions” button, then “Import Profile”.
![[VoiceAttack-import-profile.png]]
1. Navigate to your VoiceAttack Apps folder (see above), go into the
“alterNERDtive” subfolder, choose the profile example and hit “Open”.
![[VoiceAttack-import-profile-open.png]]
Once you are done with the setup and configuration process, you can find a bunch
of example commands with comments on how to do things in this profile. Make sure
to also rename it to something more exciting than “Custom Profile Example”!
## Include Profiles
In order to use my profiles with your custom profile, you will need to take two
additional steps:
1. Include the profiles in your custom profile. That will make all commands
available when your custom profile is active.
1. Create a startup command for your custom profile. You can use it to do
anything you want when your profile loads, but it will also have to run the
startup command for my profiles.
#### Set Profile Options #### Set Profile Options
While editing the profile, hit the “Options” button. While editing the profile, hit the “Options” button.
@ -179,23 +183,31 @@ button.
![[VoiceAttack-profile-options-include.png]] ![[VoiceAttack-profile-options-include.png]]
Add all my profiles (“alterNERDtive-base”, “EliteDangerous”, “RatAttack”, Add all my profiles (“alterNERDtive-base”, “EliteAttack”, “RatAttack”,
“SpanshAttack”,“StreamAttack”). “SpanshAttack”, “StreamAttack”).
![[VoiceAttack-profile-options-includelist.png]] ![[VoiceAttack-profile-options-includelist.png]]
Technically you can leave out anything you are not planning on using. Make sure that “alterNERDtive-base” is on top of the list, the order of the
Practically it probably will not hurt you to just include everything, and it others does not matter. But I like it nice and alphabetical. Technically you can
will then available for you in the future if you choose to check it out! Make leave out any profile you are not planning to use. Practically it probably will
sure that “alterNERDtive-base” is on top of the list, the order of the others not hurt you to just include everything, and it will then be available for you
does not matter. But I like it nice and alphabetical … in the future should you choose to check it out!
Now switch to the “Profile Exec” tab. Tick the “Execute a command each time this Now switch to the “Profile Exec” tab. Tick the “Execute a command each time this
profile is loaded” checkbox, and select the “startup” command you have created profile is loaded” checkbox, and select the “startup” command you have created
earlier. earlier.
![[VoiceAttack-profile-options-startup.png]] ![[VoiceAttack-profile-options-startup.png]]
## Reload the Profile
To make sure everything is loaded correctly, you now need to either reload the
profile by right clicking on VoiceAttacks title bar → “Reset Active Profile” or
by simply restarting VoiceAttack.
You should see a bunch of initialization messages pop up in the VoiceAttack log.
## Set Elite Keyboard Binds ## Set Elite Keyboard Binds
You need to have keyboard binds setup at least as secondary bindings in Elites You need to have keyboard binds setup at least as secondary bindings in Elites
@ -206,13 +218,21 @@ HOTAS. Or racing wheel. Or Rock Band set. Or bananas.
![[Elite-binds.png]] ![[Elite-binds.png]]
For the “Galaxy Map” section, make sure that the bindings do not conflict with
the ones in the “Interface Mode” section. The map bindings take precedence and a
conflict leads to VoiceAttack being unable to target systems for you. In that
case you would see it open the map for you and then wiggle the view for a split
second instead of switching tabs over to the system search.
![[Elite-binds-galmap.png]]
Should you use a keyboard layout that is _not_ en-US QWERTY, some keys might not Should you use a keyboard layout that is _not_ en-US QWERTY, some keys might not
work out of the box. You can try telling bindED about your keyboard layout by work out of the box. You can try telling bindED about your keyboard layout by
setting the `bindED.layout#` variable (including the \#!) to something more setting the `bindED.layout#` variable (including the `#`!) to something more
appropriate in your startup command. appropriate in your startup command.
![[bindED-layout.png]] ![[bindED-layout.png]]
Currently bindED supports the `en-US`, `en-GB` and `de-Neo2` layouts. [You can Currently bindED supports the `en-US`, `en-GB` and `de-Neo2` layouts. [You can
find instructions on how to add your own on find instructions on how to add your own
Github](https://github.com/alterNERDtive/bindED/wiki/Keyboard-Layouts). here](https://alternerdtive.github.io/bindED/troubleshooting/#adding-a-keyboard-layout).

View file

@ -1,60 +1,69 @@
# Requirements # Requirements
## VoiceAttack ## VoiceAttack
Obviously you will need to install [VoiceAttack](https://voiceattack.com). There Obviously you will need to install [VoiceAttack](https://voiceattack.com). There
is a free trial version available, but that one is limited to a single profile is a free trial version available, but that one is limited to a single profile
and a few commands. This is 4 profiles and … a lot of commands. You will need the and a few commands. This is 5 profiles and … a lot of commands. You will need the
full version, available for $10 (official site) or €11.99 (Steam, IIRC $14.99 full version, available for $10 (official site) or €11.99 (Steam, IIRC $14.99
for our US-based friends). for our US-based friends).
I recommend buying on the site. Why? Because on Steam, Valve gets a 30% cut. I recommend buying on the site. Why? Because on Steam, Valve gets a 30% cut.
Unlike many other developers Gary (the developer of VoiceAttack) remedies that Unlike many other developers Gary (the developer of VoiceAttack) remedies that
by having a price on Steam that ends up paying $10 to him. So basically, you are by having a price on Steam that ends up paying $10 to him. So basically, you are
paying Valve out of your own pocket. Many other developers do not do that, and paying Valve out of your own pocket. Many other developers do not do that, and
by buying from them directly instead of on Steam you are literally giving them by buying from them directly instead of on Steam you are literally giving them
extra money. Please do keep that in mind in the future! extra money. Please do keep that in mind in the future!
You also will generally need to opt into the beta version. I am usually at the You also will generally need to opt into the beta version. I am usually at the
forefront of bug reports and feature requests, and I do rely on the forefront of bug reports and feature requests, and I do rely on the
fixes/additions in beta versions quite often. fixes/additions in beta versions quite often.
## EDDI ## EDDI
[EDDI](https://github.com/EDCD/EDDI) is a companion application for Elite: [EDDI](https://github.com/EDCD/EDDI) is a companion application for Elite:
Dangerous, providing responses to events that occur in-game using data from the Dangerous, providing responses to events that occur in-game using data from the
game as well as various third-party tools. In this case, you will need to run it game as well as various third-party tools. In this case, you will need to run it
as a VoiceAttack plugin. as a VoiceAttack plugin.
EDDI also regularly publishes beta versions. Unless a profiles release EDDI also regularly publishes beta versions. Unless a profiles release
explicitly states it you will _not_ have to run EDDI beta. explicitly states it you will _not_ have to run EDDI beta.
## bindED Do note that the profiles put EDDI into quiet mode by default, disabling the
built-in speech responders. This can be changed
[bindED](https://github.com/alterNERDtive/bindED) reads your Elite Dangerous [via the `EDDI quiet mode` setting](configuration/general.md#general-settings-for-all-profiles).
binding files and makes them available to VoiceAttack as variables. That way
commands can be portable and you do not have to manually go through them and ## bindED
change any actions that you happen to not have the standard binds for.
[bindED](https://alterNERDtive.github.io/bindED) reads your Elite Dangerous
This plugin is _included_ in the release package. binding files and makes them available to VoiceAttack as variables. That way
commands can be portable and you do not have to manually go through them and
## Elite Scripts change any actions that you happen to not have the standard binds for.
I have written a [collection of Python This plugin is _included_ in the release package. You do _not_ have to download
scripts](https://github.com/alterNERDtive/elite-scripts) to interface with and install it manually, but you _can_ independently update it if a newer
various 3ʳᵈ party services like EDSM or Spansh. Those are called by the profiles version is available.
for various tasks, like checking a systems body count.
## Elite Scripts
In the future they will be replaced by VoiceAttack plugin code.
I have written a [collection of Python
The scripts are _included_ in the release package. scripts](https://github.com/alterNERDtive/elite-scripts) to interface with
various 3ʳᵈ party services like EDSM or Spansh. Those are called by the profiles
## ED-NeutronRouter for various tasks, like checking a systems body count.
(required for SpanshAttack) In the future they will be replaced by VoiceAttack plugin code.
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) interfaces The scripts are _included_ in the release package. You do _not_ have to download
with [Spanshs neutron plotter](https://spansh.uk/plotter) and makes the result and install them manually, but you _can_ independently update them if a newer
version is available.
## ED-NeutronRouter
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) interfaces
with [Spanshs neutron plotter](https://spansh.uk/plotter) and makes the result
available to VoiceAttack. available to VoiceAttack.
This will also eventually replaced by my own plugins. This plugin is _only_ required if you intend to use the SpanshAttack profile for
neutron routing. Otherwise you do not have to install it.
This will also eventually be replaced by my own plugins.

View file

@ -1,56 +1,100 @@
# Troubleshooting # Troubleshooting
This will fill up gradually with Troubleshooting tips as people run into common This will fill up gradually with Troubleshooting tips as people run into common
ones. ones.
## VoiceAttack does not understand me / mishears me / fires random commands ## VoiceAttack does not understand me / mishears me / fires random commands
There is [a thread on the VoiceAttack There is [a thread on the VoiceAttack
forums](https://forum.voiceattack.com/smf/index.php?topic=2667.msg12197#msg12197) forums](https://forum.voiceattack.com/smf/index.php?topic=2667.msg12197#msg12197)
on how to set up your microphone and the speech recognition engine to work best. on how to set up your microphone and the speech recognition engine to work best.
If your microphone is bad and you still get erroneous recognitions when you are If your microphone is bad and you still get erroneous recognitions when you are
not speaking it is probably going to recognize the same command every time. You not speaking it is probably going to recognize the same command every time. You
can remedy that by blocking the voice trigger. One-syllable triggers are can remedy that by blocking the voice trigger. One-syllable triggers are
especially prone to misrecognition. especially prone to misrecognition.
1. Create a new command in your custom profile. 1. Create a new command in your custom profile.
1. Set the “when I say” field to the trigger that gets misrecognized. 1. Set the “when I say” field to the trigger that gets misrecognized.
Adding the “Other” → “VoiceAttack Action” → “Ignore an Unrecognized Word or Adding the “Other” → “VoiceAttack Action” → “Ignore an Unrecognized Word or
Phrase” action will also hide it from the VoiceAttack log when it is (wrongly) Phrase” action will also hide it from the VoiceAttack log when it is (wrongly)
recognized. You might or might not want that. recognized. You might or might not want that.
Example for the “cruise” voice trigger of the Supercruise command: Example for the “cruise” voice trigger of the Supercruise command:
![[troubleshooting-remove-trigger.png]] ![[troubleshooting-remove-trigger.png]]
Alternatively you can raise the minimum confidence level and call the underlying Alternatively you can raise the minimum confidence level and call the underlying
command to make misfires less likely: command to make misfires less likely:
![[troubleshooting-raise-min-confidence.png]] ![[troubleshooting-raise-min-confidence.png]]
There are a few examples in the [Custom Profile There are a few examples in the [Custom Profile
Example](../installing#use-the-profile-example). Example](../installing#use-the-profile-example).
## VoiceAttack recognizes a command, but doesnt do anything in game ## VoiceAttack recognizes a command, but doesnt do anything in game
Make sure you have a keyboard bind for whatever the command is supposed to do as Make sure you have a keyboard bind for whatever the command is supposed to do as
outlined in [[Installing#Set Elite Keyboard Binds]]. outlined in [[Installing#Set Elite Keyboard Binds]].
## VoiceAttack talks over the COVAS voice ## VoiceAttack talks over the COVAS voice
There is no way to know for sure when the ingame COVAS is talking to you, so There is no way to know for sure when the ingame COVAS is talking to you, so
there is no way to always prevent the two from speaking over each other. there is no way to always prevent the two from speaking over each other.
You can however either disable TTS responses for events that you know will clash You can however either disable TTS responses for events that you know will clash
(or [file a feature (or [file a feature
request](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/) if one request](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/) if one
is not optional yet). Alternatively, if you prefer the info given by VoiceAttack is not optional yet). Alternatively, if you prefer the info given by VoiceAttack
over the ingame COVAS, you can deactivate its response to these events in the over the ingame COVAS, you can deactivate its response to these events in the
ingame Audo settings: ingame Audo settings:
![[Elite-COVAS.png]] ![[Elite-COVAS.png]]
While youre in there you might as well get rid of the spoken FSD countdown that While youre in there you might as well get rid of the spoken FSD countdown that
is off by one second … is off by one second …
## This doesnt work (well) with my HCS pack
My profiles are designed from the ground up to work with whatever else you are
doing with VoiceAttack; that is the reason for importing them into your own
custom profile instead of selecting e.g. `EliteAttack` as your active profile.
HCS on the contrary explicitly expects you do exclusively use HCS with
VoiceAttack. There is the rudimentary way of including simple profiles into
theirs, but the mechanism falls flat in many places. For example you cannot tell
HCS to run an included profiles startup command.
So, in order to mostly make stuff work, you need to treat the HCS profile as
your “custom” profile as per this documentation.
1. Include `alterNERDtive-base` and all profiles you want to use into the active
HCS profile.
1. Include a custom profile that has a startup command with a voice trigger of
your choosing, e.g. “load included profiles”.
1. Set up said startup command as you would normally.
1. Every time you start VA or change profiles, you will have to manually say
“load included profiles”.
That will make most things work. Conflicts may arise if HCS happens to have
voice triggers that are the same as mine, in which case their command will take
priority.
**Note on TTS**: EDDIs TTS (used by my profiles) and HCS TTS / recorded voice
lines act 100% independently. That means they will frequently “speak over each
other”. There is no way to alleviate this.
EDDI does have a mechanism to detect if it is currently speaking it sets a
corresponding VoiceAttack variable. HCS neither does anything similar nor checks
if EDDI is speaking to prevent conflicts. Refer to them if you want that
changed.
## This does not work with Geforce Now
Nope. Just wont. Geforce Now obviously has Elites files, journals and keybinds
stored on some random PC in the cloud. Your local VoiceAttack has no way of
accessing those.
If you can get VoiceAttack to run _on Geforce Now_ in parallel to Elite, I dont
see why it wouldnt work.

View file

@ -1,10 +1,10 @@
# Upgrading # Upgrading
To upgrade to the latest version, follow these simple steps: To upgrade to the latest version, follow these simple steps:
1. Say “download profiles update”. 1. Say “download profiles update”.
1. Put the `alterNERDtive-voiceattack-profiles.vax` file from Github into the 1. Put the `alterNERDtive-voiceattack-profiles.vax` file from Github into
“Import” folder. VoiceAttacks “Import” folder.
1. Restart VoiceAttack. 1. Restart VoiceAttack.
In general, migration from old versions will be handled automatically. If there In general, migration from old versions will be handled automatically. If there
@ -29,6 +29,13 @@ Please do not fiddle with the configuration variables from your startup command
entirely unnecessary since configuration will be saved to and loaded from the entirely unnecessary since configuration will be saved to and loaded from the
profile anyway. profile anyway.
### EDDI Events
The process for adding your own handlers for EDDI events has changed. You no
longer have to check which of my profiles handle them and add the commands for
those manually; instead you need to [run the `eddi.event` context of the
`alterNERDtive-base` plugin](../configuration/general#eddi-events).
### bindED ### bindED
If you have done anything non-standard with bindED before, it might break. The If you have done anything non-standard with bindED before, it might break. The
@ -70,7 +77,7 @@ list.
For my AdiIRC, it looks like this (obviously change the path, please): For my AdiIRC, it looks like this (obviously change the path, please):
```adiirc ```adiirc
on *:TEXT:RATSIGNAL - CMDR*(??_SIGNAL):#fuelrats:{ on *:TEXT:*RATSIGNAL*(??_SIGNAL):#fuelrats:{
if ( $away ) { if ( $away ) {
/run -h "X:\path\to\VoiceAttack\Apps\alterNERDtive\RatAttack-cli.exe" " $+ $replace($1-,","") $+ " false /run -h "X:\path\to\VoiceAttack\Apps\alterNERDtive\RatAttack-cli.exe" " $+ $replace($1-,","") $+ " false
} }
@ -83,8 +90,8 @@ on *:TEXT:RATSIGNAL - CMDR*(??_SIGNAL):#fuelrats:{
If I am away it will just add the new case to the list. If I am not away, it If I am away it will just add the new case to the list. If I am not away, it
will announce it using TTS. will announce it using TTS.
The “replace” part handles the fact that announcements now put the system in The `$replace` part handles the fact that case announcements now put the system
quotes. They have to be escaped as double quotes (`""`) to create a correct in quotes. They have to be escaped as double quotes (`""`) to create a correct
command line invocation. command line invocation.
#### Nearest CMDR Announcements #### Nearest CMDR Announcements

View file

@ -1,10 +1,10 @@
site_name: "alterNERDtive VA profiles" site_name: "alterNERDtive VA profiles"
site_url: https://alterNERDtive.github.io/VoiceAttack-profiles site_url: https://alterNERDtive.github.io/VoiceAttack-profiles
repo_url: https://github.com/alterNERDtive/VoiceAttack-profiles repo_url: https://github.com/alterNERDtive/VoiceAttack-profiles
edit_uri: "edit/devel/docs/" edit_uri: "edit/devel/docs/"
site_description: "alterNERDtive VoiceAttack profiles for Elite: Dangerous" site_description: "alterNERDtive VoiceAttack profiles for Elite: Dangerous"
site_author: "alterNERDtive" site_author: "alterNERDtive"
remote_name: "ssh-origin" remote_name: "origin"
theme: theme:
name: readthedocs name: readthedocs
@ -43,7 +43,8 @@ nav:
- 'Issues': - 'Issues':
#- 'VoiceAttack Tips': 'VoiceAttack.md' #- 'VoiceAttack Tips': 'VoiceAttack.md'
- 'troubleshooting.md' - 'troubleshooting.md'
- 'Report a Bug': 'https://github.com/alterNERDtive/VoiceAttack-profiles/issues/' - '⎋ Changelog': 'https://github.com/alterNERDtive/VoiceAttack-profiles/blob/release/CHANGELOG.md'
- '⎋ Report a Bug': 'https://github.com/alterNERDtive/VoiceAttack-profiles/issues/'
- 'Watch in Action': - 'Watch in Action':
- 'Twitch': 'https://twitch.tv/alterNERDtive' - 'Twitch': 'https://twitch.tv/alterNERDtive'
- 'Youtube': 'https://www.youtube.com/channel/UC3XNZA7xWed1zM1AWOOdmog' - 'Youtube': 'https://www.youtube.com/channel/UC3XNZA7xWed1zM1AWOOdmog'

View file

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.30413.136 VisualStudioVersion = 17.3.32519.111
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceAttack-base", "plugins\VoiceAttack-base\VoiceAttack-base.csproj", "{1C05DB3F-3449-4664-B363-A379892995E5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceAttack-base", "plugins\VoiceAttack-base\VoiceAttack-base.csproj", "{1C05DB3F-3449-4664-B363-A379892995E5}"
EndProject EndProject
@ -16,6 +16,12 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2B4D94B-8D73-431A-880B-B1E7ADF064B2}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2B4D94B-8D73-431A-880B-B1E7ADF064B2}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
CHANGELOG.md = CHANGELOG.md
.github\workflows\create-release.yaml = .github\workflows\create-release.yaml
Directory.build.props = Directory.build.props
mkdocs.yml = mkdocs.yml
README.md = README.md
stylecop.json = stylecop.json
VERSION = VERSION VERSION = VERSION
EndProjectSection EndProjectSection
EndProject EndProject
@ -43,6 +49,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "configuration", "configurat
docs\configuration\StreamAttack.md = docs\configuration\StreamAttack.md docs\configuration\StreamAttack.md = docs\configuration\StreamAttack.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A68BA76B-47FA-4D25-805E-66EBDD8C5223}"
ProjectSection(SolutionItems) = preProject
.github\dependabot.yaml = .github\dependabot.yaml
.github\FUNDING.yml = .github\FUNDING.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{0147AF7E-BB7F-4D5F-96EC-8734393DFF56}"
ProjectSection(SolutionItems) = preProject
.github\workflows\create-release.yaml = .github\workflows\create-release.yaml
.github\workflows\gh-pages.yaml = .github\workflows\gh-pages.yaml
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -76,6 +94,8 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{5401ADF7-CB6D-448B-A4AC-D8A17B2D841F} = {C2B4D94B-8D73-431A-880B-B1E7ADF064B2} {5401ADF7-CB6D-448B-A4AC-D8A17B2D841F} = {C2B4D94B-8D73-431A-880B-B1E7ADF064B2}
{1AFD9AE6-7D22-4EF4-B0DE-51C9E91370FB} = {5401ADF7-CB6D-448B-A4AC-D8A17B2D841F} {1AFD9AE6-7D22-4EF4-B0DE-51C9E91370FB} = {5401ADF7-CB6D-448B-A4AC-D8A17B2D841F}
{A68BA76B-47FA-4D25-805E-66EBDD8C5223} = {C2B4D94B-8D73-431A-880B-B1E7ADF064B2}
{0147AF7E-BB7F-4D5F-96EC-8734393DFF56} = {A68BA76B-47FA-4D25-805E-66EBDD8C5223}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {339E6747-C7BF-43C3-99C6-9249C9849A84} SolutionGuid = {339E6747-C7BF-43C3-99C6-9249C9849A84}

View file

@ -1,113 +1,175 @@
#nullable enable // <copyright file="EliteAttack.cs" company="alterNERDtive">
// Copyright 20192022 alterNERDtive.
using alterNERDtive.util; //
using System; // This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
using System.Collections.Generic; //
using System.Linq; // alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
using System.Text; // it under the terms of the GNU General Public License as published by
using System.Threading.Tasks; // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
namespace EliteAttack //
{ // alterNERDtive VoiceAttack profiles for Elite Dangerous is distributed in the hope that it will be useful,
public class EliteAttack { // but WITHOUT ANY WARRANTY; without even the implied warranty of
private static dynamic? VA { get; set; } // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
private static VoiceAttackLog Log //
=> log ??= new VoiceAttackLog(VA, "EliteAttack"); // You should have received a copy of the GNU General Public License
private static VoiceAttackLog? log; // along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
private static VoiceAttackCommands Commands
=> commands ??= new VoiceAttackCommands(VA, Log); #nullable enable
private static VoiceAttackCommands? commands;
using System;
/*================\
| plugin contexts | using alterNERDtive.util;
\================*/
namespace EliteAttack
private static void Context_Log(dynamic vaProxy) {
{ /// <summary>
string message = vaProxy.GetText("~message"); /// VoiceAttack plugin for the EliteAttack profile.
string level = vaProxy.GetText("~level"); /// </summary>
public class EliteAttack
if (level == null) {
{ private static readonly Version VERSION = new ("8.5");
Log.Log(message);
} private static VoiceAttackLog? log;
else private static VoiceAttackCommands? commands;
{
try private static dynamic? VA { get; set; }
{
Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper())); private static VoiceAttackLog Log => log ??= new (VA, "EliteAttack");
}
catch (ArgumentNullException) { throw; } private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
catch (ArgumentException)
{ /*========================================\
Log.Error($"Invalid log level '{level}'."); | required VoiceAttack plugin shenanigans |
} \========================================*/
}
} /// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API.
private static void Context_Startup(dynamic vaProxy) /// </summary>
{ /// <returns>The GUID.</returns>
Log.Notice("Starting up …"); public static Guid VA_Id()
VA = vaProxy; => new ("{5B46321D-2935-4550-BEEA-36C2145547B8}");
Log.Notice("Finished startup.");
} /// <summary>
/// The plugins display name, as required by the VoiceAttack plugin API.
/*========================================\ /// </summary>
| required VoiceAttack plugin shenanigans | /// <returns>The display name.</returns>
\========================================*/ public static string VA_DisplayName()
=> $"EliteAttack {VERSION}";
static readonly Version VERSION = new Version("8.2.1");
/// <summary>
public static Guid VA_Id() /// The plugins description, as required by the VoiceAttack plugin API.
=> new Guid("{5B46321D-2935-4550-BEEA-36C2145547B8}"); /// </summary>
public static string VA_DisplayName() /// <returns>The description.</returns>
=> $"EliteAttack {VERSION}"; public static string VA_DisplayInfo()
public static string VA_DisplayInfo() => "EliteAttack: a plugin for doing Elite-y things.";
=> "EliteAttack: a plugin for doing Elite-y things.";
/// <summary>
public static void VA_Init1(dynamic vaProxy) /// The Init method, as required by the VoiceAttack plugin API.
{ /// Runs when the plugin is initially loaded.
VA = vaProxy; /// </summary>
Log.Notice("Initializing …"); /// <param name="vaProxy">The VoiceAttack proxy object.</param>
VA.SetText("EliteAttack.version", VERSION.ToString()); public static void VA_Init1(dynamic vaProxy)
Log.Notice("Init successful."); {
} VA = vaProxy;
Log.Notice("Initializing …");
public static void VA_Invoke1(dynamic vaProxy) VA.SetText("EliteAttack.version", VERSION.ToString());
{ Log.Notice("Init successful.");
string context = vaProxy.Context.ToLower(); }
Log.Debug($"Running context '{context}' …");
try /// <summary>
{ /// The Invoke method, as required by the VoiceAttack plugin API.
switch (context) /// Runs whenever a plugin context is invoked.
{ /// </summary>
case "startup": /// <param name="vaProxy">The VoiceAttack proxy object.</param>
Context_Startup(vaProxy); public static void VA_Invoke1(dynamic vaProxy)
break; {
// log string context = vaProxy.Context.ToLower();
case "log.log": Log.Debug($"Running context '{context}' …");
Context_Log(vaProxy); try
break; {
// invalid switch (context)
default: {
Log.Error($"Invalid plugin context '{vaProxy.Context}'."); case "startup":
break; Context_Startup(vaProxy);
} break;
} case "log.log":
catch (ArgumentNullException e) // log
{ Context_Log(vaProxy);
Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'"); break;
} default:
catch (Exception e) // invalid
{ Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})"); break;
} }
} }
catch (ArgumentNullException e)
public static void VA_Exit1(dynamic vaProxy) { } {
Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
public static void VA_StopCommand() { } }
} catch (Exception e)
} {
Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})");
}
}
/// <summary>
/// The Exit method, as required by the VoiceAttack plugin API.
/// Runs when VoiceAttack is shut down.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "required by VoiceAttack plugin API")]
public static void VA_Exit1(dynamic vaProxy)
{
}
/// <summary>
/// The StopCommand method, as required by the VoiceAttack plugin API.
/// Runs whenever all commands are stopped using the “Stop All Commands”
/// button or action.
/// </summary>
public static void VA_StopCommand()
{
}
/*================\
| plugin contexts |
\================*/
#pragma warning disable IDE0060 // Remove unused parameter
private static void Context_Log(dynamic vaProxy)
{
string message = vaProxy.GetText("~message");
string level = vaProxy.GetText("~level");
if (level == null)
{
Log.Log(message);
}
else
{
try
{
Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper()));
}
catch (ArgumentNullException)
{
throw;
}
catch (ArgumentException)
{
Log.Error($"Invalid log level '{level}'.");
}
}
}
private static void Context_Startup(dynamic vaProxy)
{
Log.Notice("Starting up …");
Log.Notice("Finished startup.");
}
#pragma warning restore IDE0060 // Remove unused parameter
}
}

View file

@ -21,7 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\EliteAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -30,7 +30,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\EliteAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -1,4 +1,23 @@
using System.Reflection; // <copyright file="AssemblyInfo.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View file

@ -1,4 +1,23 @@
using System.Reflection; // <copyright file="AssemblyInfo.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View file

@ -1,39 +0,0 @@
#nullable enable
using System;
using System.IO;
using System.IO.Pipes;
using System.Text.RegularExpressions;
namespace RatAttack
{
class RatAttack_cli
{
static string stripIrcCodes(string message)
{
return Regex.Replace(message, @"[\x02\x11\x0F\x1D\x1E\x1F\x16]|\x03(\d\d?(,\d\d?)?)?", String.Empty);
}
static void Main(string[] args)
{
RatAttack.Ratsignal ratsignal = new RatAttack.Ratsignal(stripIrcCodes(args[0]), args.Length > 1 && args[1].ToLower() == "true");
using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "RatAttack", PipeDirection.Out))
{
try
{
// try connecting for up to 2minutes; then well assume VoiceAttack just isnt up and wont come up
pipeClient.Connect(120000);
using (StreamWriter writer = new StreamWriter(pipeClient))
{
writer.WriteLine(ratsignal);
}
}
catch (TimeoutException)
{
Console.Error.WriteLine("Connection to RatAttack pipe has timed out.");
}
}
}
}
}

View file

@ -25,7 +25,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\RatAttack-cli.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -35,7 +35,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\RatAttack-cli.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
@ -50,8 +50,8 @@
<Reference Include="System.Xml.Serialization" /> <Reference Include="System.Xml.Serialization" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="RatAttack-cli.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RatAttack_cli.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\RatAttack\RatAttack.csproj"> <ProjectReference Include="..\RatAttack\RatAttack.csproj">

View file

@ -0,0 +1,68 @@
// <copyright file="RatAttack_cli.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
#nullable enable
using System;
using System.IO;
using System.IO.Pipes;
using System.Text.RegularExpressions;
namespace RatAttack
{
/// <summary>
/// CLI helper tool for the RatAttack VoiceAttack plugin. Accepts RATSIGNALs
/// e.g. from an IRC client and passes them to the plugin via named pipe.
/// </summary>
public class RatAttack_cli
{
/// <summary>
/// Main entry point.
/// </summary>
/// <param name="args">The command line arguments.</param>
public static void Main(string[] args)
{
RatAttack.Ratsignal ratsignal = new (StripIrcCodes(args[0]), args.Length > 1 && args[1].ToLower() == "true");
using (NamedPipeClientStream pipeClient = new (".", "RatAttack", PipeDirection.Out))
{
try
{
// try connecting for up to 2minutes; then well assume VoiceAttack just isnt up and wont come up
pipeClient.Connect(120000);
using StreamWriter writer = new (pipeClient);
writer.WriteLine(ratsignal);
}
catch (TimeoutException)
{
Console.Error.WriteLine("Connection to RatAttack pipe has timed out.");
}
catch (UnauthorizedAccessException)
{
Console.Error.WriteLine("Cannot connect to RatAttack pipe. Are you running VoiceAttack as Admin?");
}
}
}
private static string StripIrcCodes(string message)
{
return Regex.Replace(message, @"[\x02\x11\x0F\x1D\x1E\x1F\x16]|\x03(\d\d?(,\d\d?)?)?", string.Empty);
}
}
}

View file

@ -1,4 +1,23 @@
using System.Reflection; // <copyright file="AssemblyInfo.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View file

@ -1,332 +1,445 @@
#nullable enable // <copyright file="RatAttack.cs" company="alterNERDtive">
// Copyright 20192022 alterNERDtive.
using System; //
using System.Collections.Generic; // This file is part of alterNERDtive VoiceAttack profiles for Elite Dangerous.
using System.Diagnostics; //
using System.Text.RegularExpressions; // alterNERDtive VoiceAttack profiles for Elite Dangerous is free software: you can redistribute it and/or modify
using alterNERDtive.util; // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
namespace RatAttack // (at your option) any later version.
{ //
public class RatAttack // 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
private static Dictionary<int,RatCase> CaseList { get; } = new Dictionary<int, RatCase>(); // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
private static dynamic? VA { get; set; } // GNU General Public License for more details.
private static alterNERDtive.util.PipeServer<Ratsignal> RatsignalPipe //
=> ratsignalPipe ??= new alterNERDtive.util.PipeServer<Ratsignal>(Log, "RatAttack", // You should have received a copy of the GNU General Public License
new alterNERDtive.util.PipeServer<Ratsignal>.SignalHandler(On_Ratsignal)); // along with alterNERDtive VoiceAttack profiles for Elite Dangerous. If not, see &lt;https://www.gnu.org/licenses/&gt;.
private static alterNERDtive.util.PipeServer<Ratsignal>? ratsignalPipe; // </copyright>
private static readonly Regex RatsignalRegex = new Regex( #nullable enable
@"^RATSIGNAL Case #(?<number>\d+) (?<platform>(PC|Xbox|Playstation))(?<oxygen> \(Code Red\))?(?<odyssey> \(Odyssey\))? CMDR (?<cmdr>.+) System: (None|u\u200bnknown system|""(?<system>.+)"" \((?<systemInfo>([a-zA-Z0-9\s\(\)\-~]*([0-9,\.]+ LY (""[a-zA-Z\-]+"" of|from) [a-zA-Z0-9\s\*\-]+)?( \([a-zA-Z\s]+\))?|Not found in galaxy database|Invalid system name))\)(?<permit> \(((?<permitName>.*) )?Permit Required\))?) Language: (?<language>[a-zA-z0-9\x7f-\xff\-\(\)\s]+)( Nick: (?<nick>[a-zA-Z0-9_\[\]\-]+))? \((PC|XB|PS)_SIGNAL\)\v*$"
); using System;
using System.Collections.Concurrent;
private static VoiceAttackLog Log using System.Diagnostics;
=> log ??= new VoiceAttackLog(VA, "RatAttack"); using System.Text.RegularExpressions;
private static VoiceAttackLog? log;
using alterNERDtive.util;
private static VoiceAttackCommands Commands
=> commands ??= new VoiceAttackCommands(VA, Log); namespace RatAttack
private static VoiceAttackCommands? commands; {
/// <summary>
private class RatCase /// VoiceAttack plugin for the RatAttack profile.
{ /// </summary>
public string Cmdr; public class RatAttack
public string? Language; {
public string? System; private static readonly Version VERSION = new ("6.4");
public string? SystemInfo;
public bool PermitLocked; private static readonly Regex RatsignalRegex = new (
public string? PermitName; @"^RATSIGNAL Case #(?<number>\d+) (?<platform>(PC|Xbox|Playstation))( )?(?<mode>H3.8|H4.0|ODY)?(?<oxygen> \(Code Red\))? CMDR (?<cmdr>.+) System: (None|u\u200bnknown system|""(?<system>.+)"" \((?<systemInfo>([a-zA-Z0-9\s\(\)\-/]*(~?[0-9,\.]+ LY (""[a-zA-Z\-]+"" of|from) [a-zA-Z0-9\s\*\-]+)?( \([a-zA-Z\s]+\))?|Not found in galaxy database|Invalid system name))\)(?<permit> \(((?<permitName>.*) )?Permit Required\))?) Language: (?<language>[a-zA-z0-9\x7f-\xff\-\(\)&,\s\.]+)( Nick: (?<nick>[a-zA-Z0-9_\[\]\-\^]+))? \((H3|H4|ODY|XB|PS)_SIGNAL\)\v*$");
public string Platform;
public bool Odyssey; private static PipeServer<Ratsignal>? ratsignalPipe;
public bool CodeRed; private static VoiceAttackLog? log;
public int Number; private static VoiceAttackCommands? commands;
public RatCase(string cmdr, string? language, string? system, string? systemInfo, bool permitLocked, string? permitName, string platform, bool odyssey, bool codeRed, int number) private static ConcurrentDictionary<int, RatCase> CaseList { get; } = new ();
=> (Cmdr, Language, System, SystemInfo, PermitLocked, PermitName, Platform, Odyssey, CodeRed, Number) = (cmdr, language, system, systemInfo, permitLocked, permitName, platform, odyssey, codeRed, number);
private static dynamic? VA { get; set; }
public string ShortInfo
{ private static PipeServer<Ratsignal> RatsignalPipe
get => $"#{Number}, {Platform}{(Odyssey ? " (Odyssey)" : "")}{(CodeRed ? ", code red" : "")}, {System ?? "None"}{(SystemInfo != null ? $" ({SystemInfo}{(PermitLocked ? ", permit required" : "")})" : "")}"; => ratsignalPipe ??= new (
} Log,
"RatAttack",
public override string ToString() new PipeServer<Ratsignal>.SignalHandler(On_Ratsignal));
=> ShortInfo;
} private static VoiceAttackLog Log => log ??= new (VA, "RatAttack");
public class Ratsignal : IPipable private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
{
public string Signal { get; set; } /*========================================\
public bool Announce { get; set; } | required VoiceAttack plugin shenanigans |
private readonly char separator = '\x1F'; \========================================*/
public Ratsignal() /// <summary>
=> (Signal, Announce) = ("", false); /// The plugins GUID, as required by the VoiceAttack plugin API.
/// </summary>
public Ratsignal(string signal, bool announce) /// <returns>The GUID.</returns>
=> (Signal, Announce) = (signal, announce); public static Guid VA_Id()
=> new ("{F2ADF0AE-4837-4E4A-9C87-8A7E2FA63E5F}");
public void ParseString(string serialization)
{ /// <summary>
try /// The plugins display name, as required by the VoiceAttack plugin API.
{ /// </summary>
string[] parts = serialization.Split(separator); /// <returns>The display name.</returns>
Signal = parts[0]; public static string VA_DisplayName()
Announce = Boolean.Parse(parts[1]); => $"RatAttack {VERSION}";
}
catch (Exception e) /// <summary>
{ /// The plugins description, as required by the VoiceAttack plugin API.
throw new ArgumentException($"Invalid serialized RATSIGNAL: '{serialization}'", e); /// </summary>
} /// <returns>The description.</returns>
} public static string VA_DisplayInfo()
=> "RatAttack: a plugin to handle FuelRats cases.";
public override string ToString()
=> $"{Signal}{separator}{Announce}"; /// <summary>
} /// The Init method, as required by the VoiceAttack plugin API.
/// Runs when the plugin is initially loaded.
private static int ParseRatsignal(string ratsignal) /// </summary>
{ /// <param name="vaProxy">The VoiceAttack proxy object.</param>
if (!RatsignalRegex.IsMatch(ratsignal)) public static void VA_Init1(dynamic vaProxy)
throw new ArgumentException($"Invalid RATSIGNAL format: '{ratsignal}'.", "ratsignal"); {
VA = vaProxy;
Match match = RatsignalRegex.Match(ratsignal); Log.Notice("Initializing …");
VA.SetText("RatAttack.version", VERSION.ToString());
string cmdr = match.Groups["cmdr"].Value; vaProxy.ProfileChanged += new Action<Guid?, Guid?, string, string>(On_ProfileChanged);
string? language = match.Groups["language"].Value; Log.Notice("Init successful.");
string? system = match.Groups["system"].Value; }
string? systemInfo = match.Groups["systemInfo"].Value;
bool permitLocked = match.Groups["permit"].Success; /// <summary>
string? permitName = match.Groups["permitName"].Value; /// The Invoke method, as required by the VoiceAttack plugin API.
string platform = match.Groups["platform"].Value; /// Runs whenever a plugin context is invoked.
bool codeRed = match.Groups["oxygen"].Success; /// </summary>
bool odyssey = match.Groups["odyssey"].Success; /// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Invoke1(dynamic vaProxy)
int number = int.Parse(match.Groups["number"].Value); {
string context = vaProxy.Context.ToLower();
if (String.IsNullOrEmpty(system)) Log.Debug($"Running context '{context}' …");
{ try
system = "None"; {
} switch (context)
{
Log.Debug($"New rat case: CMDR “{cmdr}” in “{system}”{(systemInfo != null ? $" ({systemInfo})" : "")} on {platform}{(odyssey ? " (Odyssey)" : "")}, permit locked: {permitLocked}{(permitLocked && permitName != null ? $" (permit name: {permitName})" : "")}, code red: {codeRed} (#{number})."); case "getcasedata":
// plugin methods
CaseList[number] = new RatCase(cmdr, language, system, systemInfo, permitLocked, permitName, platform, odyssey, codeRed, number); Context_GetCaseData(vaProxy);
break;
return number; case "parseratsignal":
} Context_ParseRatsignal(vaProxy);
break;
private static void On_Ratsignal(Ratsignal ratsignal) case "startup":
{ Context_Startup(vaProxy);
try break;
{ case "edsm.getnearestcmdr":
int number = ParseRatsignal(ratsignal.Signal); // EDSM
Log.Notice($"New rat case: {CaseList[number]}."); Context_EDSM_GetNearestCMDR(vaProxy);
Commands.TriggerEvent("RatAttack.incomingCase", parameters: new dynamic[] { new int[] { number }, new bool[] { ratsignal.Announce } }); break;
} case "log.log":
catch (ArgumentException e) // log
{ Context_Log(vaProxy);
Log.Error(e.Message); break;
Commands.TriggerEvent("RatAttack.invalidRatsignal", parameters: new dynamic[] { new string[] { ratsignal.Signal } }); default:
} // invalid
catch (Exception e) Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
{ break;
Log.Error($"Unhandled exception while parsing RATSIGNAL: '{e.Message}'."); }
} }
} catch (ArgumentNullException e)
{
private static void On_ProfileChanged(Guid? from, Guid? to, string fromName, string toName) Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
=> VA_Exit1(VA); }
catch (Exception e)
/*================\ {
| plugin contexts | Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})");
\================*/ }
}
private static void Context_EDSM_GetNearestCMDR(dynamic vaProxy)
{ /// <summary>
int caseNo = vaProxy.GetInt("~caseNo") ?? throw new ArgumentNullException("~caseNo"); /// The Exit method, as required by the VoiceAttack plugin API.
string cmdrList = vaProxy.GetText("~cmdrs") ?? throw new ArgumentNullException("~cmdrs"); /// Runs when VoiceAttack is shut down.
string[] cmdrs = cmdrList.Split(';'); /// </summary>
if (cmdrs.Length == 0) /// <param name="vaProxy">The VoiceAttack proxy object.</param>
{ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "required by VoiceAttack plugin API")]
throw new ArgumentNullException("~cmdrs"); public static void VA_Exit1(dynamic vaProxy)
} {
string system = CaseList[caseNo]?.System ?? throw new ArgumentException($"Case #{caseNo} has no system information", "~caseNo"); Log.Debug("Starting teardown …");
Log.Debug("Closing RATSIGNAL pipe …");
string path = $@"{vaProxy.SessionState["VA_SOUNDS"]}\Scripts\edsm-getnearest.exe"; RatsignalPipe.Stop();
string arguments = $@"--short --text --system ""{system}"" ""{string.Join(@""" """, cmdrs)}"""; Log.Debug("Teardown finished.");
}
Process p = PythonProxy.SetupPythonScript(path, arguments);
/// <summary>
p.Start(); /// The StopCommand method, as required by the VoiceAttack plugin API.
string stdout = p.StandardOutput.ReadToEnd(); /// Runs whenever all commands are stopped using the “Stop All Commands”
string stderr = p.StandardError.ReadToEnd(); /// button or action.
p.WaitForExit(); /// </summary>
public static void VA_StopCommand()
string message = stdout; {
string? errorMessage = null; }
bool error = true;
/// <summary>
switch (p.ExitCode) /// Parses a RATSIGNAL and extracts case data for storage.
{ /// </summary>
case 0: /// <param name="ratsignal">The incoming RATSIGNAL.</param>
error = false; /// <returns>The case number.</returns>
Log.Info(message); /// <exception cref="ArgumentException">Thrown on invalid RATSIGNAL.</exception>
break; private static int ParseRatsignal(string ratsignal)
case 1: // CMDR not found, Server Error, Api Exception (jeez, what a mess did I make there?) {
error = true; if (!RatsignalRegex.IsMatch(ratsignal))
Log.Error(message); {
break; throw new ArgumentException($"Invalid RATSIGNAL format: '{ratsignal}'.", "ratsignal");
case 2: // System not found }
error = true;
Log.Warn(message); Match match = RatsignalRegex.Match(ratsignal);
break;
default: string cmdr = match.Groups["cmdr"].Value;
error = true; string? language = match.Groups["language"].Value;
Log.Error(stderr); string? system = match.Groups["system"].Value;
errorMessage = "Unrecoverable error in plugin."; string? systemInfo = match.Groups["systemInfo"].Value;
break; bool permitLocked = match.Groups["permit"].Success;
string? permitName = match.Groups["permitName"].Value;
} string platform = match.Groups["platform"].Value;
bool codeRed = match.Groups["oxygen"].Success;
vaProxy.SetText("~message", message); string? mode = match.Groups["mode"].Value;
vaProxy.SetBoolean("~error", error);
vaProxy.SetText("~errorMessage", errorMessage); int number = int.Parse(match.Groups["number"].Value);
vaProxy.SetInt("~exitCode", p.ExitCode);
} if (string.IsNullOrEmpty(system))
{
private static void Context_GetCaseData(dynamic vaProxy) system = "None";
{ }
int cn = vaProxy.GetInt("~caseNumber");
Log.Debug($"New rat case: CMDR “{cmdr}” in “{system}”{(!string.IsNullOrEmpty(systemInfo) ? $" ({systemInfo})" : string.Empty)} on {platform}{(!string.IsNullOrEmpty(mode) ? $" ({mode})" : string.Empty)}, permit locked: {permitLocked}{(permitLocked && !string.IsNullOrEmpty(permitName) ? $" (permit name: {permitName})" : string.Empty)}, code red: {codeRed} (#{number}).");
if (CaseList.ContainsKey(cn))
{ CaseList[number] = new RatCase(cmdr, language, system, systemInfo, permitLocked, permitName, platform, mode, codeRed, number);
RatCase rc = CaseList[cn];
return number;
vaProxy.SetInt("~~caseNumber", rc.Number); }
vaProxy.SetText("~~cmdr", rc.Cmdr);
vaProxy.SetText("~~system", rc?.System?.ToLower()); private static void On_Ratsignal(Ratsignal ratsignal)
vaProxy.SetText("~~systemInfo", rc?.SystemInfo); {
vaProxy.SetBoolean("~~permitLocked", rc?.PermitLocked); try
vaProxy.SetText("~~permitName", rc?.PermitName); {
vaProxy.SetText("~~platform", rc?.Platform); int number = ParseRatsignal(ratsignal.Signal);
vaProxy.SetBoolean("~~odyssey", rc?.Odyssey); Log.Notice($"New rat case: {CaseList[number]}.");
vaProxy.SetBoolean("~~codeRed", rc?.CodeRed); Commands.TriggerEvent("RatAttack.incomingCase", parameters: new dynamic[] { new int[] { number }, new bool[] { ratsignal.Announce } });
} }
else catch (ArgumentException e)
{ {
Log.Warn($"Case #{cn} not found in the case list"); Log.Error(e.Message);
} Commands.TriggerEvent("RatAttack.invalidRatsignal", parameters: new dynamic[] { new string[] { ratsignal.Signal } });
} }
catch (Exception e)
private static void Context_Log(dynamic vaProxy) {
{ Log.Error($"Unhandled exception while parsing RATSIGNAL: '{e.Message}'.");
string message = vaProxy.GetText("~message"); }
string level = vaProxy.GetText("~level"); }
if (level == null) private static void On_ProfileChanged(Guid? from, Guid? to, string fromName, string toName)
{ => VA_Exit1(VA);
Log.Log(message);
} /*================\
else | plugin contexts |
{ \================*/
try
{ #pragma warning disable IDE0060 // Remove unused parameter
Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper())); private static void Context_EDSM_GetNearestCMDR(dynamic vaProxy)
} {
catch (ArgumentNullException) { throw; } int caseNo = vaProxy.GetInt("~caseNo") ?? throw new ArgumentNullException("~caseNo");
catch (ArgumentException) string cmdrList = vaProxy.GetText("~cmdrs") ?? throw new ArgumentNullException("~cmdrs");
{ string[] cmdrs = cmdrList.Split(';');
Log.Error($"Invalid log level '{level}'."); if (cmdrs.Length == 0)
} {
} throw new ArgumentNullException("~cmdrs");
} }
private static void Context_Startup(dynamic vaProxy) string system = CaseList[caseNo]?.System ?? throw new ArgumentException($"Case #{caseNo} has no system information", "~caseNo");
{
Log.Notice("Starting up …"); string path = $@"{vaProxy.SessionState["VA_SOUNDS"]}\Scripts\edsm-getnearest.exe";
VA = vaProxy; string arguments = $@"--short --text --system ""{system}"" ""{string.Join(@""" """, cmdrs)}""";
_ = RatsignalPipe.Run();
Log.Notice("Finished startup."); Process p = PythonProxy.SetupPythonScript(path, arguments);
}
p.Start();
private static void Context_ParseRatsignal(dynamic vaProxy) string stdout = p.StandardOutput.ReadToEnd();
{ string stderr = p.StandardError.ReadToEnd();
Log.Warn("Passing a RATSIGNAL from VoiceAttack (through the clipboard or a file) is DEPRECATED and will no longer be supported in the future."); p.WaitForExit();
On_Ratsignal(new Ratsignal(vaProxy.GetText("~ratsignal"), vaProxy.GetBoolean("~announceRatsignal")));
} string message = stdout;
string? errorMessage = null;
/*========================================\ bool error;
| required VoiceAttack plugin shenanigans |
\========================================*/ switch (p.ExitCode)
{
static readonly Version VERSION = new Version("6.2.2"); case 0:
error = false;
public static Guid VA_Id() Log.Info(message);
=> new Guid("{F2ADF0AE-4837-4E4A-9C87-8A7E2FA63E5F}"); break;
public static string VA_DisplayName() case 1: // CMDR not found, Server Error, Api Exception (jeez, what a mess did I make there?)
=> $"RatAttack {VERSION}"; error = true;
public static string VA_DisplayInfo() Log.Error(message);
=> "RatAttack: a plugin to handle FuelRats cases."; break;
case 2: // System not found
public static void VA_Init1(dynamic vaProxy) error = true;
{ Log.Warn(message);
VA = vaProxy; break;
Log.Notice("Initializing …"); default:
VA.SetText("RatAttack.version", VERSION.ToString()); error = true;
vaProxy.ProfileChanged += new Action<Guid?, Guid?, String, String>(On_ProfileChanged); Log.Error(stderr);
Log.Notice("Init successful."); errorMessage = "Unrecoverable error in plugin.";
} break;
}
public static void VA_Invoke1(dynamic vaProxy)
{ vaProxy.SetText("~message", message);
string context = vaProxy.Context.ToLower(); vaProxy.SetBoolean("~error", error);
Log.Debug($"Running context '{context}' …"); vaProxy.SetText("~errorMessage", errorMessage);
try vaProxy.SetInt("~exitCode", p.ExitCode);
{ }
switch (context)
{ private static void Context_GetCaseData(dynamic vaProxy)
// plugin methods {
case "getcasedata": int cn = vaProxy.GetInt("~caseNumber");
Context_GetCaseData(vaProxy);
break; if (CaseList.ContainsKey(cn))
case "parseratsignal": {
Context_ParseRatsignal(vaProxy); RatCase rc = CaseList[cn];
break;
case "startup": vaProxy.SetInt("~~caseNumber", rc.Number);
Context_Startup(vaProxy); vaProxy.SetText("~~cmdr", rc.Cmdr);
break; vaProxy.SetText("~~system", rc?.System?.ToLower());
// EDSM vaProxy.SetText("~~systemInfo", rc?.SystemInfo);
case "edsm.getnearestcmdr": vaProxy.SetBoolean("~~permitLocked", rc?.PermitLocked);
Context_EDSM_GetNearestCMDR(vaProxy); vaProxy.SetText("~~permitName", rc?.PermitName);
break; vaProxy.SetText("~~platform", rc?.Platform);
// log vaProxy.SetText("~~mode", rc?.Mode);
case "log.log": vaProxy.SetBoolean("~~codeRed", rc?.CodeRed);
Context_Log(vaProxy); }
break; else
// invalid {
default: Log.Warn($"Case #{cn} not found in the case list");
Log.Error($"Invalid plugin context '{vaProxy.Context}'."); }
break; }
}
} private static void Context_Log(dynamic vaProxy)
catch (ArgumentNullException e) {
{ string message = vaProxy.GetText("~message");
Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'"); string level = vaProxy.GetText("~level");
}
catch (Exception e) if (level == null)
{ {
Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})"); Log.Log(message);
} }
} else
{
public static void VA_Exit1(dynamic vaProxy) try
{ {
Log.Debug("Starting teardown …"); Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper()));
Log.Debug("Closing RATSIGNAL pipe …"); }
RatsignalPipe.Stop(); catch (ArgumentNullException)
Log.Debug("Teardown finished."); {
} throw;
}
public static void VA_StopCommand() { } catch (ArgumentException)
} {
} Log.Error($"Invalid log level '{level}'.");
}
}
}
private static void Context_Startup(dynamic vaProxy)
{
Log.Notice("Starting up …");
_ = RatsignalPipe.Run();
Log.Notice("Finished startup.");
}
private static void Context_ParseRatsignal(dynamic vaProxy)
{
Log.Warn("Passing a RATSIGNAL to VoiceAttack through the clipboard or a file is DEPRECATED and will no longer be supported in the future.");
On_Ratsignal(new Ratsignal(vaProxy.GetText("~ratsignal"), vaProxy.GetBoolean("~announceRatsignal") ?? false));
}
#pragma warning restore IDE0060 // Remove unused parameter
/// <summary>
/// Encapsulates a RATSIGNAL for sending between the CLI helper tool and
/// the plugin via named pipe.
/// </summary>
public class Ratsignal : IPipable
{
private readonly char separator = '\x1F';
/// <summary>
/// Initializes a new instance of the <see cref="Ratsignal"/> class.
/// </summary>
public Ratsignal()
=> (this.Signal, this.Announce) = (string.Empty, false);
/// <summary>
/// Initializes a new instance of the <see cref="Ratsignal"/> class.
/// </summary>
/// <param name="signal">The RATSIGNAL.</param>
/// <param name="announce">Whether or not to announce the new case.</param>
public Ratsignal(string signal, bool announce)
=> (this.Signal, this.Announce) = (signal, announce);
/// <summary>
/// Gets or sets the RATSIGNAL.
/// </summary>
public string Signal { get; set; }
/// <summary>
/// Gets or Sets a value indicating whether to announce the incoming
/// case.
/// </summary>
public bool Announce { get; set; }
/// <summary>
/// Initializes the <see cref="Ratsignal"/> instance from a
/// serialized representation.
/// FIXXME: should probably make this a static factory method.
/// </summary>
/// <param name="serialization">The serialized <see cref="Ratsignal"/>.</param>
/// <exception cref="ArgumentException">Thrown on receiving an invalid signal.</exception>
public void ParseString(string serialization)
{
try
{
string[] parts = serialization.Split(this.separator);
this.Signal = parts[0];
this.Announce = bool.Parse(parts[1]);
}
catch (Exception e)
{
throw new ArgumentException($"Invalid serialized RATSIGNAL: '{serialization}'", e);
}
}
/// <inheritdoc/>
public override string ToString()
=> $"{this.Signal}{this.separator}{this.Announce}";
}
private class RatCase
{
public RatCase(string cmdr, string? language, string? system, string? systemInfo, bool permitLocked, string? permitName, string platform, string mode, bool codeRed, int number)
=> (this.Cmdr, this.Language, this.System, this.SystemInfo, this.PermitLocked, this.PermitName, this.Platform, this.Mode, this.CodeRed, this.Number)
= (cmdr, language, system, systemInfo, permitLocked, permitName, platform, mode, codeRed, number);
public string Cmdr { get; }
public string? Language { get; }
public string? System { get; }
public string? SystemInfo { get; }
public bool PermitLocked { get; }
public string? PermitName { get; }
public string Platform { get; }
public string? Mode { get; }
public bool CodeRed { get; }
public int Number { get; }
public string ShortInfo
{
get => $"#{this.Number}, {this.Platform}{(!string.IsNullOrEmpty(this.Mode) ? $" ({this.Mode})" : string.Empty)}{(this.CodeRed ? ", code red" : string.Empty)}, {this.System ?? "None"}{(!string.IsNullOrEmpty(this.SystemInfo) ? $" ({this.SystemInfo}{(this.PermitLocked ? ", permit required" : string.Empty)})" : string.Empty)}";
}
public override string ToString()
=> this.ShortInfo;
}
}
}

View file

@ -25,7 +25,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\RatAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -34,7 +34,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\RatAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -1,4 +1,23 @@
using System.Reflection; // <copyright file="AssemblyInfo.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View file

@ -1,29 +1,158 @@
#nullable enable // <copyright file="SpanshAttack.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
#nullable enable
using alterNERDtive.util;
using alterNERDtive.edts;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using alterNERDtive.edts;
using alterNERDtive.util;
namespace SpanshAttack namespace SpanshAttack
{ {
/// <summary>
/// VoiceAttack plugin for the SpanshAttack profile.
/// </summary>
public class SpanshAttack public class SpanshAttack
{ {
private static readonly Version VERSION = new ("7.2.2");
private static VoiceAttackLog? log;
private static VoiceAttackCommands? commands;
private static dynamic? VA { get; set; } private static dynamic? VA { get; set; }
private static VoiceAttackLog Log private static VoiceAttackLog Log => log ??= new (VA, "SpanshAttack");
=> log ??= new VoiceAttackLog(VA, "SpanshAttack");
private static VoiceAttackLog? log;
private static VoiceAttackCommands Commands private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
=> commands ??= new VoiceAttackCommands(VA, Log);
private static VoiceAttackCommands? commands; /*========================================\
| required VoiceAttack plugin shenanigans |
\========================================*/
/// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The GUID.</returns>
public static Guid VA_Id()
=> new ("{e722b29d-898e-47dd-a843-a409c87e0bd8}");
/// <summary>
/// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The display name.</returns>
public static string VA_DisplayName()
=> $"SpanshAttack {VERSION}";
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The description.</returns>
public static string VA_DisplayInfo()
=> "SpanshAttack: a plugin for doing routing with spansh.co.uk for Elite: Dangerous.";
/// <summary>
/// The Init method, as required by the VoiceAttack plugin API.
/// Runs when the plugin is initially loaded.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Init1(dynamic vaProxy)
{
VA = vaProxy;
Log.Notice("Initializing …");
VA.SetText("SpanshAttack.version", VERSION.ToString());
Log.Notice("Init successful.");
}
/// <summary>
/// The Invoke method, as required by the VoiceAttack plugin API.
/// Runs whenever a plugin context is invoked.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Invoke1(dynamic vaProxy)
{
string context = vaProxy.Context.ToLower();
Log.Debug($"Running context '{context}' …");
try
{
switch (context)
{
case "startup":
Context_Startup(vaProxy);
break;
case "edts.getcoordinates":
// EDTS
Context_EDTS_GetCoordinates(vaProxy);
break;
case "log.log":
// log
Context_Log(vaProxy);
break;
case "spansh.systemexists":
// Spansh
Context_Spansh_SytemExists(vaProxy);
break;
case "spansh.nearestsystem":
Context_Spansh_Nearestsystem(vaProxy);
break;
default:
// invalid
Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
break;
}
}
catch (ArgumentNullException e)
{
Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
}
catch (Exception e)
{
Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})");
}
}
/// <summary>
/// The Exit method, as required by the VoiceAttack plugin API.
/// Runs when VoiceAttack is shut down.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "required by VoiceAttack plugin API")]
public static void VA_Exit1(dynamic vaProxy)
{
}
/// <summary>
/// The StopCommand method, as required by the VoiceAttack plugin API.
/// Runs whenever all commands are stopped using the “Stop All Commands”
/// button or action.
/// </summary>
public static void VA_StopCommand()
{
}
/*================\ /*================\
| plugin contexts | | plugin contexts |
\================*/ \================*/
#pragma warning disable IDE0060 // Remove unused parameter
private static void Context_EDTS_GetCoordinates(dynamic vaProxy) private static void Context_EDTS_GetCoordinates(dynamic vaProxy)
{ {
string name = vaProxy.GetText("~system") ?? throw new ArgumentNullException("~system"); string name = vaProxy.GetText("~system") ?? throw new ArgumentNullException("~system");
@ -51,11 +180,13 @@ namespace SpanshAttack
vaProxy.SetInt("~precision", system.Coords.Precision); vaProxy.SetInt("~precision", system.Coords.Precision);
success = true; success = true;
} catch (ArgumentException e) }
catch (ArgumentException e)
{ {
errorType = "invalid name"; errorType = "invalid name";
errorMessage = e.Message; errorMessage = e.Message;
} catch (Exception e) }
catch (Exception e)
{ {
errorType = "connection error"; errorType = "connection error";
errorMessage = e.Message; errorMessage = e.Message;
@ -85,7 +216,10 @@ namespace SpanshAttack
{ {
Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper())); Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper()));
} }
catch (ArgumentNullException) { throw; } catch (ArgumentNullException)
{
throw;
}
catch (ArgumentException) catch (ArgumentException)
{ {
Log.Error($"Invalid log level '{level}'."); Log.Error($"Invalid log level '{level}'.");
@ -104,11 +238,11 @@ namespace SpanshAttack
Process p = PythonProxy.SetupPythonScript(path, arguments); Process p = PythonProxy.SetupPythonScript(path, arguments);
Dictionary<char, decimal> coords = new Dictionary<char, decimal> { { 'x', 0 }, { 'y', 0 }, { 'z', 0 } }; Dictionary<char, decimal> coords = new () { { 'x', 0 }, { 'y', 0 }, { 'z', 0 } };
string system = ""; string system = string.Empty;
decimal distance = 0; decimal distance = 0;
bool error = false; bool error = false;
string errorMessage = ""; string errorMessage = string.Empty;
p.Start(); p.Start();
string stdout = p.StandardOutput.ReadToEnd(); string stdout = p.StandardOutput.ReadToEnd();
@ -159,7 +293,7 @@ namespace SpanshAttack
bool exists = true; bool exists = true;
bool error = false; bool error = false;
string errorMessage = ""; string errorMessage = string.Empty;
p.Start(); p.Start();
string stdout = p.StandardOutput.ReadToEnd(); string stdout = p.StandardOutput.ReadToEnd();
@ -195,75 +329,8 @@ namespace SpanshAttack
private static void Context_Startup(dynamic vaProxy) private static void Context_Startup(dynamic vaProxy)
{ {
Log.Notice("Starting up …"); Log.Notice("Starting up …");
VA = vaProxy;
Log.Notice("Finished startup."); Log.Notice("Finished startup.");
} }
#pragma warning restore IDE0060 // Remove unused parameter
/*========================================\
| required VoiceAttack plugin shenanigans |
\========================================*/
static readonly Version VERSION = new Version("7.2.0");
public static Guid VA_Id()
=> new Guid("{e722b29d-898e-47dd-a843-a409c87e0bd8}");
public static string VA_DisplayName()
=> $"SpanshAttack {VERSION}";
public static string VA_DisplayInfo()
=> "SpanshAttack: a plugin for doing routing with spansh.co.uk for Elite: Dangerous.";
public static void VA_Init1(dynamic vaProxy)
{
VA = vaProxy;
Log.Notice("Initializing …");
VA.SetText("SpanshAttack.version", VERSION.ToString());
Log.Notice("Init successful.");
}
public static void VA_Invoke1(dynamic vaProxy)
{
string context = vaProxy.Context.ToLower();
Log.Debug($"Running context '{context}' …");
try
{
switch (context)
{
case "startup":
Context_Startup(vaProxy);
break;
// EDTS
case "edts.getcoordinates":
Context_EDTS_GetCoordinates(vaProxy);
break;
// log
case "log.log":
Context_Log(vaProxy);
break;
// Spansh
case "spansh.systemexists":
Context_Spansh_SytemExists(vaProxy);
break;
case "spansh.nearestsystem":
Context_Spansh_Nearestsystem(vaProxy);
break;
// invalid
default:
Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
break;
}
}
catch (ArgumentNullException e)
{
Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
}
catch (Exception e)
{
Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})");
}
}
public static void VA_Exit1(dynamic vaProxy) { }
public static void VA_StopCommand() { }
} }
} }

View file

@ -21,7 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\SpanshAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@ -30,7 +30,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\SpanshAttack.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -0,0 +1,28 @@
// <copyright file="GlobalSuppressions.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
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")]

View file

@ -1,4 +1,23 @@
using System.Reflection; // <copyright file="AssemblyInfo.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -10,7 +29,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VoiceAttack-base")] [assembly: AssemblyProduct("VoiceAttack-base")]
[assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyCopyright("Copyright © 20202022")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]

View file

@ -0,0 +1,25 @@
<UserControl x:Class="alterNERDtive.SettingsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:alterNERDtive"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<TabControl Name="tabs">
<TabItem Name="general" Header="general"></TabItem>
<TabItem Name="EliteAttack" Header="EliteAttack"></TabItem>
<TabItem Name="RatAttack" Header="RatAttack"></TabItem>
<TabItem Name="SpanshAttack" Header="SpanshAttack"></TabItem>
<TabItem Name="StreamAttack" Header="StreamAttack"></TabItem>
</TabControl>
<WrapPanel VerticalAlignment="Bottom" HorizontalAlignment="Right">
<Button Name="applyButton" Click="ApplyButton_Click" Padding="5" Margin="5" Width="100">Apply</Button>
<Button Name="okButton" Click="OkButton_Click" Padding="5" Margin="5" Width="100">Done</Button>
<Button Name="cancelButton" Click="CancelButton_Click" Padding="5" Margin="5" Width="100">Cancel</Button>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>

View file

@ -0,0 +1,188 @@
// <copyright file="SettingsDialog.xaml.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace alterNERDtive
{
/// <summary>
/// Interaction logic for SettingsDialog.xaml.
/// </summary>
public partial class SettingsDialog : UserControl
{
private readonly List<Setting> values = new List<Setting>();
private util.Configuration config;
private util.VoiceAttackLog log;
/// <summary>
/// Initializes a new instance of the <see cref="SettingsDialog"/> class.
/// </summary>
/// <param name="config">The plugin Configuration.</param>
/// <param name="log">The plugin Log.</param>
public SettingsDialog(util.Configuration config, util.VoiceAttackLog log)
{
this.InitializeComponent();
this.config = config;
this.log = log;
foreach (TabItem tab in this.tabs.Items)
{
string profile = tab.Name;
if (profile == "general")
{
profile = "alterNERDtive-base";
}
tab.IsEnabled = BasePlugin.IsProfileActive(profile);
StackPanel panel = new StackPanel();
util.Configuration.OptDict<string, util.Configuration.Option> options = util.Configuration.GetOptions(profile);
foreach (dynamic option in options.Values)
{
dynamic value = config.GetConfig(profile, option.Name);
if (option is util.Configuration.Option<bool>)
{
WrapPanel row = new WrapPanel();
CheckBox checkBox = new CheckBox();
checkBox.IsChecked = value;
checkBox.VerticalAlignment = VerticalAlignment.Center;
row.Children.Add(checkBox);
this.values.Add(new Setting(profile, option, value, checkBox));
Label label = new Label();
label.Content = option.Description;
row.Children.Add(label);
panel.Children.Add(row);
}
else
{
StackPanel row = new StackPanel();
Label label = new Label();
label.Content = option.Description;
row.Children.Add(label);
TextBox input = new TextBox();
input.Text = value.ToString();
row.Children.Add(input);
this.values.Add(new Setting(profile, option, value, input));
panel.Children.Add(row);
}
}
tab.Content = panel;
}
}
private bool ApplySettings()
{
bool success = true;
foreach (Setting setting in this.values)
{
dynamic? state = null;
try
{
if (setting.Option is util.Configuration.Option<bool>)
{
state = ((CheckBox)setting.UiElement).IsChecked ?? false;
}
else if (setting.Option is util.Configuration.Option<DateTime>)
{
state = DateTime.Parse(((TextBox)setting.UiElement).Text);
}
else if (setting.Option is util.Configuration.Option<decimal>)
{
state = decimal.Parse(((TextBox)setting.UiElement).Text);
}
else if (setting.Option is util.Configuration.Option<int>)
{
state = int.Parse(((TextBox)setting.UiElement).Text);
}
else if (setting.Option is util.Configuration.Option<short>)
{
state = short.Parse(((TextBox)setting.UiElement).Text);
}
else if (setting.Option is util.Configuration.Option<string>)
{
state = ((TextBox)setting.UiElement).Text;
}
if (state != setting.Value)
{
this.log.Log($@"Configuration changed via settings dialog: ""{setting.Profile}.{setting.Option.Name}"" → ""{state}""", util.LogLevel.DEBUG);
this.config.SetConfig(setting.Profile, setting.Option.Name, state);
}
}
catch (Exception e) when (e is ArgumentNullException || e is FormatException || e is OverflowException)
{
this.log.Log($@"Invalid value for ""{setting.Profile}.{setting.Option.Name}"": ""{((TextBox)setting.UiElement).Text}""", util.LogLevel.ERROR);
success = false;
}
}
return success;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
Window.GetWindow(this).Close();
this.log.Log("Settings dialog cancelled.", util.LogLevel.DEBUG);
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
if (this.ApplySettings())
{
Window.GetWindow(this).Close();
}
}
private void ApplyButton_Click(object sender, RoutedEventArgs reeargs)
{
this.ApplySettings();
}
private struct Setting
{
public Setting(string profile, dynamic option, dynamic value, dynamic uiElement)
=> (this.Profile, this.Option, this.Value, this.UiElement) = (profile, option, value, uiElement);
public string Profile { get; }
public dynamic Option { get; }
public dynamic Value { get; }
public dynamic UiElement { get; }
}
}
}

View file

@ -24,7 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\base.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -33,33 +33,50 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion> <DocumentationFile>..\build\alterNERDtive\base.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="PresentationCore" />
<HintPath>..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath> <Reference Include="PresentationFramework" />
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="base.cs" /> <Compile Include="base.cs" />
<Compile Include="edts.cs" /> <Compile Include="edts.cs">
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
</Compile>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SettingsDialog.xaml.cs">
<DependentUpon>SettingsDialog.xaml</DependentUpon>
</Compile>
<Compile Include="util.cs" /> <Compile Include="util.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <Page Include="SettingsDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Client">
<Version>5.2.9</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
</PackageReference>
<PackageReference Include="System.Net.Http">
<Version>4.3.4</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,31 @@
#nullable enable // <auto-generated/>
// Not really, but this file will not bow to StyleCop tyranny.
// Why? Because it will be obsolete mid term anyway ;)
// <copyright file="edts.cs" company="alterNERDtive">
// Copyright 20192022 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 &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
#nullable enable
using System; using System;
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace alterNERDtive.edts namespace alterNERDtive.edts
{ {
@ -57,4 +78,4 @@ namespace alterNERDtive.edts
return new StarSystem { Name=name, Coords=new Position { X=x, Y=y, Z=z, Precision=uncertainty } }; return new StarSystem { Name=name, Coords=new Position { X=x, Y=y, Z=z, Precision=uncertainty } };
} }
} }
} }

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.7" targetFramework="net48" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net48" />
</packages>

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
mkdocs-roamlinks-plugin

16
stylecop.json Normal file
View file

@ -0,0 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
},
"documentationRules": {
"companyName": "alterNERDtive",
"copyrightText": "Copyright {year} {companyName}.\n\nThis file is part of {application}.\n\n{application} is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\n{application} is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with {application}. If not, see <https://www.gnu.org/licenses/>.",
"variables": {
"application": "alterNERDtive VoiceAttack profiles for Elite Dangerous",
"year": "20192022"
}
}
}
}