Compare commits

...

117 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
d47ea2597c 4.2.2! 2021-05-19 17:45:29 +02:00
42488b3bcf RatAttack: slightly changed case announcements 2021-05-19 17:44:21 +02:00
25a7440c7b CHANGELOG: bindED 4.0 tidbit 2021-05-19 12:41:34 +02:00
254ada7318 Preemptively changed VERSION before I forget it again … 2021-05-19 10:02:04 +02:00
aed4609ed8 RatAttack: fixed code red detection
… broke with the reworked case announcement.

fixes #115
2021-05-19 09:54:40 +02:00
1991e6b5f6 RatAttack: correctly handles "" system now
Still not sure if a completely empty system (happens on manual RATSIGNAL) is intentional; but I’m handling it gracefully now, defaulting to “None”.
2021-05-19 09:53:31 +02:00
b30074317f RatAttack: support for Odyssey
Due to split instancing between Horizons and Odyssey the RATSIGNAL announcement now contains extra info on that.

fixes #114
2021-05-19 09:51:00 +02:00
3b9031ae59 forgot to bump VERSION … 2021-05-03 10:39:32 +02:00
63 changed files with 3919 additions and 2084 deletions

View file

@ -1,5 +1,14 @@
[*]
[*]
guidelines = 80
end_of_line = lf
insert_final_newline = true
charset = utf-8-bom
[*.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
/site
/src
/build.csproj

View file

@ -1,3 +1,208 @@
# 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)
Now comes with Odyssey-compatible bindED.
**DO read [the release notes on
that](https://github.com/alterNERDtive/bindED/releases/tag/release/4.0) before
upgrading!**
## RatAttack 6.2.2
### Fixed
* Added handling for Odyssey clients. (#114)
* Correctly setting system for manual Ratsignals (`""`) to `None` now. IMO a
Mecha bug, but whatever.
* Code reds are now correctly detected again with the new case announcement
format. (#115)
-----
# 4.2.1 (2021-05-03)
This will probably be the last pre-Odyssey release. I am not in the Odyssey
@ -111,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.
* `system information for fuel rat case` configuration option: System
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.
### Changed
@ -930,8 +1135,7 @@ up to date in EDDN.
* Added `open [rat;] dispatch board` command. Opens the web dispatch board in
your default browser.
* Added proper handling for multiple ratsignals hitting at once. Thats mainly
an IRC client config thing,
[see the docs](docs/RatAttack.md#getting-case-data-from-irc).
an IRC client config thing, [see the docs](docs/configuration/RatAttack.md).
* Renamed `RatAttack.getInfoFromRatsignal` to
`RatAttack.announceCaseFromRatsignal`. Removed the “open case?” voice input
prompt.

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
to enhance my Elite experience. They give me important info, facilitate
day-to-day gaming and do some special things for [Fuel
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 ##
* 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
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.0
4.5

View file

@ -1,4 +1,4 @@
# EliteAttack
# EliteAttack
This is my personal VoiceAttack profile for Elite: Dangerous. It started out
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
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
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;]`:
Overly complicated command to handle everything related to Cargo Scoop, Hard
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
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.
* `[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,
@ -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
first.
## SRV controls
## SRV Controls
Things relevant to your SRV, but not your ship.
@ -107,13 +106,12 @@ Things relevant to your SRV, but not your ship.
## 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 :)
* `target the [drive;drives;power plant;frame shift drive;f s d;shield
generator]`:*
Targets the given submodule on your current target, or your next target if you
dont have one currently. Does not persist between targets.
generator]`: Targets the given submodule on your current target, or your next
target if you dont have one currently. Does not persist between targets.
* `clear sub [module;system] target`: Clears the current submodule target.
* `target next system`: Selects the next system on your route.
* `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
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
Reports on the module that has been repaired, and if it has been fully or
@ -226,8 +228,8 @@ scripts).
### Docked
Automatically refuels, repairs, optionally rearms, then gets your ship into the
hangar and opens station services.
Automatically refuels, repairs, rearms, then gets your ship into the hangar and
opens station services. Can be individually disabled.
### Docking Denied
@ -268,7 +270,7 @@ the (rough) range you still have on the fumes left in your tank.
### 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.
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
Lists you all bodies EDDI considers worth mapping in the current system.
Lists all bodies EDDI considers worth mapping in the current system.
### Undocked

View file

@ -1,4 +1,4 @@
# RatAttack
# RatAttack
This profile facilitates [Fuel Ratting](https://www.fuelrats.com). It aims to
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
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 ###
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
station/port/outpost/planet.
* `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;]`:
Friend request confirmations, with all the
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
you?
By default, VoiceAttack will ask for confirmation before sending calls to the
`#fuelrats` channel.
### Closing a Case
* `[close;clear] rat case`: Closes the currently open rat case.

View file

@ -1,10 +1,10 @@
# SpanshAttack
This profile uses the
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) plugin to plot
neutron jumps using [spansh](https://spansh.co.uk/plotter). It fully does
everything you need from within the game and VoiceAttack, you wont have to
visit the site at any point.
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) plugin to
plot neutron jumps using [Spansh](https://spansh.co.uk/plotter). It does
everything you need fully from within the game and VoiceAttack, you wont have
to visit the site at any point.
## Plotting a Route
@ -23,7 +23,7 @@ visit the site at any point.
### 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
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.
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
neutron waypoint to the clipboard.
### Skipping a waypoint
### Skipping a Waypoint
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

View file

@ -1,4 +1,4 @@
# StreamAttack
# StreamAttack
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
@ -23,6 +23,10 @@ Default folder is `%appdata%\StreamAttack\`.
### 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
* `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\full`: `“<name>” | <model> | <build>`.
* `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
@ -8,8 +8,20 @@ Toggles:
without constraints. Default: false.
* `auto honk new systems`: Automatically honk upon entering a system if it is
your first visit. Default: true.
* `auto restock`: Automatically restock after docking at a station. You will
always refuel, repair and enter the Station Services menu. Default: true.
* `auto refuel`: Automatically refuel after docking at a station. Default:
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
against your discovery scan. Default: true.
* `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.
* `outdated stations`: Announce stations with outdated data in the online
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.
* `road to riches`: Announce bodies worth scanning if you are looking for some
starting cash on the Road to Riches. Default: false.

View file

@ -1,4 +1,4 @@
# RatAttack
# RatAttack
## 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
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
@ -88,6 +88,8 @@ Toggles:
* `auto close fuel rat case`: Automatically close a rat case when sending
“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
confirmation to prevent mistakes. 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
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.
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.

View file

@ -1,4 +1,4 @@
# General Configuration
# General Configuration
## 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
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 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
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
@ -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
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
when you switch profiles. Once you switch back, the correct settings for the
profile are re-loaded.
Since the settings are saved to your custom profile they will not necessarily be
preserved when you switch profiles. Once you switch back, the correct settings
for the profile are re-loaded.
### General Settings for All Profiles
@ -48,9 +51,10 @@ Other settings:
## Note on Non-Standard Keyboard Layouts
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]
layout. You will have to change it to the key that is physically in the place
where `V` would be on QWERTY, e.g. `P` for [Neo2](https://neo-layout.org).
for pasting text into Elite Dangerous if you are not using a keyboard layout
that the game supports by default. You will have to change it to the key that
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”.
@ -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
stuff _will_ break.
![[EDDI-event.png]]
![[EDDI-event.png]]

View file

@ -2,7 +2,7 @@
## 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).
## Chat
@ -10,7 +10,7 @@ See [the configuration section](../configuration/general#settings).
* `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
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
@ -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
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.
* `open documentation`: Opens the documentation in your default browser.
* `open EDDI options;configure EDDI`: Displays EDDIs configuration window.
* `open elite bindings folder`: Opens Elites bindings folder
(`%localappdata%\Frontier Developments\Elite Dangerous\Options\Bindings`)
* `reload elite key binds`: Forces a reload of your Elite binds. Should not be
necessary.
* `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
to enhance my Elite experience. They give me important info, facilitate
day-to-day gaming and do some special things for [Fuel
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
* [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.
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
issue](https://github.com/alterNERDtive/VoiceAttack-profiles/issues). Thanks! :)
You can also [say “Hi” on Discord](https://discord.gg/kXtXm54) if that is your
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
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
executable installer. 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.
executable installer.
![[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
folder](https://steamcommunity.com/sharedfiles/filedetails/?id=760447682). It is
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
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:
@ -40,13 +38,14 @@ While you are there, you might also want to enable the automatic update checks
## Install EDDI
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
VoiceAttack
Github (The `EDDI-X.Y.Z.exe` file under “Assets”). You will need to [install it
as a 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
already have it installed somewhere else) you can also just create a symbolic
link. Open a CMD prompt (Windows key + `R`, enter “cmd”, hit `Enter`) and do
already have it installed somewhere else) you can instead put it elsewhere and
create a symbolic link. Open a command prompt (Windows key + `R`, enter “cmd”,
hit `Enter`) and do
```cmd
>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
within “ProgramFiles” you will have to run the CMD prompt as admin (Windows key
+ `R`, enter “cmd”, hit `Control` + `Shift` + `Enter`).
within “Program Files” you will have to run the command prompt as admin (Windows
key + `R`, enter “cmd”, hit `Control` + `Shift` + `Enter`).
## Install ED-NeutronRouter
**Make sure to [grab the pre-release
1.02](https://github.com/sc-pulgan/ED-NeutronRouter/releases/tag/1.02)** since
1.01 has a bug with a hardcoded 50ly jump range (the
`EDNeutronRouter.vX.YZ.zip` under “Assets”).
You will have to extract the contents of the release .zip file to your
VoiceAttack Apps folder:
Grab [the latest release release from
Github](https://github.com/sc-pulgan/ED-NeutronRouter/releases/latest) (The
`EDNeutronRouter.vX.YZ.zip` under “Assets”). 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. 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
release](https://github.com/alterNERDtive/VoiceAttack-profiles/releases/latest)
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. 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
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”.
![[VoiceAttack-new-profile.png]]
1. Give it a name and add some commands if you want to.
1. Hit “Done” to create the new profile.
### 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.
Alternatively you can keep using your existing profile. You will still have to
follow the rest of the instructions in this case.
#### 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]]
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.
![[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
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
While editing the profile, hit the “Options” button.
@ -179,23 +183,31 @@ button.
![[VoiceAttack-profile-options-include.png]]
Add all my profiles (“alterNERDtive-base”, “EliteDangerous”, “RatAttack”,
“SpanshAttack”,“StreamAttack”).
Add all my profiles (“alterNERDtive-base”, “EliteAttack”, “RatAttack”,
“SpanshAttack”, “StreamAttack”).
![[VoiceAttack-profile-options-includelist.png]]
Technically you can leave out anything you are not planning on using.
Practically it probably will not hurt you to just include everything, and it
will then available for you in the future if you choose to check it out! Make
sure that “alterNERDtive-base” is on top of the list, the order of the others
does not matter. But I like it nice and alphabetical …
Make sure that “alterNERDtive-base” is on top of the list, the order of the
others does not matter. But I like it nice and alphabetical. Technically you can
leave out any profile you are not planning to use. Practically it probably will
not hurt you to just include everything, and it will then be available for you
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
profile is loaded” checkbox, and select the “startup” command you have created
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
earlier.
![[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
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]]
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
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.
![[bindED-layout.png]]
Currently bindED supports the `en-US`, `en-GB` and `de-Neo2` layouts. [You can
find instructions on how to add your own on
Github](https://github.com/alterNERDtive/bindED/wiki/Keyboard-Layouts).
find instructions on how to add your own
here](https://alternerdtive.github.io/bindED/troubleshooting/#adding-a-keyboard-layout).

View file

@ -1,60 +1,69 @@
# Requirements
## VoiceAttack
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
and a few commands. This is 4 profiles and … a lot of commands. You will need the
full version, available for $10 (official site) or €11.99 (Steam, IIRC $14.99
for our US-based friends).
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
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
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!
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
fixes/additions in beta versions quite often.
## EDDI
[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
game as well as various third-party tools. In this case, you will need to run it
as a VoiceAttack plugin.
EDDI also regularly publishes beta versions. Unless a profiles release
explicitly states it you will _not_ have to run EDDI beta.
## bindED
[bindED](https://github.com/alterNERDtive/bindED) reads your Elite Dangerous
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
change any actions that you happen to not have the standard binds for.
This plugin is _included_ in the release package.
## Elite Scripts
I have written a [collection of Python
scripts](https://github.com/alterNERDtive/elite-scripts) to interface with
various 3ʳᵈ party services like EDSM or Spansh. Those are called by the profiles
for various tasks, like checking a systems body count.
In the future they will be replaced by VoiceAttack plugin code.
The scripts are _included_ in the release package.
## ED-NeutronRouter
(required for SpanshAttack)
[ED-NeutronRouter](https://github.com/sc-pulgan/ED-NeutronRouter) interfaces
with [Spanshs neutron plotter](https://spansh.uk/plotter) and makes the result
# Requirements
## VoiceAttack
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
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
for our US-based friends).
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
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
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!
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
fixes/additions in beta versions quite often.
## EDDI
[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
game as well as various third-party tools. In this case, you will need to run it
as a VoiceAttack plugin.
EDDI also regularly publishes beta versions. Unless a profiles release
explicitly states it you will _not_ have to run EDDI beta.
Do note that the profiles put EDDI into quiet mode by default, disabling the
built-in speech responders. This can be changed
[via the `EDDI quiet mode` setting](configuration/general.md#general-settings-for-all-profiles).
## bindED
[bindED](https://alterNERDtive.github.io/bindED) reads your Elite Dangerous
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
change any actions that you happen to not have the standard binds for.
This plugin is _included_ in the release package. You do _not_ have to download
and install it manually, but you _can_ independently update it if a newer
version is available.
## Elite Scripts
I have written a [collection of Python
scripts](https://github.com/alterNERDtive/elite-scripts) to interface with
various 3ʳᵈ party services like EDSM or Spansh. Those are called by the profiles
for various tasks, like checking a systems body count.
In the future they will be replaced by VoiceAttack plugin code.
The scripts are _included_ in the release package. You do _not_ have to download
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.
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
This will fill up gradually with Troubleshooting tips as people run into common
ones.
## VoiceAttack does not understand me / mishears me / fires random commands
There is [a thread on the VoiceAttack
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.
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
can remedy that by blocking the voice trigger. One-syllable triggers are
especially prone to misrecognition.
1. Create a new command in your custom profile.
1. Set the “when I say” field to the trigger that gets misrecognized.
Adding the “Other” → “VoiceAttack Action” → “Ignore an Unrecognized Word or
Phrase” action will also hide it from the VoiceAttack log when it is (wrongly)
recognized. You might or might not want that.
Example for the “cruise” voice trigger of the Supercruise command:
![[troubleshooting-remove-trigger.png]]
Alternatively you can raise the minimum confidence level and call the underlying
command to make misfires less likely:
![[troubleshooting-raise-min-confidence.png]]
There are a few examples in the [Custom Profile
Example](../installing#use-the-profile-example).
## 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
outlined in [[Installing#Set Elite Keyboard Binds]].
## 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 always prevent the two from speaking over each other.
You can however either disable TTS responses for events that you know will clash
(or [file a feature
request](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/) if one
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
ingame Audo settings:
![[Elite-COVAS.png]]
While youre in there you might as well get rid of the spoken FSD countdown that
is off by one second …
# Troubleshooting
This will fill up gradually with Troubleshooting tips as people run into common
ones.
## VoiceAttack does not understand me / mishears me / fires random commands
There is [a thread on the VoiceAttack
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.
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
can remedy that by blocking the voice trigger. One-syllable triggers are
especially prone to misrecognition.
1. Create a new command in your custom profile.
1. Set the “when I say” field to the trigger that gets misrecognized.
Adding the “Other” → “VoiceAttack Action” → “Ignore an Unrecognized Word or
Phrase” action will also hide it from the VoiceAttack log when it is (wrongly)
recognized. You might or might not want that.
Example for the “cruise” voice trigger of the Supercruise command:
![[troubleshooting-remove-trigger.png]]
Alternatively you can raise the minimum confidence level and call the underlying
command to make misfires less likely:
![[troubleshooting-raise-min-confidence.png]]
There are a few examples in the [Custom Profile
Example](../installing#use-the-profile-example).
## 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
outlined in [[Installing#Set Elite Keyboard Binds]].
## 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 always prevent the two from speaking over each other.
You can however either disable TTS responses for events that you know will clash
(or [file a feature
request](https://github.com/alterNERDtive/VoiceAttack-profiles/issues/) if one
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
ingame Audo settings:
![[Elite-COVAS.png]]
While youre in there you might as well get rid of the spoken FSD countdown that
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:
1. Say “download profiles update”.
1. Put the `alterNERDtive-voiceattack-profiles.vax` file from Github into the
“Import” folder.
1. Put the `alterNERDtive-voiceattack-profiles.vax` file from Github into
VoiceAttacks “Import” folder.
1. Restart VoiceAttack.
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
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
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):
```adiirc
on *:TEXT:RATSIGNAL - CMDR*(??_SIGNAL):#fuelrats:{
on *:TEXT:*RATSIGNAL*(??_SIGNAL):#fuelrats:{
if ( $away ) {
/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
will announce it using TTS.
The “replace” part handles the fact that announcements now put the system in
quotes. They have to be escaped as double quotes (`""`) to create a correct
The `$replace` part handles the fact that case announcements now put the system
in quotes. They have to be escaped as double quotes (`""`) to create a correct
command line invocation.
#### 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
repo_url: https://github.com/alterNERDtive/VoiceAttack-profiles
edit_uri: "edit/devel/docs/"
site_description: "alterNERDtive VoiceAttack profiles for Elite: Dangerous"
site_author: "alterNERDtive"
remote_name: "ssh-origin"
remote_name: "origin"
theme:
name: readthedocs
@ -43,7 +43,8 @@ nav:
- 'Issues':
#- 'VoiceAttack Tips': 'VoiceAttack.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':
- 'Twitch': 'https://twitch.tv/alterNERDtive'
- 'Youtube': 'https://www.youtube.com/channel/UC3XNZA7xWed1zM1AWOOdmog'
- 'Twitch': 'https://twitch.tv/alterNERDtive'
- 'Youtube': 'https://www.youtube.com/channel/UC3XNZA7xWed1zM1AWOOdmog'

View file

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
# Visual Studio Version 17
VisualStudioVersion = 17.3.32519.111
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceAttack-base", "plugins\VoiceAttack-base\VoiceAttack-base.csproj", "{1C05DB3F-3449-4664-B363-A379892995E5}"
EndProject
@ -16,6 +16,13 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2B4D94B-8D73-431A-880B-B1E7ADF064B2}"
ProjectSection(SolutionItems) = preProject
.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
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{5401ADF7-CB6D-448B-A4AC-D8A17B2D841F}"
@ -42,6 +49,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "configuration", "configurat
docs\configuration\StreamAttack.md = docs\configuration\StreamAttack.md
EndProjectSection
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -75,6 +94,8 @@ Global
GlobalSection(NestedProjects) = preSolution
{5401ADF7-CB6D-448B-A4AC-D8A17B2D841F} = {C2B4D94B-8D73-431A-880B-B1E7ADF064B2}
{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
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {339E6747-C7BF-43C3-99C6-9249C9849A84}

View file

@ -1,113 +1,175 @@
#nullable enable
using alterNERDtive.util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EliteAttack
{
public class EliteAttack {
private static dynamic? VA { get; set; }
private static VoiceAttackLog Log
=> log ??= new VoiceAttackLog(VA, "EliteAttack");
private static VoiceAttackLog? log;
private static VoiceAttackCommands Commands
=> commands ??= new VoiceAttackCommands(VA, Log);
private static VoiceAttackCommands? commands;
/*================\
| plugin contexts |
\================*/
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 …");
VA = vaProxy;
Log.Notice("Finished startup.");
}
/*========================================\
| required VoiceAttack plugin shenanigans |
\========================================*/
static readonly Version VERSION = new Version("8.2.1");
public static Guid VA_Id()
=> new Guid("{5B46321D-2935-4550-BEEA-36C2145547B8}");
public static string VA_DisplayName()
=> $"EliteAttack {VERSION}";
public static string VA_DisplayInfo()
=> "EliteAttack: a plugin for doing Elite-y things.";
public static void VA_Init1(dynamic vaProxy)
{
VA = vaProxy;
Log.Notice("Initializing …");
VA.SetText("EliteAttack.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;
// log
case "log.log":
Context_Log(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() { }
}
}
// <copyright file="EliteAttack.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 alterNERDtive.util;
namespace EliteAttack
{
/// <summary>
/// VoiceAttack plugin for the EliteAttack profile.
/// </summary>
public class EliteAttack
{
private static readonly Version VERSION = new ("8.5");
private static VoiceAttackLog? log;
private static VoiceAttackCommands? commands;
private static dynamic? VA { get; set; }
private static VoiceAttackLog Log => log ??= new (VA, "EliteAttack");
private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
/*========================================\
| 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 ("{5B46321D-2935-4550-BEEA-36C2145547B8}");
/// <summary>
/// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The display name.</returns>
public static string VA_DisplayName()
=> $"EliteAttack {VERSION}";
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The description.</returns>
public static string VA_DisplayInfo()
=> "EliteAttack: a plugin for doing Elite-y things.";
/// <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("EliteAttack.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 "log.log":
// log
Context_Log(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 |
\================*/
#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>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\EliteAttack.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -30,7 +30,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\EliteAttack.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<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.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.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>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\RatAttack-cli.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -35,7 +35,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\RatAttack-cli.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -50,8 +50,8 @@
<Reference Include="System.Xml.Serialization" />
</ItemGroup>
<ItemGroup>
<Compile Include="RatAttack-cli.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RatAttack_cli.cs" />
</ItemGroup>
<ItemGroup>
<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.InteropServices;

View file

@ -1,324 +1,445 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using alterNERDtive.util;
namespace RatAttack
{
public class RatAttack
{
private static Dictionary<int,RatCase> CaseList { get; } = new Dictionary<int, RatCase>();
private static dynamic? VA { get; set; }
private static alterNERDtive.util.PipeServer<Ratsignal> RatsignalPipe
=> ratsignalPipe ??= new alterNERDtive.util.PipeServer<Ratsignal>(Log, "RatAttack",
new alterNERDtive.util.PipeServer<Ratsignal>.SignalHandler(On_Ratsignal));
private static alterNERDtive.util.PipeServer<Ratsignal>? ratsignalPipe;
private static readonly Regex RatsignalRegex = new Regex(
@"^RATSIGNAL Case #(?<number>\d+) (?<platform>(PC|Xbox|Playstation))(?<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_\[\]\-]+))? \((PC|XB|PS)_SIGNAL\)\v*$"
);
private static VoiceAttackLog Log
=> log ??= new VoiceAttackLog(VA, "RatAttack");
private static VoiceAttackLog? log;
private static VoiceAttackCommands Commands
=> commands ??= new VoiceAttackCommands(VA, Log);
private static VoiceAttackCommands? commands;
private class RatCase
{
public string Cmdr;
public string? Language;
public string? System;
public string? SystemInfo;
public bool PermitLocked;
public string? PermitName;
public string Platform;
public bool CodeRed;
public int Number;
public RatCase(string cmdr, string? language, string? system, string? systemInfo, bool permitLocked, string? permitName, string platform, bool codeRed, int number)
=> (Cmdr, Language, System, SystemInfo, PermitLocked, PermitName, Platform, CodeRed, Number) = (cmdr, language, system, systemInfo, permitLocked, permitName, platform, codeRed, number);
public string ShortInfo
{
get => $"#{Number}, {Platform}{(CodeRed ? ", code red" : "")}, {System ?? "None"}{(SystemInfo != null ? $" ({SystemInfo}{(PermitLocked ? ", permit required" : "")})" : "")}";
}
public override string ToString()
=> ShortInfo;
}
public class Ratsignal : IPipable
{
public string Signal { get; set; }
public bool Announce { get; set; }
private readonly char separator = '\x1F';
public Ratsignal()
=> (Signal, Announce) = ("", false);
public Ratsignal(string signal, bool announce)
=> (Signal, Announce) = (signal, announce);
public void ParseString(string serialization)
{
try
{
string[] parts = serialization.Split(separator);
Signal = parts[0];
Announce = Boolean.Parse(parts[1]);
}
catch (Exception e)
{
throw new ArgumentException($"Invalid serialized RATSIGNAL: '{serialization}'", e);
}
}
public override string ToString()
=> $"{Signal}{separator}{Announce}";
}
private static int ParseRatsignal(string ratsignal)
{
if (!RatsignalRegex.IsMatch(ratsignal))
throw new ArgumentException($"Invalid RATSIGNAL format: '{ratsignal}'.", "ratsignal");
Match match = RatsignalRegex.Match(ratsignal);
string cmdr = match.Groups["cmdr"].Value;
string? language = match.Groups["language"].Value;
string? system = match.Groups["system"].Value;
string? systemInfo = match.Groups["systemInfo"].Value;
bool permitLocked = match.Groups["permit"].Success;
string? permitName = match.Groups["permitName"].Value;
string platform = match.Groups["platform"].Value;
bool codeRed = match.Groups["oxygen"].Value == "NOT OK";
int number = int.Parse(match.Groups["number"].Value);
Log.Debug($"New rat case: CMDR “{cmdr}” in “{system}”{(systemInfo != null ? $" ({systemInfo})" : "")} on {platform}, permit locked: {permitLocked}{(permitLocked && permitName != null ? $" (permit name: {permitName})" : "")}, code red: {codeRed} (#{number}).");
CaseList[number] = new RatCase(cmdr, language, system, systemInfo, permitLocked, permitName, platform, codeRed, number);
return number;
}
private static void On_Ratsignal(Ratsignal ratsignal)
{
try
{
int number = ParseRatsignal(ratsignal.Signal);
Log.Notice($"New rat case: {CaseList[number]}.");
Commands.TriggerEvent("RatAttack.incomingCase", parameters: new dynamic[] { new int[] { number }, new bool[] { ratsignal.Announce } });
}
catch (ArgumentException e)
{
Log.Error(e.Message);
Commands.TriggerEvent("RatAttack.invalidRatsignal", parameters: new dynamic[] { new string[] { ratsignal.Signal } });
}
catch (Exception e)
{
Log.Error($"Unhandled exception while parsing RATSIGNAL: '{e.Message}'.");
}
}
private static void On_ProfileChanged(Guid? from, Guid? to, string fromName, string toName)
=> VA_Exit1(VA);
/*================\
| plugin contexts |
\================*/
private static void Context_EDSM_GetNearestCMDR(dynamic vaProxy)
{
int caseNo = vaProxy.GetInt("~caseNo") ?? throw new ArgumentNullException("~caseNo");
string cmdrList = vaProxy.GetText("~cmdrs") ?? throw new ArgumentNullException("~cmdrs");
string[] cmdrs = cmdrList.Split(';');
if (cmdrs.Length == 0)
{
throw new ArgumentNullException("~cmdrs");
}
string system = CaseList[caseNo]?.System ?? throw new ArgumentException($"Case #{caseNo} has no system information", "~caseNo");
string path = $@"{vaProxy.SessionState["VA_SOUNDS"]}\Scripts\edsm-getnearest.exe";
string arguments = $@"--short --text --system ""{system}"" ""{string.Join(@""" """, cmdrs)}""";
Process p = PythonProxy.SetupPythonScript(path, arguments);
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
string stderr = p.StandardError.ReadToEnd();
p.WaitForExit();
string message = stdout;
string? errorMessage = null;
bool error = true;
switch (p.ExitCode)
{
case 0:
error = false;
Log.Info(message);
break;
case 1: // CMDR not found, Server Error, Api Exception (jeez, what a mess did I make there?)
error = true;
Log.Error(message);
break;
case 2: // System not found
error = true;
Log.Warn(message);
break;
default:
error = true;
Log.Error(stderr);
errorMessage = "Unrecoverable error in plugin.";
break;
}
vaProxy.SetText("~message", message);
vaProxy.SetBoolean("~error", error);
vaProxy.SetText("~errorMessage", errorMessage);
vaProxy.SetInt("~exitCode", p.ExitCode);
}
private static void Context_GetCaseData(dynamic vaProxy)
{
int cn = vaProxy.GetInt("~caseNumber");
if (CaseList.ContainsKey(cn))
{
RatCase rc = CaseList[cn];
vaProxy.SetInt("~~caseNumber", rc.Number);
vaProxy.SetText("~~cmdr", rc.Cmdr);
vaProxy.SetText("~~system", rc?.System?.ToLower());
vaProxy.SetText("~~systemInfo", rc?.SystemInfo);
vaProxy.SetBoolean("~~permitLocked", rc?.PermitLocked);
vaProxy.SetText("~~permitName", rc?.PermitName);
vaProxy.SetText("~~platform", rc?.Platform);
vaProxy.SetBoolean("~~codeRed", rc?.CodeRed);
}
else
{
Log.Warn($"Case #{cn} not found in the case list");
}
}
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 …");
VA = vaProxy;
_ = RatsignalPipe.Run();
Log.Notice("Finished startup.");
}
private static void Context_ParseRatsignal(dynamic vaProxy)
{
Log.Warn("Passing a RATSIGNAL from 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")));
}
/*========================================\
| required VoiceAttack plugin shenanigans |
\========================================*/
static readonly Version VERSION = new Version("6.2.1");
public static Guid VA_Id()
=> new Guid("{F2ADF0AE-4837-4E4A-9C87-8A7E2FA63E5F}");
public static string VA_DisplayName()
=> $"RatAttack {VERSION}";
public static string VA_DisplayInfo()
=> "RatAttack: a plugin to handle FuelRats cases.";
public static void VA_Init1(dynamic vaProxy)
{
VA = vaProxy;
Log.Notice("Initializing …");
VA.SetText("RatAttack.version", VERSION.ToString());
vaProxy.ProfileChanged += new Action<Guid?, Guid?, String, String>(On_ProfileChanged);
Log.Notice("Init successful.");
}
public static void VA_Invoke1(dynamic vaProxy)
{
string context = vaProxy.Context.ToLower();
Log.Debug($"Running context '{context}' …");
try
{
switch (context)
{
// plugin methods
case "getcasedata":
Context_GetCaseData(vaProxy);
break;
case "parseratsignal":
Context_ParseRatsignal(vaProxy);
break;
case "startup":
Context_Startup(vaProxy);
break;
// EDSM
case "edsm.getnearestcmdr":
Context_EDSM_GetNearestCMDR(vaProxy);
break;
// log
case "log.log":
Context_Log(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)
{
Log.Debug("Starting teardown …");
Log.Debug("Closing RATSIGNAL pipe …");
RatsignalPipe.Stop();
Log.Debug("Teardown finished.");
}
public static void VA_StopCommand() { }
}
}
// <copyright file="RatAttack.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.Concurrent;
using System.Diagnostics;
using System.Text.RegularExpressions;
using alterNERDtive.util;
namespace RatAttack
{
/// <summary>
/// VoiceAttack plugin for the RatAttack profile.
/// </summary>
public class RatAttack
{
private static readonly Version VERSION = new ("6.4");
private static readonly Regex RatsignalRegex = new (
@"^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*$");
private static PipeServer<Ratsignal>? ratsignalPipe;
private static VoiceAttackLog? log;
private static VoiceAttackCommands? commands;
private static ConcurrentDictionary<int, RatCase> CaseList { get; } = new ();
private static dynamic? VA { get; set; }
private static PipeServer<Ratsignal> RatsignalPipe
=> ratsignalPipe ??= new (
Log,
"RatAttack",
new PipeServer<Ratsignal>.SignalHandler(On_Ratsignal));
private static VoiceAttackLog Log => log ??= new (VA, "RatAttack");
private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
/*========================================\
| 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 ("{F2ADF0AE-4837-4E4A-9C87-8A7E2FA63E5F}");
/// <summary>
/// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The display name.</returns>
public static string VA_DisplayName()
=> $"RatAttack {VERSION}";
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The description.</returns>
public static string VA_DisplayInfo()
=> "RatAttack: a plugin to handle FuelRats cases.";
/// <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("RatAttack.version", VERSION.ToString());
vaProxy.ProfileChanged += new Action<Guid?, Guid?, string, string>(On_ProfileChanged);
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 "getcasedata":
// plugin methods
Context_GetCaseData(vaProxy);
break;
case "parseratsignal":
Context_ParseRatsignal(vaProxy);
break;
case "startup":
Context_Startup(vaProxy);
break;
case "edsm.getnearestcmdr":
// EDSM
Context_EDSM_GetNearestCMDR(vaProxy);
break;
case "log.log":
// log
Context_Log(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)
{
Log.Debug("Starting teardown …");
Log.Debug("Closing RATSIGNAL pipe …");
RatsignalPipe.Stop();
Log.Debug("Teardown finished.");
}
/// <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()
{
}
/// <summary>
/// Parses a RATSIGNAL and extracts case data for storage.
/// </summary>
/// <param name="ratsignal">The incoming RATSIGNAL.</param>
/// <returns>The case number.</returns>
/// <exception cref="ArgumentException">Thrown on invalid RATSIGNAL.</exception>
private static int ParseRatsignal(string ratsignal)
{
if (!RatsignalRegex.IsMatch(ratsignal))
{
throw new ArgumentException($"Invalid RATSIGNAL format: '{ratsignal}'.", "ratsignal");
}
Match match = RatsignalRegex.Match(ratsignal);
string cmdr = match.Groups["cmdr"].Value;
string? language = match.Groups["language"].Value;
string? system = match.Groups["system"].Value;
string? systemInfo = match.Groups["systemInfo"].Value;
bool permitLocked = match.Groups["permit"].Success;
string? permitName = match.Groups["permitName"].Value;
string platform = match.Groups["platform"].Value;
bool codeRed = match.Groups["oxygen"].Success;
string? mode = match.Groups["mode"].Value;
int number = int.Parse(match.Groups["number"].Value);
if (string.IsNullOrEmpty(system))
{
system = "None";
}
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}).");
CaseList[number] = new RatCase(cmdr, language, system, systemInfo, permitLocked, permitName, platform, mode, codeRed, number);
return number;
}
private static void On_Ratsignal(Ratsignal ratsignal)
{
try
{
int number = ParseRatsignal(ratsignal.Signal);
Log.Notice($"New rat case: {CaseList[number]}.");
Commands.TriggerEvent("RatAttack.incomingCase", parameters: new dynamic[] { new int[] { number }, new bool[] { ratsignal.Announce } });
}
catch (ArgumentException e)
{
Log.Error(e.Message);
Commands.TriggerEvent("RatAttack.invalidRatsignal", parameters: new dynamic[] { new string[] { ratsignal.Signal } });
}
catch (Exception e)
{
Log.Error($"Unhandled exception while parsing RATSIGNAL: '{e.Message}'.");
}
}
private static void On_ProfileChanged(Guid? from, Guid? to, string fromName, string toName)
=> VA_Exit1(VA);
/*================\
| plugin contexts |
\================*/
#pragma warning disable IDE0060 // Remove unused parameter
private static void Context_EDSM_GetNearestCMDR(dynamic vaProxy)
{
int caseNo = vaProxy.GetInt("~caseNo") ?? throw new ArgumentNullException("~caseNo");
string cmdrList = vaProxy.GetText("~cmdrs") ?? throw new ArgumentNullException("~cmdrs");
string[] cmdrs = cmdrList.Split(';');
if (cmdrs.Length == 0)
{
throw new ArgumentNullException("~cmdrs");
}
string system = CaseList[caseNo]?.System ?? throw new ArgumentException($"Case #{caseNo} has no system information", "~caseNo");
string path = $@"{vaProxy.SessionState["VA_SOUNDS"]}\Scripts\edsm-getnearest.exe";
string arguments = $@"--short --text --system ""{system}"" ""{string.Join(@""" """, cmdrs)}""";
Process p = PythonProxy.SetupPythonScript(path, arguments);
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
string stderr = p.StandardError.ReadToEnd();
p.WaitForExit();
string message = stdout;
string? errorMessage = null;
bool error;
switch (p.ExitCode)
{
case 0:
error = false;
Log.Info(message);
break;
case 1: // CMDR not found, Server Error, Api Exception (jeez, what a mess did I make there?)
error = true;
Log.Error(message);
break;
case 2: // System not found
error = true;
Log.Warn(message);
break;
default:
error = true;
Log.Error(stderr);
errorMessage = "Unrecoverable error in plugin.";
break;
}
vaProxy.SetText("~message", message);
vaProxy.SetBoolean("~error", error);
vaProxy.SetText("~errorMessage", errorMessage);
vaProxy.SetInt("~exitCode", p.ExitCode);
}
private static void Context_GetCaseData(dynamic vaProxy)
{
int cn = vaProxy.GetInt("~caseNumber");
if (CaseList.ContainsKey(cn))
{
RatCase rc = CaseList[cn];
vaProxy.SetInt("~~caseNumber", rc.Number);
vaProxy.SetText("~~cmdr", rc.Cmdr);
vaProxy.SetText("~~system", rc?.System?.ToLower());
vaProxy.SetText("~~systemInfo", rc?.SystemInfo);
vaProxy.SetBoolean("~~permitLocked", rc?.PermitLocked);
vaProxy.SetText("~~permitName", rc?.PermitName);
vaProxy.SetText("~~platform", rc?.Platform);
vaProxy.SetText("~~mode", rc?.Mode);
vaProxy.SetBoolean("~~codeRed", rc?.CodeRed);
}
else
{
Log.Warn($"Case #{cn} not found in the case list");
}
}
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 …");
_ = 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>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\RatAttack.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -34,7 +34,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\RatAttack.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<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.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.Collections.Generic;
using System.Diagnostics;
using alterNERDtive.edts;
using alterNERDtive.util;
namespace SpanshAttack
{
/// <summary>
/// VoiceAttack plugin for the SpanshAttack profile.
/// </summary>
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 VoiceAttackLog Log
=> log ??= new VoiceAttackLog(VA, "SpanshAttack");
private static VoiceAttackLog? log;
private static VoiceAttackLog Log => log ??= new (VA, "SpanshAttack");
private static VoiceAttackCommands Commands
=> commands ??= new VoiceAttackCommands(VA, Log);
private static VoiceAttackCommands? commands;
private static VoiceAttackCommands Commands => commands ??= new (VA, Log);
/*========================================\
| 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 |
\================*/
#pragma warning disable IDE0060 // Remove unused parameter
private static void Context_EDTS_GetCoordinates(dynamic vaProxy)
{
string name = vaProxy.GetText("~system") ?? throw new ArgumentNullException("~system");
@ -51,11 +180,13 @@ namespace SpanshAttack
vaProxy.SetInt("~precision", system.Coords.Precision);
success = true;
} catch (ArgumentException e)
}
catch (ArgumentException e)
{
errorType = "invalid name";
errorMessage = e.Message;
} catch (Exception e)
}
catch (Exception e)
{
errorType = "connection error";
errorMessage = e.Message;
@ -85,7 +216,10 @@ namespace SpanshAttack
{
Log.Log(message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper()));
}
catch (ArgumentNullException) { throw; }
catch (ArgumentNullException)
{
throw;
}
catch (ArgumentException)
{
Log.Error($"Invalid log level '{level}'.");
@ -104,11 +238,11 @@ namespace SpanshAttack
Process p = PythonProxy.SetupPythonScript(path, arguments);
Dictionary<char, decimal> coords = new Dictionary<char, decimal> { { 'x', 0 }, { 'y', 0 }, { 'z', 0 } };
string system = "";
Dictionary<char, decimal> coords = new () { { 'x', 0 }, { 'y', 0 }, { 'z', 0 } };
string system = string.Empty;
decimal distance = 0;
bool error = false;
string errorMessage = "";
string errorMessage = string.Empty;
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
@ -159,7 +293,7 @@ namespace SpanshAttack
bool exists = true;
bool error = false;
string errorMessage = "";
string errorMessage = string.Empty;
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
@ -195,75 +329,8 @@ namespace SpanshAttack
private static void Context_Startup(dynamic vaProxy)
{
Log.Notice("Starting up …");
VA = vaProxy;
Log.Notice("Finished startup.");
}
/*========================================\
| 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() { }
#pragma warning restore IDE0060 // Remove unused parameter
}
}

View file

@ -21,7 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\SpanshAttack.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -30,7 +30,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\SpanshAttack.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<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.InteropServices;
@ -10,7 +29,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VoiceAttack-base")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyCopyright("Copyright © 20202022")]
[assembly: AssemblyTrademark("")]
[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>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\base.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -33,33 +33,50 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
<DocumentationFile>..\build\alterNERDtive\base.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<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.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<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="SettingsDialog.xaml.cs">
<DependentUpon>SettingsDialog.xaml</DependentUpon>
</Compile>
<Compile Include="util.cs" />
</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>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</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.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
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 } };
}
}
}
}

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"
}
}
}
}