mirror of
https://github.com/TeamMidnightDust/MidnightControls.git
synced 2025-12-13 07:15:10 +01:00
Compare commits
169 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e861470d8 | ||
|
|
e87ed53485 | ||
|
|
8847c7f499 | ||
|
|
da2773b1e9 | ||
|
|
3650e756c3 | ||
|
|
6b885beede | ||
|
|
abb3d1de3c | ||
|
|
a8cdfeeba9 | ||
|
|
ed4d310565 | ||
|
|
ac73c99506 | ||
|
|
d45eeac6dc | ||
|
|
77ff589544 | ||
|
|
ce5c0b22db | ||
|
|
98af13fd0b | ||
|
|
2387444d3a | ||
|
|
57860490c8 | ||
|
|
f46aa175a4 | ||
|
|
78390ca02f | ||
|
|
31821b4833 | ||
|
|
e34ba6025a | ||
|
|
9891cd2977 | ||
|
|
8566e859e3 | ||
|
|
735b9e8718 | ||
|
|
71ee3d8e0a | ||
|
|
11f102759c | ||
|
|
02dd543027 | ||
|
|
c247e51d00 | ||
|
|
e3052343d3 | ||
|
|
9e879f9230 | ||
|
|
bda436d863 | ||
|
|
98d2cdae63 | ||
|
|
8e0e444d15 | ||
|
|
f586647e07 | ||
|
|
73ab8456e7 | ||
|
|
1a3d7ce225 | ||
|
|
965ab57159 | ||
|
|
c8dba43b90 | ||
|
|
dd8e62c73e | ||
|
|
b24a158b9b | ||
|
|
df7c7bbdad | ||
|
|
3ddae57eab | ||
|
|
a0701f55f6 | ||
|
|
b15766dfdb | ||
|
|
19ed068da9 | ||
|
|
d2e21f8723 | ||
|
|
b3f7350de9 | ||
|
|
34408d7a2a | ||
|
|
6e64c7c97d | ||
|
|
bca73c93cc | ||
|
|
913e79fe5b | ||
|
|
b1334a0455 | ||
|
|
ce9495c79c | ||
|
|
ff8270d18a | ||
|
|
3f33549760 | ||
|
|
d034607032 | ||
|
|
e9a11c0ab9 | ||
|
|
e0d90fc56a | ||
|
|
cac23d12a1 | ||
|
|
b982772d31 | ||
|
|
e4ca0db2a2 | ||
|
|
f0cd2a51d1 | ||
|
|
9d37862144 | ||
|
|
c31fb76215 | ||
|
|
b6563af2b1 | ||
|
|
1a0030a680 | ||
|
|
78c858b753 | ||
|
|
24288b3ebd | ||
|
|
0226bfc62f | ||
|
|
28d92a65d4 | ||
|
|
132bdc0cc5 | ||
|
|
bdffef681f | ||
|
|
a49a64ef44 | ||
|
|
0e192b2af4 | ||
|
|
6dd654f2f5 | ||
|
|
eb406a4681 | ||
|
|
d386e3d9a2 | ||
|
|
5fcf4c2b1b | ||
|
|
bfaa4f5d9a | ||
|
|
5d5d808403 | ||
|
|
d08647b3fc | ||
|
|
1039ea5344 | ||
|
|
bb19ea4da5 | ||
|
|
a1773cc0d9 | ||
|
|
e652390583 | ||
|
|
f62819d9a4 | ||
|
|
978b94176c | ||
|
|
c8989cb9ee | ||
|
|
97521e832d | ||
|
|
400b625716 | ||
|
|
f89eebf0e7 | ||
|
|
cc0a9aebe2 | ||
|
|
b8ec934c10 | ||
|
|
ac8fab83a2 | ||
|
|
1489b796a8 | ||
|
|
ab8b78f75a | ||
|
|
c7026ba1fd | ||
|
|
0532913d69 | ||
|
|
597700b2bc | ||
|
|
4edb0952d8 | ||
|
|
178f19dc2c | ||
|
|
129c3d0f3b | ||
|
|
deccb758ea | ||
|
|
b7c60f8651 | ||
|
|
1ee75bc4e5 | ||
|
|
1f0ddab36b | ||
|
|
8a919934e2 | ||
|
|
0178f5e684 | ||
|
|
c495e2ae0c | ||
|
|
3ecce09775 | ||
|
|
e94d2eb240 | ||
|
|
7571a2aa1b | ||
|
|
d09a225518 | ||
|
|
4290d79bd0 | ||
|
|
f9f0a7a18d | ||
|
|
f889fc6367 | ||
|
|
7dcbda7e57 | ||
|
|
bd065f7b5b | ||
|
|
2785d634dc | ||
|
|
257f01ec19 | ||
|
|
127c44a046 | ||
|
|
272ef6b1dd | ||
|
|
155278130f | ||
|
|
3f221feb26 | ||
|
|
7cfd8c9b77 | ||
|
|
2c53575d17 | ||
|
|
24f7054eff | ||
|
|
4669e446dc | ||
|
|
ff672f05a1 | ||
|
|
756e7d102d | ||
|
|
e676a37c7f | ||
|
|
45dd94fd34 | ||
|
|
f8cc1fcc20 | ||
|
|
d56126c680 | ||
|
|
5a2fad4445 | ||
|
|
0eadc054b6 | ||
|
|
07a296603a | ||
|
|
8e082404f9 | ||
|
|
8efddb24b0 | ||
|
|
581757aaee | ||
|
|
40dc8d424c | ||
|
|
230a9f6424 | ||
|
|
ba01df036b | ||
|
|
ff90d55351 | ||
|
|
efc2d6284d | ||
|
|
9e2c4720e9 | ||
|
|
8063116820 | ||
|
|
0050b0216c | ||
|
|
e13569b71d | ||
|
|
47b0cfdad4 | ||
|
|
f56eb9e0ad | ||
|
|
bc712f138b | ||
|
|
66721b00b9 | ||
|
|
e0762b16ee | ||
|
|
7bfff00841 | ||
|
|
a8c969b176 | ||
|
|
f1607fe1bf | ||
|
|
d52a630cb4 | ||
|
|
502e9ff15e | ||
|
|
32e4ad1b7c | ||
|
|
4ae6404ad2 | ||
|
|
185d5e4303 | ||
|
|
0d02e56134 | ||
|
|
738d094fb1 | ||
|
|
7c06afb4a4 | ||
|
|
647e03134f | ||
|
|
1b5a0e470e | ||
|
|
e6da7adf89 | ||
|
|
46d43c6f46 | ||
|
|
9ae1bc2a3b |
23
.github/workflows/gradlebuild.yml
vendored
Executable file
23
.github/workflows/gradlebuild.yml
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
name: Gradle Build
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 16
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 16
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Artifacts
|
||||
path: ./build/libs/
|
||||
40
.github/workflows/gradlepublish.yml
vendored
Executable file
40
.github/workflows/gradlepublish.yml
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
name: Gradle Package
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 16
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 16
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Artifacts
|
||||
path: ./build/libs/
|
||||
|
||||
# The USERNAME and PASSWORD need to correspond to the credentials environment variables used in
|
||||
# the publishing section of your build.gradle
|
||||
- name: Publish to GitHub Packages and other Mavens
|
||||
run: ./gradlew publish
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.ref }}
|
||||
RUN_COUNT: ${{ github.run_number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
USERNAME: ${{ github.actor }}
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
midnightcontrols_MAVEN: ${{ secrets.MAVEN_URL }}
|
||||
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
23
.github/workflows/modrinth_update.yml
vendored
Executable file
23
.github/workflows/modrinth_update.yml
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
name: Gradle Build
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 16
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 16
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
run: ./gradlew publishModrinth
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,11 +1,13 @@
|
||||
#
|
||||
# LambdAurora's ignore file
|
||||
#
|
||||
# v0.12
|
||||
# v0.15
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
## Intellij IDEA
|
||||
out/
|
||||
## CLion
|
||||
@@ -51,6 +53,8 @@ __pycache__/
|
||||
# OS
|
||||
## Windows
|
||||
desktop.ini
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
# File types
|
||||
*.dll
|
||||
@@ -59,10 +63,13 @@ desktop.ini
|
||||
*.lib
|
||||
lib*.a
|
||||
*.png~
|
||||
*.tar.?z
|
||||
|
||||
# Common
|
||||
bin/
|
||||
build/
|
||||
dist/
|
||||
lib/
|
||||
obj/
|
||||
run/
|
||||
target/
|
||||
|
||||
151
CHANGELOG.md
Normal file
151
CHANGELOG.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Changelog
|
||||
|
||||
# MidnightControls
|
||||
|
||||
## 0.1.0
|
||||
First beta!
|
||||
Changes from LambdaControls:
|
||||
|
||||
- Support for Steam Deck and Dualsense
|
||||
- Support for L4, L5, R4, R5 buttons
|
||||
- Updated Libraries
|
||||
- New Logo and Name
|
||||
- Lots of Bugfixes
|
||||
- MidnightConfig backend
|
||||
|
||||
|
||||
# LambdaControls
|
||||
|
||||
## 1.0.0
|
||||
|
||||
:tada: First release! :tada:
|
||||
|
||||
- Added controller support.
|
||||
- Added new controls settings GUI.
|
||||
- Added experimental touchscreen support.
|
||||
- Added controller controls GUI.
|
||||
- Added a lot of options.
|
||||
- Added key bindings for look around.
|
||||
- And more!
|
||||
|
||||
### 1.0.1
|
||||
|
||||
- Fixed tutorial toast to look around not affected by camera movement done with a controller. ([#2](https://github.com/LambdAurora/LambdaControls/issues/2))
|
||||
|
||||
### 1.0.2 (Unofficial)
|
||||
|
||||
This update was never pushed but was aiming to fix [#4](https://github.com/LambdAurora/LambdaControls/issues/4).
|
||||
|
||||
- Fixed the toggle sneak button binding.
|
||||
- Fixed broken chat arrow keys.
|
||||
- Optimized a little bit the button indicator. (need more work)
|
||||
|
||||
## 1.1.0 - Chording update
|
||||
|
||||
This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAurora/LambdaControls/issues/9)).
|
||||
|
||||
- Rewrote everything (almost).
|
||||
- Added [networking](https://github.com/LambdAurora/LambdaControls/wiki/LambdaControls-Networking) for some features.
|
||||
- Added second controller support (Joycons supported now hopefully).
|
||||
- Added chording.
|
||||
- Added better developer API
|
||||
- Added hover messages ([#5](https://github.com/LambdAurora/LambdaControls/issues/5)).
|
||||
- Added hotbar button bindings ([#7](https://github.com/LambdAurora/LambdaControls/issues/7)).
|
||||
- Added front block placing feature ([#8](https://github.com/LambdAurora/LambdaControls/issues/8)).
|
||||
- Added no creative fly drifting ([#8](https://github.com/LambdAurora/LambdaControls/issues/8)).
|
||||
- Added option to enable controller focus.
|
||||
- Added [OkZoomer](https://github.com/joaoh1/OkZoomer) compatibility.
|
||||
- Added D-pad movements in inventories.
|
||||
- Increased max speed ranges.
|
||||
- Added [SpruceUI](https://github.com/LambdAurora/SpruceUI) for cleaner custom UI widgets.
|
||||
- Added reset settings button.
|
||||
- HUD side affects button indicators now.
|
||||
- Added support for Advancements tabs.
|
||||
|
||||
### 1.1.1
|
||||
|
||||
## 1.2.0-1.3.0
|
||||
|
||||
- Improved rotation algorithm ([#11](https://github.com/LambdAurora/LambdaControls/issues/11)).
|
||||
- Added virtual mouse.
|
||||
- Added outline on front block placing.
|
||||
- Added fast block placement ([#8](https://github.com/LambdAurora/LambdaControls/issues/8)).
|
||||
- Added [REI](https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items) compatibility.
|
||||
- Improved HUD.
|
||||
- Added recipe book control.
|
||||
- And more!
|
||||
- v1.3.0 specific: Updated to Minecraft 1.16.1
|
||||
|
||||
### 1.3.1
|
||||
|
||||
- Fixed broken inventory interactions ([#13](https://github.com/LambdAurora/LambdaControls/issues/13))
|
||||
- Fixed virtual mouse preventing continuous attack (thus making breaking blocks impossible).
|
||||
- Added support for [ModUpdater](https://gitea.thebrokenrail.com/TheBrokenRail/ModUpdater) hopefully.
|
||||
- Updated [SpruceUI](https://github.com/LambdAurora/SpruceUI) to 1.5.2.
|
||||
|
||||
### 1.3.2
|
||||
|
||||
- Added vertical reacharound.
|
||||
- Added more API for compatibility handlers.
|
||||
- Improved reacharound API.
|
||||
- Improved [REI](https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items) compatibility.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- Added analog movements ([#10](https://github.com/LambdAurora/LambdaControls/issues/10)).
|
||||
- Improved Ok Zoomer compability.
|
||||
- Updated [SpruceUI](https://github.com/LambdAurora/SpruceUI) to 1.5.8 to ensure 1.16.2 compability.
|
||||
- Internal changes:
|
||||
- Added analog input value to button bindings.
|
||||
- Replace lot of strings with Texts.
|
||||
- Improved block outline rendering injection.
|
||||
- Shadow library jars instead of Jar-in-Jar.
|
||||
- Fixed crash in inventory ([#16](https://github.com/LambdAurora/LambdaControls/issues/16))
|
||||
- WIP:
|
||||
- Started to work on action ring.
|
||||
- Will allow for better compability with other mods.
|
||||
- Might be interesting for keyboard users too.
|
||||
|
||||
### 1.4.1
|
||||
|
||||
- Fixed crash with [REI](https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items).
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- Added mappings string editor screen.
|
||||
- Added Simplified Chinese translations ([#18](https://github.com/LambdAurora/LambdaControls/pull/18)).
|
||||
- Added Mexican Spanish translations ([#22](https://github.com/LambdAurora/LambdaControls/pull/22)).
|
||||
- Added Xbox 360 button skin and overhauled Xbox button skin.
|
||||
- Added debug option.
|
||||
- Respect toggle setting in Accessibility screen.
|
||||
- Tweaked rotation speeds.
|
||||
- Updated to Minecraft 1.16.2.
|
||||
- Updated [SpruceUI] to 1.6.4.
|
||||
- Overhauled REI compatibility.
|
||||
- Improved horizontal reach-around.
|
||||
- Fixed crashes with Ok Zoomer.
|
||||
- Fixed crashes with key unbinding.
|
||||
- More WIP on keybind ring.
|
||||
|
||||
## 1.6.0
|
||||
|
||||
- Reworked entirely the settings screen.
|
||||
- Added independent stick dead zones. ([#32](https://github.com/LambdAurora/LambdaControls/issues/32))
|
||||
- Added max values range. ([#41](https://github.com/LambdAurora/LambdaControls/issues/41))
|
||||
- Updated [SpruceUI] and fix related crashes due to incompatible versions ([#40](https://github.com/LambdAurora/LambdaControls/issues/40), [#48](https://github.com/LambdAurora/LambdaControls/issues/48)).
|
||||
- Fix boat control issues ([#37](https://github.com/LambdAurora/LambdaControls/issues/37)).
|
||||
- Fix incompatibilities with mods using night-config. Now shadowing properly night-config. ([#33](https://github.com/LambdAurora/LambdaControls/issues/33), [#39](https://github.com/LambdAurora/LambdaControls/issues/39))
|
||||
|
||||
## 1.7.0
|
||||
|
||||
- Updated to 1.17.
|
||||
- Small improvements to the codebase thanks to Java 16.
|
||||
- Fix controller bindings not being saved ([#31](https://github.com/LambdAurora/LambdaControls/issues/31), [#55](https://github.com/LambdAurora/LambdaControls/issues/55)).
|
||||
- Dropped entirely Touchscreen Input Mode.
|
||||
- Dropped Roughly Enough Items compatibility.
|
||||
|
||||
### 1.7.1
|
||||
|
||||
- Fix crash at startup.
|
||||
|
||||
[SpruceUI]: https://github.com/LambdAurora/SpruceUI
|
||||
@@ -1,8 +1,8 @@
|
||||
# Contributing to LambdaControls
|
||||
# Contributing to midnightcontrols
|
||||
|
||||
:tada: First of all, thanks for taking time to contribute! :tada:
|
||||
|
||||
The following is a set of guidelines for contributing to LambdaControls.
|
||||
The following is a set of guidelines for contributing to MidnightControls.
|
||||
Feel free to propose changes to this document in a pull request.
|
||||
|
||||
**Table of Contents**
|
||||
@@ -17,8 +17,8 @@ Feel free to propose changes to this document in a pull request.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/LambdAurora/LambdaControls/blob/master/CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior at [aurora42lambda@gmail.com](mailto:aurora42lambda@gmail.com).
|
||||
This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/LambdAurora/midnightcontrols/blob/master/CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior at [motschen@midnightdust.eu](mailto:motschen@midnightdust.eu).
|
||||
|
||||
## What should I know before I get started?
|
||||
|
||||
@@ -26,9 +26,9 @@ By participating, you are expected to uphold this code. Please report unacceptab
|
||||
|
||||
[Fabric](https://fabricmc.net/) is the mod loader and the software which allows Gradle to setup the workspace.
|
||||
|
||||
### Java 8
|
||||
### Java 17
|
||||
|
||||
Java is the main language used to make LambdaControls alive.
|
||||
Java is the main language used to make MidnightControls alive.
|
||||
Knowing how to code in Java is necessary if you contribute to the code.
|
||||
|
||||
### Minecraft
|
||||
@@ -45,9 +45,9 @@ As it is a Minecraft mod you should know a bit how Minecraft works and how moddi
|
||||
|
||||
### Git
|
||||
|
||||
Git is the control version software we use for LambdaControls, please know how to use it if you consider contributing to the code.
|
||||
Git is the control version software we use for midnightcontrols, please know how to use it if you consider contributing to the code.
|
||||
|
||||
Git commits should be and must be signed.
|
||||
Git commits should be signed.
|
||||
|
||||
## How can I contribute?
|
||||
|
||||
@@ -55,21 +55,21 @@ Git commits should be and must be signed.
|
||||
|
||||
#### Before submitting a bug report
|
||||
|
||||
- Check if you can reproduce it on other platforms, on multiple web browsers.
|
||||
- Check if you can reproduce it on other platforms.
|
||||
- Perform a search to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
|
||||
|
||||
#### How do I submit a bug report?
|
||||
|
||||
Go in the issues tab in GitHub and read the [bug report guide](https://github.com/LambdAurora/LambdaControls/blob/master/.github/ISSUE_TEMPLATE/bug_report.md)
|
||||
Go in the issues tab in GitHub and read the [bug report guide](https://github.com/TeamMidnightDust/MidnightControls/blob/1.18/.github/ISSUE_TEMPLATE/bug_report.md)
|
||||
|
||||
### Suggesting enhancements
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/LambdAurora/LambdaControls/issues).
|
||||
Check out the [feature request](https://github.com/LambdAurora/LambdaControls/blob/master/.github/ISSUE_TEMPLATE/feature_request.md) guide.
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/TeamMidnightDust/MidnightControls/issues).
|
||||
Check out the [feature request](https://github.com/TeamMidnightDust/MidnightControls/blob/1.17/.github/ISSUE_TEMPLATE/feature_request.md) guide.
|
||||
|
||||
### Do pull requests
|
||||
|
||||
You can help LambdaControls by writing code and submit it with pull requests.
|
||||
You can help midnightcontrols by writing code and submit it with pull requests.
|
||||
|
||||
Pull requests will be accepted if they follow the [styleguide](#styleguides), if they are useful, etc...
|
||||
We can refuse a pull request if the commits are not signed, so don't forget to [sign them](https://help.github.com/en/articles/signing-commits)!
|
||||
@@ -81,21 +81,16 @@ Feel free to pull request!
|
||||
### Git commit messages
|
||||
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Consider starting the commit message with an emote, emotes for your commit can be found at the [gitmoji guide](https://gitmoji.carloscuesta.me/).
|
||||
* (Not for the message) Don't forget to sign the commit.
|
||||
|
||||
### Naming convention
|
||||
|
||||
Names in the code should be explicit and always in `snake_case`, `camelCase` will not be allowed.
|
||||
Names in the code should be explicit and always in `camelCase`, `snake_case` will not be allowed.
|
||||
`PascalCase` can be used for class name.
|
||||
|
||||
We chose `snake_case` because it is more accessible for everyone: for people who don't speak English as their native language it is more easy to see the words when they are separated,
|
||||
it also allows the correct use of screen reader on the code with `snake_case` due to the absence of upper case characters.
|
||||
|
||||
### Brace placement
|
||||
|
||||
Every braces should be at the end of the line of function declaration, etc...
|
||||
The only exception is class declarations: braces must be on the next line.
|
||||
Every braces should be at the end of the line of function declaration, etc.
|
||||
|
||||
### Quick note for users of the Intellij IDEA IDE
|
||||
|
||||
|
||||
7
HEADER
Normal file
7
HEADER
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
Copyright © 2022 Motschen <motschen@midnightdust.eu>
|
||||
|
||||
This file is part of MidnightControls.
|
||||
|
||||
Licensed under the MIT license. For more information,
|
||||
see the LICENSE file.
|
||||
5
LICENSE
5
LICENSE
@@ -1,6 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
Copyright © 2022 Motschen <motschen@midnightdust.eu>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
||||
73
README.md
73
README.md
@@ -1,8 +1,69 @@
|
||||
# LambdaControls
|
||||
<div align="center">
|
||||
<img src=https://github.com/TeamMidnightDust/MidnightControls/blob/1.19/src/main/resources/assets/midnightcontrols/icon.png>
|
||||
|
||||

|
||||
[](https://raw.githubusercontent.com/LambdAurora/LambdaControls/master/LICENSE)
|
||||

|
||||
# MidnightControls
|
||||
|
||||
A Fabric Minecraft mod which add better controls.
|
||||
It allows the use of a controller or the touchscreen.
|
||||
[](https://www.curseforge.com/minecraft/mc-mods/midnightcontrols)
|
||||
[![Modrinth]](https://modrinth.com/mod/midnightcontrols)
|
||||
[](https://github.com/TeamMidnightDust/MidnightControls/releases)
|
||||

|
||||
[![Mod loader: Quilt/Fabric]][Quilt]
|
||||
[](https://www.oracle.com/java/technologies/downloads/#java17)
|
||||
[](LICENSE)
|
||||
|
||||
A Fabric Minecraft mod which adds better controls, reach-around and controller support.
|
||||
Forked from [LambdaControls](https://github.com/LambdAurora/LambdaControls) by the amazing [LambdAurora](https://github.com/LambdAurora), which was sadly discontinued.
|
||||
|
||||
</div>
|
||||
|
||||
## What's this mod?
|
||||
|
||||
MidnightControls is a mod which provides controller and touchscreen support in Minecraft: Java Edition.<br />It also includes some Bedrock Edition parity and reach-around features!
|
||||
|
||||
## Features:
|
||||
|
||||
- Controller support
|
||||
- Touchscreen support
|
||||
- Keyboard controls to look around.
|
||||
- Toggleable on screen button indicator (like in Bedrock Edition).
|
||||
- Vertical reach-around.
|
||||
- Many Bedrock Edition features:
|
||||
- Toggleable fly drifting
|
||||
- Front block placing (be careful with this one)
|
||||
- New controls settings!
|
||||
- Many options in config to change to your liking.
|
||||
- Many controllers supported and in a simple way your own controller mappings.
|
||||
- An easy API for developers to add their own button bindings.
|
||||
|
||||
## 🎮 Supported Controllers:
|
||||
|
||||
- DualShock controllers
|
||||
- DualSense controllers
|
||||
- Xbox controllers
|
||||
- Switch Pro controllers
|
||||
- Joy-Cons
|
||||
- Steam controller and Steam Deck (WIP)
|
||||
- And many more!
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
## Build
|
||||
|
||||
Clone the repo with:
|
||||
```
|
||||
git clone https://github.com/TeamMidnightDust/MidnightControls
|
||||
```
|
||||
Enter the folder created and run:
|
||||
```
|
||||
./gradlew build
|
||||
```
|
||||
After this is done, everything should be built just fine!
|
||||
|
||||
[Quilt]: https://quiltmc.org
|
||||
|
||||
[Mod loader: Quilt/Fabric]: https://img.shields.io/badge/modloader-Quilt%2FFabric-blueviolet?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAADGAAAAxgGwdJvFAAAFU0lEQVR4nO1bwW7bOBCdCHtP+wUNxIvhS7yA7kkPOjf9AKPam4/OF2zzB96bblXhH0jPPkS5G1jnIvgib/IFtX/ALoYZemmKtEWRRe3aDzAsURbJGQ6HM4/02Wq1gmNGcNTSnxQA8IdakDK4AOCfJpj3SpiI98r2rAMAbxrWNWFFOMeL1nCJdXQa1gPTbpCbnm34ABIeBThv2hgAfOyVcF+2ZwkAfHGo54kVIRe6NVxiny4d6vpn2g36ugfqFLhwFB6kkUoc67k0XLv0qYKj9wEnBXiqZwEAf/ZKOOuV8BkLWBFesyI8A4C/LOt6AoC39C7HtBvg9Vt65hUnC/BUDzrOf1MGq5S9WkDZnuVle7ZqsBKgw/tO73K0hku8/u64EmhxsoA96MMvxdEroBIKO+CWoshnqqJPYTAGRJ8sql3QO3Op7D19D3z7AV8KeAGArFf+32lWhDwnKNuzuaUCclaE93KBiOU9hMQV+FLAOxz9lPHRR0VkZXs2oBDUNon5ULZnKOgcYwl4FTwna/K+CtRRwIK+dTnCCwkP9I0fkXmh4FcN+6UKqqtHblvtb+18po4TvKaPilsS8qVuYx5havsbJXS1I0ZVAXP1B5jfyzm+hAnN+We1XPluioX03kJtY9oNtG1T+b1aburDxhRAQVMGHw3z9k65F41nktmjUnjjrAj7ZXv27ECIyEKgBd7saBuka7lsPu0GA1MjR0+KVpwgMTlrMkN4YoztlZ/2camTvD0QjcWZF6KxMgcLyKbdAN+HKOb1y6PYH49gEsUwUKw1G48gi2KQZcApkYxH1eldUQBxeKbkRfXEQjDZ21+h2bMiHJDwH2wk1rSX0XWueHZd2yCZ/oVSnilTaA3VAiqjRRahQ0JBjuov3ijfPqAua0kUg67tmyjWCmvsS504wGQRnywjPJ8wtY3xw3827fjMBXziUalL3DcNrIzYRwXcTbvBZ7lgPHoNxKKYky1/+2xsHxXQbw2X/EIoggQHyjC9Yh8VcC6NshDc66jLOHpC5KSAPejDL8U+KuArboTQZgjHeARn+MFnvhvbRyfYaQ2XfNkTVFgUr/mIxlvkJuyjAjCae6BrYQUPW37vhDpT4MnAsCyIgbHBnYbcADJt7/t+deBCiSWsCG8sOv5IgU2FnJh2g+RnBDl1oCpgogqEx1TEURUFokx+tpCYnEzzjg1kh+fq/Ix9OXpG6OjjgI1VIGWcOMhlXh4PPdAz1VTe90rIU8Z/L9JUnALXSK4SjSUzOY+Y1ekyOlzjaamTvf3X8cjunBH1v6OeVtsG1QI66qYEVkoVmyA/O5fYmIHjgSsrskUavAc6q1BLeXXiANMZu0HKwGav7opGv+LticZqHOToLBeZrJRxC97qjOsowCTgZYO9OlNa25haMwgvsFMJPp3gLW1ji8b6dO89fhfYIbzAl23T4adtjyNvD6/mbbs9Xgs1hRcwWoIvCxDb47nQNm5aRDHvoPGcblNYCi+gtQSfU+AdLYfioLXYtHA9eruBhsILVJRwUIGQo/ACG0o4GAV4El5grQSffMA3SqbEnBdb1zeunfYsvAB3jL4UwE92KauA2NnNbLerNKhEqJ6Q+FIAOjpcATAVzjFHoC3qC9OurCW0W9s+6vXpAy4p0hPkSUL3ziNHiY3tqfNdQN4jORgnSEGMLyU8Udbq1QIEdEyRF3hSwlp4vKkwQimrHDuxAR6S4tleFIPzERnhSFXQEtbkD1kbwiMOlhJroISK8IiDpcQsp4NWeMRBc4I1lWAUHnHwpOgOJWwVHvFbsMIGJewUHvFb7QuQYxR/ttgIzbUAgB9KOM3uc+PN7QAAAABJRU5ErkJggg==
|
||||
|
||||
[Modrinth]: https://img.shields.io/modrinth/dt/bXX9h73M?logoColor=white&logo=data:image/svg%2bxml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTEyIDUxNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwMy4xNiAzMjMuNTZDNTE0LjU1IDI4MS40NyA1MTUuMzIgMjM1LjkxIDUwMy4yIDE5MC43NkM0NjYuNTcgNTQuMjI5OSAzMjYuMDQgLTI2LjgwMDEgMTg5LjMzIDkuNzc5OTFDODMuODEwMSAzOC4wMTk5IDExLjM4OTkgMTI4LjA3IDAuNjg5OTQxIDIzMC40N0g0My45OUM1NC4yOSAxNDcuMzMgMTEzLjc0IDc0LjcyOTggMTk5Ljc1IDUxLjcwOThDMzA2LjA1IDIzLjI1OTggNDE1LjEzIDgwLjY2OTkgNDUzLjE3IDE4MS4zOEw0MTEuMDMgMTkyLjY1QzM5MS42NCAxNDUuOCAzNTIuNTcgMTExLjQ1IDMwNi4zIDk2LjgxOThMMjk4LjU2IDE0MC42NkMzMzUuMDkgMTU0LjEzIDM2NC43MiAxODQuNSAzNzUuNTYgMjI0LjkxQzM5MS4zNiAyODMuOCAzNjEuOTQgMzQ0LjE0IDMwOC41NiAzNjkuMTdMMzIwLjA5IDQxMi4xNkMzOTAuMjUgMzgzLjIxIDQzMi40IDMxMC4zIDQyMi40MyAyMzUuMTRMNDY0LjQxIDIyMy45MUM0NjguOTEgMjUyLjYyIDQ2Ny4zNSAyODEuMTYgNDYwLjU1IDMwOC4wN0w1MDMuMTYgMzIzLjU2WiIgZmlsbD0iIzMwYjI3YiIvPgogIDxwYXRoIGQ9Ik0zMjEuOTkgNTA0LjIyQzE4NS4yNyA1NDAuOCA0NC43NTAxIDQ1OS43NyA4LjExMDExIDMyMy4yNEMzLjg0MDExIDMwNy4zMSAxLjE3IDI5MS4zMyAwIDI3NS40Nkg0My4yN0M0NC4zNiAyODcuMzcgNDYuNDY5OSAyOTkuMzUgNDkuNjc5OSAzMTEuMjlDNTMuMDM5OSAzMjMuOCA1Ny40NSAzMzUuNzUgNjIuNzkgMzQ3LjA3TDEwMS4zOCAzMjMuOTJDOTguMTI5OSAzMTYuNDIgOTUuMzkgMzA4LjYgOTMuMjEgMzAwLjQ3QzY5LjE3IDIxMC44NyAxMjIuNDEgMTE4Ljc3IDIxMi4xMyA5NC43NjAxQzIyOS4xMyA5MC4yMTAxIDI0Ni4yMyA4OC40NDAxIDI2Mi45MyA4OS4xNTAxTDI1NS4xOSAxMzNDMjQ0LjczIDEzMy4wNSAyMzQuMTEgMTM0LjQyIDIyMy41MyAxMzcuMjVDMTU3LjMxIDE1NC45OCAxMTguMDEgMjIyLjk1IDEzNS43NSAyODkuMDlDMTM2Ljg1IDI5My4xNiAxMzguMTMgMjk3LjEzIDEzOS41OSAzMDAuOTlMMTg4Ljk0IDI3MS4zOEwxNzQuMDcgMjMxLjk1TDIyMC42NyAxODQuMDhMMjc5LjU3IDE3MS4zOUwyOTYuNjIgMTkyLjM4TDI2OS40NyAyMTkuODhMMjQ1Ljc5IDIyNy4zM0wyMjguODcgMjQ0LjcyTDIzNy4xNiAyNjcuNzlDMjM3LjE2IDI2Ny43OSAyNTMuOTUgMjg1LjYzIDI1My45OCAyODUuNjRMMjc3LjcgMjc5LjMzTDI5NC41OCAyNjAuNzlMMzMxLjQ0IDI0OS4xMkwzNDIuNDIgMjczLjgyTDMwNC4zOSAzMjAuNDVMMjQwLjY2IDM0MC42M0wyMTIuMDggMzA4LjgxTDE2Mi4yNiAzMzguN0MxODcuOCAzNjcuNzggMjI2LjIgMzgzLjkzIDI2Ni4wMSAzODAuNTZMMjc3LjU0IDQyMy41NUMyMTguMTMgNDMxLjQxIDE2MC4xIDQwNi44MiAxMjQuMDUgMzYxLjY0TDg1LjYzOTkgMzg0LjY4QzEzNi4yNSA0NTEuMTcgMjIzLjg0IDQ4NC4xMSAzMDkuNjEgNDYxLjE2QzM3MS4zNSA0NDQuNjQgNDE5LjQgNDAyLjU2IDQ0NS40MiAzNDkuMzhMNDg4LjA2IDM2NC44OEM0NTcuMTcgNDMxLjE2IDM5OC4yMiA0ODMuODIgMzIxLjk5IDUwNC4yMloiIGZpbGw9IiMzMGIyN2IiLz4KPC9zdmc+
|
||||
|
||||
303
build.gradle
303
build.gradle
@@ -1,104 +1,261 @@
|
||||
plugins {
|
||||
id 'fabric-loom' version '0.2.6-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
id 'fabric-loom' version '0.11-SNAPSHOT'
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '7.0.0'
|
||||
id 'com.modrinth.minotaur' version '1.2.+'
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
import com.modrinth.minotaur.request.VersionType
|
||||
import com.modrinth.minotaur.responses.ResponseError
|
||||
import org.apache.http.client.config.CookieSpecs
|
||||
import org.apache.http.client.config.RequestConfig
|
||||
import org.apache.http.client.entity.EntityBuilder
|
||||
import org.apache.http.client.methods.HttpPatch
|
||||
import org.apache.http.entity.ContentType
|
||||
import org.apache.http.impl.client.HttpClientBuilder
|
||||
import org.apache.http.util.EntityUtils
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
version = project.mod_version
|
||||
group = project.maven_group
|
||||
version = "${project.mod_version}+${getMCVersionString()}"
|
||||
archivesBaseName = project.archives_base_name
|
||||
|
||||
minecraft {
|
||||
// This field defines the Java version your mod target.
|
||||
def targetJavaVersion = 17
|
||||
|
||||
boolean isMCVersionNonRelease() {
|
||||
return project.minecraft_version.matches('^\\d\\dw\\d\\d[a-z]$')
|
||||
|| project.minecraft_version.matches('\\d+\\.\\d+-(pre|rc)(\\d+)')
|
||||
}
|
||||
|
||||
String getMCVersionString() {
|
||||
if (isMCVersionNonRelease()) {
|
||||
return project.minecraft_version
|
||||
}
|
||||
def version = project.minecraft_version.split('\\.')
|
||||
return version[0] + '.' + version[1]
|
||||
}
|
||||
|
||||
boolean pingUrl(String address) {
|
||||
try {
|
||||
def conn = (HttpURLConnection) new URL(address).openConnection()
|
||||
int responseCode = conn.getResponseCode()
|
||||
return (200 <= responseCode && responseCode <= 399)
|
||||
} catch (IOException ignored) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
String parseReadme() {
|
||||
def excludeRegex = /(?m)<!-- modrinth_exclude\.start -->(.|\n)*?<!-- modrinth_exclude\.end -->/
|
||||
def linkRegex = /!\[([A-z_ ]+)]\((images\/[A-z.\/_]+)\)/
|
||||
|
||||
def readme = (String) file('README.md').text
|
||||
readme = readme.replaceAll(excludeRegex, '')
|
||||
return readme
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url = 'https://aperlambda.github.io/maven' }
|
||||
maven {
|
||||
name = 'CottonMC'
|
||||
url = 'http://server.bbkr.space:8081/artifactory/libs-snapshot'
|
||||
}
|
||||
repositories {
|
||||
maven { url = "https://jitpack.io" }
|
||||
}
|
||||
maven { url 'https://aperlambda.github.io/maven' }
|
||||
maven {
|
||||
name 'Gegy'
|
||||
url 'https://maven.gegy.dev'
|
||||
}
|
||||
maven {
|
||||
name = "CottonMC"
|
||||
url = "https://server.bbkr.space/artifactory/libs-release"
|
||||
}
|
||||
maven { url "https://maven.terraformersmc.com/releases/" }
|
||||
maven { url 'https://maven.kosmx.dev' }
|
||||
maven { url 'https://maven.shedaniel.me/' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url "https://api.modrinth.com/maven" }
|
||||
maven { url 'https://maven.quiltmc.org/repository/release'}
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadow
|
||||
api.extendsFrom shadow
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//to change the versions see the gradle.properties file
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}"
|
||||
modCompile "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
//to change the versions see the gradle.properties file
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||
modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
|
||||
modCompile "io.github.prospector:modmenu:1.8.0+build.16"
|
||||
//modCompile "io.github.cottonmc:cotton-client-commands:0.4.2+1.14.3-SNAPSHOT"
|
||||
modImplementation "dev.lambdaurora:spruceui:${project.spruceui_version}"
|
||||
include "dev.lambdaurora:spruceui:${project.spruceui_version}"
|
||||
api('org.aperlambda:lambdajcommon:1.8.1') {
|
||||
exclude group: 'com.google.code.gson'
|
||||
exclude group: 'com.google.guava'
|
||||
}
|
||||
include 'org.aperlambda:lambdajcommon:1.8.1'
|
||||
|
||||
implementation "org.jetbrains:annotations:17.0.0"
|
||||
implementation "org.aperlambda:lambdajcommon:1.7.2"
|
||||
implementation "com.electronwill.night-config:core:3.5.3"
|
||||
implementation "com.electronwill.night-config:toml:3.5.3"
|
||||
include "org.jetbrains:annotations:17.0.0"
|
||||
include "org.aperlambda:lambdajcommon:1.7.2"
|
||||
include "com.electronwill.night-config:core:3.5.3"
|
||||
include "com.electronwill.night-config:toml:3.5.3"
|
||||
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"
|
||||
|
||||
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
|
||||
// You may need to force-disable transitiveness on them.
|
||||
// Compatibility mods
|
||||
|
||||
// Ok Zoomer and LibZoomer are temporarily disabled for the time being, as we are currently using Reflection at runtime instead in OkZoomerCompat due to there being two major, completely incompatible API versions.
|
||||
// modImplementation("maven.modrinth:ok-zoomer:${project.ok_zoomer_version}")
|
||||
// modImplementation "io.github.ennuil:LibZoomer:${project.libzoomer_version}"
|
||||
|
||||
modImplementation "io.github.cottonmc:LibGui:${project.libgui_version}"
|
||||
modImplementation "org.quiltmc:quilt-json5:1.0.0"
|
||||
modCompileOnly "maven.modrinth:sodium:${project.sodium_version}"
|
||||
modImplementation("maven.modrinth:emi:${project.emi_version}")
|
||||
modImplementation "maven.modrinth:emotecraft:${project.emotecraft_version}"
|
||||
modImplementation "io.github.kosmx:bendy-lib:${project.bendylib_version}"
|
||||
|
||||
modImplementation "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||
include "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||
|
||||
shadow 'com.electronwill.night-config:core:3.6.3'
|
||||
shadow 'com.electronwill.night-config:toml:3.6.3'
|
||||
apply plugin: 'java-library'
|
||||
}
|
||||
loom {
|
||||
accessWidenerPath = file("src/main/resources/midnightcontrols.accesswidener")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.toVersion(targetJavaVersion)
|
||||
targetCompatibility = JavaVersion.toVersion(targetJavaVersion)
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
it.options.encoding = 'UTF-8'
|
||||
|
||||
it.options.release.set(targetJavaVersion)
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
inputs.property 'version', project.version
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include "fabric.mod.json"
|
||||
expand "version": project.version
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude "fabric.mod.json"
|
||||
}
|
||||
filesMatching('fabric.mod.json') {
|
||||
expand 'version': project.version
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
task publishModrinth(type: TaskModrinthUpload) {
|
||||
dependsOn(build)
|
||||
onlyIf {
|
||||
System.getenv('MODRINTH_TOKEN')
|
||||
}
|
||||
|
||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||
// if it is present.
|
||||
// If you remove this task, sources will not be generated.
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier = "sources"
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
token = System.getenv('MODRINTH_TOKEN')
|
||||
projectId = project.modrinth_id
|
||||
versionNumber = version
|
||||
versionName = "midnightcontrols ${project.mod_version} (${getMCVersionString()})"
|
||||
addGameVersion((String) project.minecraft_version)
|
||||
addLoader('fabric')
|
||||
versionType = isMCVersionNonRelease() ? VersionType.BETA : VersionType.RELEASE
|
||||
|
||||
jar {
|
||||
from "LICENSE"
|
||||
// Changelog fetching
|
||||
def changelogText = file('CHANGELOG.md').text
|
||||
def regexVersion = ((String) project.mod_version).replaceAll('\\.', /\\./).replaceAll('\\+', '\\+')
|
||||
def changelogRegex = ~"###? ${regexVersion}\\n\\n(( *- .+\\n)+)"
|
||||
def matcher = changelogText =~ changelogRegex
|
||||
|
||||
if (matcher.find()) {
|
||||
changelog = matcher.group(1)
|
||||
|
||||
def changelogLines = changelogText.split('\n')
|
||||
def linkRefRegex = ~'^\\[([A-z0-9 _\\-/+.]+)]: '
|
||||
for (int i = changelogLines.length - 1; i > 0; i--) {
|
||||
def line = changelogLines[i]
|
||||
if ((line =~ linkRefRegex).find())
|
||||
changelog += '\n' + line
|
||||
else break
|
||||
}
|
||||
}
|
||||
|
||||
// Readme
|
||||
doFirst {
|
||||
final def client = HttpClientBuilder.create().setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build()).build()
|
||||
final def patch = new HttpPatch((String) (apiURL + '/v1/mod/' + projectId))
|
||||
patch.addHeader("Authorization", token)
|
||||
|
||||
var json = new JsonObject()
|
||||
json.addProperty("body", parseReadme())
|
||||
patch.setEntity(EntityBuilder.create()
|
||||
.setText(json.toString())
|
||||
.setContentType(ContentType.APPLICATION_JSON)
|
||||
.build())
|
||||
|
||||
final def response = client.execute(patch)
|
||||
final int status = response.getStatusLine().getStatusCode()
|
||||
|
||||
final def gson = new GsonBuilder().create()
|
||||
if (status == 200) {
|
||||
project.getLogger().lifecycle("Successfully updated readme to ${projectId}.")
|
||||
} else {
|
||||
errorInfo = gson.fromJson(EntityUtils.toString(response.getEntity()), ResponseError.class)
|
||||
project.getLogger().error("Upload failed! Status: ${status} Error: ${errorInfo.getError()} Reason: ${errorInfo.getDescription()}")
|
||||
throw new GradleException("Upload failed! Status: ${status} Reason: ${errorInfo.getDescription()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// configure the maven publication
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
// add all the jars that should be included when publishing to maven
|
||||
artifact(remapJar) {
|
||||
builtBy remapJar
|
||||
}
|
||||
artifact(sourcesJar) {
|
||||
builtBy remapSourcesJar
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact(sourcesJar) {
|
||||
builtBy remapSourcesJar
|
||||
}
|
||||
|
||||
// select the repositories you want to publish to
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
pom {
|
||||
name = 'midnightcontrols'
|
||||
description = 'Adds better controls, and controller support.'
|
||||
}
|
||||
|
||||
pom.withXml {
|
||||
def dependenciesNode = asNode().appendNode('dependencies')
|
||||
|
||||
configurations.shadow.allDependencies.each {
|
||||
def dependencyNode = dependenciesNode.appendNode('dependency')
|
||||
|
||||
dependencyNode.appendNode('groupId', it.group)
|
||||
dependencyNode.appendNode('artifactId', it.name)
|
||||
dependencyNode.appendNode('version', it.version)
|
||||
dependencyNode.appendNode('scope', 'compile')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
name 'GithubPackages'
|
||||
url uri('https://maven.pkg.github.com/LambdAurora/midnightcontrols')
|
||||
credentials {
|
||||
username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
|
||||
password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
|
||||
}
|
||||
}
|
||||
def midnightcontrolsMaven = System.getenv('midnightcontrols_MAVEN')
|
||||
if (midnightcontrolsMaven) {
|
||||
maven {
|
||||
name 'midnightcontrolsMaven'
|
||||
url uri(midnightcontrolsMaven)
|
||||
credentials {
|
||||
username = project.findProperty('gpr.user') ?: System.getenv('MAVEN_USERNAME')
|
||||
password = project.findProperty('gpr.key') ?: System.getenv('MAVEN_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
303
controller.svg
Normal file
303
controller.svg
Normal file
@@ -0,0 +1,303 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1024.0px"
|
||||
height="1024.0px"
|
||||
viewBox="0 0 1024.0 1024.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="controller.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview893"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="px"
|
||||
showgrid="true"
|
||||
inkscape:zoom="0.42961102"
|
||||
inkscape:cx="455.06282"
|
||||
inkscape:cy="790.24975"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1050"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid956" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs888">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1152">
|
||||
<stop
|
||||
style="stop-color:#ed02a0;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop1148" />
|
||||
<stop
|
||||
style="stop-color:#8900cb;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop1150" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient1154"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient4289"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient4663"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient5165"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient5507"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient5817"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient6161"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1020"
|
||||
y2="1020"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient6535"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1025"
|
||||
y2="1025"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient7579"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1025"
|
||||
y2="1025"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient8115"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1025"
|
||||
y2="1025"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:url(#linearGradient1154);fill-opacity:1"
|
||||
id="rect1033"
|
||||
width="1025"
|
||||
height="1025"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2078"
|
||||
width="671.12531"
|
||||
height="330.82504"
|
||||
x="178.42294"
|
||||
y="283.78436"
|
||||
ry="110.27502" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41852"
|
||||
id="rect2146"
|
||||
width="199.30905"
|
||||
height="504.61328"
|
||||
x="518.59406"
|
||||
y="450.07187"
|
||||
ry="97.030388"
|
||||
transform="matrix(0.97214881,-0.23436445,0.26615211,0.96393104,0,0)" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41852"
|
||||
id="rect2146-3"
|
||||
width="200.94308"
|
||||
height="502.92538"
|
||||
x="271.98715"
|
||||
y="209.38025"
|
||||
ry="97.050156"
|
||||
transform="matrix(0.97237519,0.23342343,-0.26569554,0.96405699,0,0)" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2384"
|
||||
width="87.538078"
|
||||
height="55.137505"
|
||||
x="251.37137"
|
||||
y="270"
|
||||
ry="27.568752" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2384-6"
|
||||
width="87.538078"
|
||||
height="55.137505"
|
||||
x="691.97968"
|
||||
y="270"
|
||||
ry="27.568752" />
|
||||
<path
|
||||
style="fill:url(#linearGradient4663);fill-opacity:1"
|
||||
id="path2566"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="302.43524"
|
||||
sodipodi:cy="400.95157"
|
||||
sodipodi:rx="51.063881"
|
||||
sodipodi:ry="48.245316"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 255.25223,419.40025 a 51.063881,48.245316 0 0 1 27.4529,-62.94723 51.063881,48.245316 0 0 1 66.74267,25.66543 51.063881,48.245316 0 0 1 -26.87629,63.16896 51.063881,48.245316 0 0 1 -66.97486,-25.11969" />
|
||||
<path
|
||||
style="fill:url(#linearGradient5817);fill-opacity:1"
|
||||
id="path2566-7"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="663.29633"
|
||||
sodipodi:cy="527.23883"
|
||||
sodipodi:rx="51.063881"
|
||||
sodipodi:ry="48.245316"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 616.11332,545.68751 a 51.063881,48.245316 0 0 1 27.45289,-62.94723 51.063881,48.245316 0 0 1 66.74267,25.66543 51.063881,48.245316 0 0 1 -26.87628,63.16896 51.063881,48.245316 0 0 1 -66.97487,-25.11969" />
|
||||
<path
|
||||
style="fill:url(#linearGradient5165);fill-opacity:1"
|
||||
id="path2710"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="740.12561"
|
||||
sodipodi:cy="345.81409"
|
||||
sodipodi:rx="21.88452"
|
||||
sodipodi:ry="20.676565"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 719.90432,353.72066 a 21.88452,20.676565 0 0 1 11.76553,-26.97738 21.88452,20.676565 0 0 1 28.604,10.99947 21.88452,20.676565 0 0 1 -11.51841,27.07241 21.88452,20.676565 0 0 1 -28.70351,-10.76558" />
|
||||
<path
|
||||
style="fill:url(#linearGradient5507);fill-opacity:1"
|
||||
id="path2710-5"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="785.35364"
|
||||
sodipodi:cy="385.78876"
|
||||
sodipodi:rx="21.88452"
|
||||
sodipodi:ry="20.676565"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 765.13235,393.69534 a 21.88452,20.676565 0 0 1 11.76553,-26.97739 21.88452,20.676565 0 0 1 28.604,10.99947 21.88452,20.676565 0 0 1 -11.51841,27.07241 21.88452,20.676565 0 0 1 -28.70351,-10.76558" />
|
||||
<path
|
||||
style="fill:url(#linearGradient6535);fill-opacity:1"
|
||||
id="path2710-3"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="697.81555"
|
||||
sodipodi:cy="385.78876"
|
||||
sodipodi:rx="21.88452"
|
||||
sodipodi:ry="20.676565"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 677.59426,393.69534 a 21.88452,20.676565 0 0 1 11.76553,-26.97739 21.88452,20.676565 0 0 1 28.604,10.99947 21.88452,20.676565 0 0 1 -11.51841,27.07241 21.88452,20.676565 0 0 1 -28.70351,-10.76558" />
|
||||
<path
|
||||
style="fill:url(#linearGradient6161);fill-opacity:1"
|
||||
id="path2710-56"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="741.58459"
|
||||
sodipodi:cy="427.14188"
|
||||
sodipodi:rx="21.88452"
|
||||
sodipodi:ry="20.676565"
|
||||
sodipodi:start="2.7492077"
|
||||
sodipodi:end="2.731929"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 721.36331,435.04845 a 21.88452,20.676565 0 0 1 11.76552,-26.97738 21.88452,20.676565 0 0 1 28.604,10.99947 21.88452,20.676565 0 0 1 -11.51841,27.07241 21.88452,20.676565 0 0 1 -28.70351,-10.76558" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient8115);fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2896"
|
||||
width="36.474197"
|
||||
height="110.27502"
|
||||
x="369.54779"
|
||||
y="476.76566"
|
||||
ry="13.784376" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient7579);fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2898"
|
||||
width="116.71745"
|
||||
height="37.217815"
|
||||
x="327.2377"
|
||||
y="512.60504"
|
||||
ry="13.383304" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient4289);fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect3104"
|
||||
width="233.43489"
|
||||
height="110.27502"
|
||||
x="400.1861"
|
||||
y="338.92191"
|
||||
ry="13.383307" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
269
controller_banner.svg
Normal file
269
controller_banner.svg
Normal file
@@ -0,0 +1,269 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1920"
|
||||
height="1080"
|
||||
viewBox="0 0 1920 1080"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="controller_banner.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
inkscape:export-filename="/home/martin/Bilder/Bilder/MidnightControlsBanner.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview893"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="px"
|
||||
showgrid="true"
|
||||
inkscape:zoom="0.40120419"
|
||||
inkscape:cx="928.45492"
|
||||
inkscape:cy="816.29258"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1050"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid956" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs888">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2059">
|
||||
<stop
|
||||
style="stop-color:#ed02a0;stop-opacity:0.72909278"
|
||||
offset="0"
|
||||
id="stop2055" />
|
||||
<stop
|
||||
style="stop-color:#8900cb;stop-opacity:0.73234183"
|
||||
offset="1"
|
||||
id="stop2057" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="powerclip"
|
||||
id="path-effect904"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
inverse="true"
|
||||
flatten="false"
|
||||
hide_clip="false"
|
||||
message="Füllregel Gerade-Ungerade im <b>Füllung und Kontur</b>-Dialog verwenden, wenn das Anwenden des Ausschnitts mit nachfolgendem Umwandeln in Pfade kein Ergebnis bringt" />
|
||||
<inkscape:path-effect
|
||||
effect="powerclip"
|
||||
id="path-effect900"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
inverse="true"
|
||||
flatten="false"
|
||||
hide_clip="false"
|
||||
message="Füllregel Gerade-Ungerade im <b>Füllung und Kontur</b>-Dialog verwenden, wenn das Anwenden des Ausschnitts mit nachfolgendem Umwandeln in Pfade kein Ergebnis bringt" />
|
||||
<inkscape:path-effect
|
||||
effect="powerclip"
|
||||
id="path-effect896"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
inverse="true"
|
||||
flatten="false"
|
||||
hide_clip="false"
|
||||
message="Füllregel Gerade-Ungerade im <b>Füllung und Kontur</b>-Dialog verwenden, wenn das Anwenden des Ausschnitts mit nachfolgendem Umwandeln in Pfade kein Ergebnis bringt" />
|
||||
<rect
|
||||
x="500"
|
||||
y="800"
|
||||
width="840"
|
||||
height="220"
|
||||
id="rect7833" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1152">
|
||||
<stop
|
||||
style="stop-color:#ed02a0;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop1148" />
|
||||
<stop
|
||||
style="stop-color:#8900cb;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop1150" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2059"
|
||||
id="linearGradient1154"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1920"
|
||||
y2="1080"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient4289"
|
||||
x1="-398"
|
||||
y1="56"
|
||||
x2="1522"
|
||||
y2="1136"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(398,-56)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient7579"
|
||||
x1="-398"
|
||||
y1="56"
|
||||
x2="1522"
|
||||
y2="1136"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(398,-56)" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath878">
|
||||
<path
|
||||
id="path880"
|
||||
style="display:none;fill:url(#linearGradient882);stroke-width:1.00028"
|
||||
d="m 1170.0221,-4.111892 a 21.817197,20.751893 3.393352 0 0 -8.3753,3.68511674 21.817197,20.751893 3.393352 0 0 -4.1775,28.99380526 l 0.2304,0.284584 a 21.817197,20.751893 3.393352 0 0 30.5488,3.770316 21.817197,20.751893 3.393352 0 0 3.9128,-29.0289691 21.817197,20.751893 3.393352 0 0 -22.1392,-7.7048529 z M 858.04762,85.714292 c -7.15183,1.731644 -11.32417,8.932859 -9.35312,16.146398 l 22.2005,81.24794 c 1.97106,7.21354 9.31675,11.62594 16.46858,9.8943 L 1086.714,144.73497 c 7.1518,-1.73163 11.3223,-8.9324 9.3512,-16.14595 l -22.2005,-81.247936 c -1.971,-7.213536 -9.3149,-11.626381 -16.4667,-9.894748 z M 756.02165,124.73884 a 50.906793,48.421082 3.3933293 0 0 -19.54511,8.59585 50.906793,48.421082 3.3933293 0 0 -9.74699,67.65414 l 0.53564,0.66651 a 50.906793,48.421082 3.3933293 0 0 71.28286,8.79621 50.906793,48.421082 3.3933293 0 0 9.13065,-67.73384 50.906793,48.421082 3.3933293 0 0 -51.65705,-17.97887 z M 1139.8366,44.661798 a 21.817197,20.751893 3.393352 0 0 -8.3752,3.685094 21.817197,20.751893 3.393352 0 0 -4.1775,28.993805 l 0.2304,0.284584 a 21.817197,20.751893 3.393352 0 0 30.5487,3.770339 21.817197,20.751893 3.393352 0 0 3.9147,-29.029436 21.817197,20.751893 3.393352 0 0 -22.1411,-7.704386 z m 84.4396,-20.445031 a 21.817197,20.751893 3.393352 0 0 -8.3754,3.685117 21.817197,20.751893 3.393352 0 0 -4.1774,28.993806 l 0.2285,0.285027 a 21.817197,20.751893 3.393352 0 0 30.5488,3.77034 21.817197,20.751893 3.393352 0 0 3.9147,-29.02946 21.817197,20.751893 3.393352 0 0 -22.1392,-7.70483 z M 1193.05,74.672751 a 21.817197,20.751893 3.393352 0 0 -8.3759,3.68321 21.817197,20.751893 3.393352 0 0 -4.1769,28.995709 l 0.2303,0.28458 a 21.817197,20.751893 3.393352 0 0 30.5488,3.77035 21.817197,20.751893 3.393352 0 0 3.9128,-29.029006 21.817197,20.751893 3.393352 0 0 -22.1391,-7.704843 z M 865.52481,226.88685 c -7.36616,1.78355 -11.66248,9.20165 -9.63236,16.63136 l 5.86309,21.45735 -27.90161,6.7557 c -7.15183,1.73165 -11.32229,8.9324 -9.35123,16.14595 l 2.77837,10.1681 c 1.97106,7.21354 9.31487,11.6264 16.46669,9.89476 l 27.90161,-6.75571 6.22966,22.7989 c 2.03012,7.42971 9.59558,11.97577 16.96174,10.19223 l 8.59091,-2.08008 c 7.36616,-1.78354 11.6606,-9.2012 9.63048,-16.63091 l -6.22966,-22.7989 23.68151,-5.73391 c 7.15183,-1.73165 11.32229,-8.93242 9.35123,-16.14595 l -2.77837,-10.16811 c -1.97106,-7.21354 -9.31486,-11.62639 -16.46669,-9.89474 l -23.68151,5.73391 -5.86309,-21.45735 c -2.03012,-7.42971 -9.5937,-11.97623 -16.95987,-10.19269 z m 272.15169,-63.56116 a 50.906793,48.421082 3.3933293 0 0 -19.5432,8.59539 50.906793,48.421082 3.3933293 0 0 -9.747,67.65414 l 0.5356,0.66651 a 50.906793,48.421082 3.3933293 0 0 71.2829,8.79621 50.906793,48.421082 3.3933293 0 0 9.1307,-67.73384 50.906793,48.421082 3.3933293 0 0 -51.659,-17.97841 z" />
|
||||
<path
|
||||
id="lpe_path-effect896"
|
||||
style="fill:url(#linearGradient882);stroke-width:1.00028"
|
||||
class="powerclip"
|
||||
d="M 636.0108,56.943474 H 846.95389 V 569.86886 H 636.0108 Z m 534.0113,-61.055366 a 21.817197,20.751893 3.393352 0 0 -8.3753,3.68511674 21.817197,20.751893 3.393352 0 0 -4.1775,28.99380526 l 0.2304,0.284584 a 21.817197,20.751893 3.393352 0 0 30.5488,3.770316 21.817197,20.751893 3.393352 0 0 3.9128,-29.0289691 21.817197,20.751893 3.393352 0 0 -22.1392,-7.7048529 z M 858.04762,85.714292 c -7.15183,1.731644 -11.32417,8.932859 -9.35312,16.146398 l 22.2005,81.24794 c 1.97106,7.21354 9.31675,11.62594 16.46858,9.8943 L 1086.714,144.73497 c 7.1518,-1.73163 11.3223,-8.9324 9.3512,-16.14595 l -22.2005,-81.247936 c -1.971,-7.213536 -9.3149,-11.626381 -16.4667,-9.894748 z M 756.02165,124.73884 a 50.906793,48.421082 3.3933293 0 0 -19.54511,8.59585 50.906793,48.421082 3.3933293 0 0 -9.74699,67.65414 l 0.53564,0.66651 a 50.906793,48.421082 3.3933293 0 0 71.28286,8.79621 50.906793,48.421082 3.3933293 0 0 9.13065,-67.73384 50.906793,48.421082 3.3933293 0 0 -51.65705,-17.97887 z M 1139.8366,44.661798 a 21.817197,20.751893 3.393352 0 0 -8.3752,3.685094 21.817197,20.751893 3.393352 0 0 -4.1775,28.993805 l 0.2304,0.284584 a 21.817197,20.751893 3.393352 0 0 30.5487,3.770339 21.817197,20.751893 3.393352 0 0 3.9147,-29.029436 21.817197,20.751893 3.393352 0 0 -22.1411,-7.704386 z m 84.4396,-20.445031 a 21.817197,20.751893 3.393352 0 0 -8.3754,3.685117 21.817197,20.751893 3.393352 0 0 -4.1774,28.993806 l 0.2285,0.285027 a 21.817197,20.751893 3.393352 0 0 30.5488,3.77034 21.817197,20.751893 3.393352 0 0 3.9147,-29.02946 21.817197,20.751893 3.393352 0 0 -22.1392,-7.70483 z M 1193.05,74.672751 a 21.817197,20.751893 3.393352 0 0 -8.3759,3.68321 21.817197,20.751893 3.393352 0 0 -4.1769,28.995709 l 0.2303,0.28458 a 21.817197,20.751893 3.393352 0 0 30.5488,3.77035 21.817197,20.751893 3.393352 0 0 3.9128,-29.029006 21.817197,20.751893 3.393352 0 0 -22.1391,-7.704843 z M 865.52481,226.88685 c -7.36616,1.78355 -11.66248,9.20165 -9.63236,16.63136 l 5.86309,21.45735 -27.90161,6.7557 c -7.15183,1.73165 -11.32229,8.9324 -9.35123,16.14595 l 2.77837,10.1681 c 1.97106,7.21354 9.31487,11.6264 16.46669,9.89476 l 27.90161,-6.75571 6.22966,22.7989 c 2.03012,7.42971 9.59558,11.97577 16.96174,10.19223 l 8.59091,-2.08008 c 7.36616,-1.78354 11.6606,-9.2012 9.63048,-16.63091 l -6.22966,-22.7989 23.68151,-5.73391 c 7.15183,-1.73165 11.32229,-8.93242 9.35123,-16.14595 l -2.77837,-10.16811 c -1.97106,-7.21354 -9.31486,-11.62639 -16.46669,-9.89474 l -23.68151,5.73391 -5.86309,-21.45735 c -2.03012,-7.42971 -9.5937,-11.97623 -16.95987,-10.19269 z m 272.15169,-63.56116 a 50.906793,48.421082 3.3933293 0 0 -19.5432,8.59539 50.906793,48.421082 3.3933293 0 0 -9.747,67.65414 l 0.5356,0.66651 a 50.906793,48.421082 3.3933293 0 0 71.2829,8.79621 50.906793,48.421082 3.3933293 0 0 9.1307,-67.73384 50.906793,48.421082 3.3933293 0 0 -51.659,-17.97841 z" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient882"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.96459266,-0.23355313,0.26584317,0.97291549,369.02066,-147.43741)"
|
||||
x1="-398"
|
||||
y1="56"
|
||||
x2="1522"
|
||||
y2="1136" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath884">
|
||||
<path
|
||||
id="path886"
|
||||
style="display:none;fill:url(#linearGradient888);stroke-width:1.00027"
|
||||
d="m 1026.6374,528.82806 a 20.752793,21.815894 86.926601 0 0 -9.2243,-0.58528 20.752793,21.815894 86.926601 0 0 -18.53105,23.48045 l 0.0557,0.35396 a 20.752793,21.815894 86.926601 0 0 24.81605,17.20206 20.752793,21.815894 86.926601 0 0 18.3173,-23.63171 20.752793,21.815894 86.926601 0 0 -15.4337,-16.81948 z M 707.37883,465.4963 c -7.15078,-1.7386 -14.49832,2.66675 -16.47273,9.87849 l -22.23828,81.2277 c -1.97441,7.21175 2.19438,14.41711 9.34515,16.15571 l 199.32113,48.46175 c 7.15073,1.73859 14.49643,-2.66722 16.47084,-9.87896 l 22.23828,-81.2277 c 1.97441,-7.21174 -2.19254,-14.41666 -9.34327,-16.15524 z M 598.02135,453.23104 a 48.423182,50.903752 86.926623 0 0 -21.5241,-1.36927 48.423182,50.903752 86.926623 0 0 -43.23978,54.7896 l 0.12713,0.82721 a 48.423182,50.903752 86.926623 0 0 57.90649,40.13811 48.423182,50.903752 86.926623 0 0 42.74098,-55.13985 48.423182,50.903752 86.926623 0 0 -36.01072,-39.2458 z m 377.16446,104.5578 a 20.752793,21.815894 86.926601 0 0 -9.22414,-0.58526 20.752793,21.815894 86.926601 0 0 -18.53108,23.48045 l 0.0557,0.35396 a 20.752793,21.815894 86.926601 0 0 24.8159,17.20204 20.752793,21.815894 86.926601 0 0 18.31921,-23.63124 20.752793,21.815894 86.926601 0 0 -15.43563,-16.81995 z m 84.42719,20.52712 a 20.752793,21.815894 86.926601 0 0 -9.2243,-0.58529 20.752793,21.815894 86.926601 0 0 -18.531,23.48045 l 0.054,0.35352 a 20.752793,21.815894 86.926601 0 0 24.8159,17.20204 20.752793,21.815894 86.926601 0 0 18.3193,-23.63122 20.752793,21.815894 86.926601 0 0 -15.4338,-16.8195 z m -53.2258,29.9599 a 20.752793,21.815894 86.926601 0 0 -9.22373,-0.58718 20.752793,21.815894 86.926601 0 0 -18.53161,23.48235 l 0.0557,0.35395 a 20.752793,21.815894 86.926601 0 0 24.81584,17.20205 20.752793,21.815894 86.926601 0 0 18.3174,-23.63169 20.752793,21.815894 86.926601 0 0 -15.4337,-16.81948 z M 641.50831,592.48259 c -7.36508,-1.79069 -14.93245,2.74811 -16.96603,10.17597 l -5.87307,21.452 -27.8975,-6.78283 c -7.15078,-1.7386 -14.49644,2.6672 -16.47085,9.87895 l -2.7831,10.16557 c -1.97441,7.21174 2.1925,14.41666 9.34328,16.15525 l 27.8975,6.78284 -6.24026,22.79322 c -2.03358,7.42786 2.25909,14.85024 9.62417,16.64094 l 8.58964,2.08844 c 7.36508,1.7907 14.93057,-2.74856 16.96415,-10.17642 l 6.24026,-22.79322 23.67803,5.75693 c 7.15078,1.7386 14.49644,-2.66722 16.47085,-9.87896 l 2.7831,-10.16557 c 1.97441,-7.21175 -2.1925,-14.41665 -9.34328,-16.15525 l -23.67803,-5.75693 5.87307,-21.45201 c 2.03358,-7.42785 -2.25721,-14.84978 -9.62229,-16.64048 z m 270.91446,68.20272 a 48.423182,50.903752 86.926623 0 0 -21.52221,-1.3688 48.423182,50.903752 86.926623 0 0 -43.23975,54.78961 l 0.12708,0.8272 a 48.423182,50.903752 86.926623 0 0 57.90652,40.13811 48.423182,50.903752 86.926623 0 0 42.74097,-55.13984 48.423182,50.903752 86.926623 0 0 -36.01261,-39.24628 z" />
|
||||
<path
|
||||
id="lpe_path-effect900"
|
||||
style="fill:url(#linearGradient888);stroke-width:1.00027"
|
||||
class="powerclip"
|
||||
d="M 912.35657,483.93158 H 1121.6656 V 998.54486 H 912.35657 Z m 114.28083,44.89648 a 20.752793,21.815894 86.926601 0 0 -9.2243,-0.58528 20.752793,21.815894 86.926601 0 0 -18.53105,23.48045 l 0.0557,0.35396 a 20.752793,21.815894 86.926601 0 0 24.81605,17.20206 20.752793,21.815894 86.926601 0 0 18.3173,-23.63171 20.752793,21.815894 86.926601 0 0 -15.4337,-16.81948 z M 707.37883,465.4963 c -7.15078,-1.7386 -14.49832,2.66675 -16.47273,9.87849 l -22.23828,81.2277 c -1.97441,7.21175 2.19438,14.41711 9.34515,16.15571 l 199.32113,48.46175 c 7.15073,1.73859 14.49643,-2.66722 16.47084,-9.87896 l 22.23828,-81.2277 c 1.97441,-7.21174 -2.19254,-14.41666 -9.34327,-16.15524 z M 598.02135,453.23104 a 48.423182,50.903752 86.926623 0 0 -21.5241,-1.36927 48.423182,50.903752 86.926623 0 0 -43.23978,54.7896 l 0.12713,0.82721 a 48.423182,50.903752 86.926623 0 0 57.90649,40.13811 48.423182,50.903752 86.926623 0 0 42.74098,-55.13985 48.423182,50.903752 86.926623 0 0 -36.01072,-39.2458 z m 377.16446,104.5578 a 20.752793,21.815894 86.926601 0 0 -9.22414,-0.58526 20.752793,21.815894 86.926601 0 0 -18.53108,23.48045 l 0.0557,0.35396 a 20.752793,21.815894 86.926601 0 0 24.8159,17.20204 20.752793,21.815894 86.926601 0 0 18.31921,-23.63124 20.752793,21.815894 86.926601 0 0 -15.43563,-16.81995 z m 84.42719,20.52712 a 20.752793,21.815894 86.926601 0 0 -9.2243,-0.58529 20.752793,21.815894 86.926601 0 0 -18.531,23.48045 l 0.054,0.35352 a 20.752793,21.815894 86.926601 0 0 24.8159,17.20204 20.752793,21.815894 86.926601 0 0 18.3193,-23.63122 20.752793,21.815894 86.926601 0 0 -15.4338,-16.8195 z m -53.2258,29.9599 a 20.752793,21.815894 86.926601 0 0 -9.22373,-0.58718 20.752793,21.815894 86.926601 0 0 -18.53161,23.48235 l 0.0557,0.35395 a 20.752793,21.815894 86.926601 0 0 24.81584,17.20205 20.752793,21.815894 86.926601 0 0 18.3174,-23.63169 20.752793,21.815894 86.926601 0 0 -15.4337,-16.81948 z M 641.50831,592.48259 c -7.36508,-1.79069 -14.93245,2.74811 -16.96603,10.17597 l -5.87307,21.452 -27.8975,-6.78283 c -7.15078,-1.7386 -14.49644,2.6672 -16.47085,9.87895 l -2.7831,10.16557 c -1.97441,7.21174 2.1925,14.41666 9.34328,16.15525 l 27.8975,6.78284 -6.24026,22.79322 c -2.03358,7.42786 2.25909,14.85024 9.62417,16.64094 l 8.58964,2.08844 c 7.36508,1.7907 14.93057,-2.74856 16.96415,-10.17642 l 6.24026,-22.79322 23.67803,5.75693 c 7.15078,1.7386 14.49644,-2.66722 16.47085,-9.87896 l 2.7831,-10.16557 c 1.97441,-7.21175 -2.1925,-14.41665 -9.34328,-16.15525 l -23.67803,-5.75693 5.87307,-21.45201 c 2.03358,-7.42785 -2.25721,-14.84978 -9.62229,-16.64048 z m 270.91446,68.20272 a 48.423182,50.903752 86.926623 0 0 -21.52221,-1.3688 48.423182,50.903752 86.926623 0 0 -43.23975,54.78961 l 0.12708,0.8272 a 48.423182,50.903752 86.926623 0 0 57.90652,40.13811 48.423182,50.903752 86.926623 0 0 42.74097,-55.13984 48.423182,50.903752 86.926623 0 0 -36.01261,-39.24628 z" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient888"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.96445087,0.23449084,-0.26629564,0.97267307,398.764,38.857662)"
|
||||
x1="-398"
|
||||
y1="56"
|
||||
x2="1522"
|
||||
y2="1136" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath890">
|
||||
<path
|
||||
id="path892"
|
||||
style="display:none;fill:url(#linearGradient894)"
|
||||
d="m 1138.793,269.14648 a 21.88452,20.676565 0 0 0 -9.1231,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1484,0.32813 a 21.88452,20.676565 0 0 0 28.7032,10.76562 21.88452,20.676565 0 0 0 11.5175,-27.07226 21.88452,20.676565 0 0 0 -19.4804,-12.59571 z m -327.22269,13.7754 c -7.41435,0 -13.38476,5.96846 -13.38476,13.38281 v 83.50976 c 0,7.41436 5.97041,13.38282 13.38476,13.38282 h 206.66799 c 7.4143,0 13.3828,-5.96846 13.3828,-13.38282 v -83.50976 c 0,-7.41435 -5.9685,-13.38281 -13.3828,-13.38281 z m -109.57617,13.80664 a 51.063881,48.245316 0 0 0 -21.28906,3.7246 51.063881,48.245316 0 0 0 -27.45313,62.94727 l 0.34375,0.76758 a 51.063881,48.245316 0 0 0 66.97657,25.11914 51.063881,48.245316 0 0 0 26.875,-63.16797 51.063881,48.245316 0 0 0 -45.45313,-29.39062 z m 394.48826,12.39257 a 21.88452,20.676565 0 0 0 -9.123,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1484,0.32813 a 21.88452,20.676565 0 0 0 28.7031,10.76562 21.88452,20.676565 0 0 0 11.5195,-27.07226 21.88452,20.676565 0 0 0 -19.4824,-12.59571 z m 87.5391,0 a 21.88452,20.676565 0 0 0 -9.1231,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1465,0.32813 a 21.88452,20.676565 0 0 0 28.7031,10.76562 21.88452,20.676565 0 0 0 11.5196,-27.07226 21.88452,20.676565 0 0 0 -19.4805,-12.59571 z m -43.7695,41.35352 a 21.88452,20.676565 0 0 0 -9.1231,1.5957 21.88452,20.676565 0 0 0 -11.7656,26.97852 l 0.1484,0.32812 a 21.88452,20.676565 0 0 0 28.7031,10.76563 21.88452,20.676565 0 0 0 11.5176,-27.07227 21.88452,20.676565 0 0 0 -19.4804,-12.5957 z m -358.91997,70.29101 c -7.63655,10e-6 -13.78515,6.14862 -13.78515,13.78516 v 22.05469 h -28.92579 c -7.41435,0 -13.38281,5.96845 -13.38281,13.38281 v 10.45117 c 0,7.41435 5.96846,13.38282 13.38281,13.38282 h 28.92579 v 23.43359 c 0,7.63654 6.1486,13.78516 13.78515,13.78516 h 8.90625 c 7.63655,0 13.7832,-6.14862 13.7832,-13.78516 v -23.43359 h 24.55079 c 7.41435,0 13.38281,-5.96847 13.38281,-13.38282 v -10.45117 c 0,-7.41436 -5.96846,-13.38281 -13.38281,-13.38281 h -24.55079 v -22.05469 c 0,-7.63654 -6.14665,-13.78516 -13.7832,-13.78516 z m 281.52147,2.25 a 51.063881,48.245316 0 0 0 -21.2871,3.72461 51.063881,48.245316 0 0 0 -27.4531,62.94727 l 0.3437,0.76758 a 51.063881,48.245316 0 0 0 66.9766,25.11914 51.063881,48.245316 0 0 0 26.875,-63.16797 51.063881,48.245316 0 0 0 -45.4551,-29.39063 z" />
|
||||
<path
|
||||
id="lpe_path-effect904"
|
||||
style="fill:url(#linearGradient894)"
|
||||
class="powerclip"
|
||||
d="M 571.42297,222.78436 H 1252.5483 V 563.60941 H 571.42297 Z m 567.37003,46.36212 a 21.88452,20.676565 0 0 0 -9.1231,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1484,0.32813 a 21.88452,20.676565 0 0 0 28.7032,10.76562 21.88452,20.676565 0 0 0 11.5175,-27.07226 21.88452,20.676565 0 0 0 -19.4804,-12.59571 z m -327.22269,13.7754 c -7.41435,0 -13.38476,5.96846 -13.38476,13.38281 v 83.50976 c 0,7.41436 5.97041,13.38282 13.38476,13.38282 h 206.66799 c 7.4143,0 13.3828,-5.96846 13.3828,-13.38282 v -83.50976 c 0,-7.41435 -5.9685,-13.38281 -13.3828,-13.38281 z m -109.57617,13.80664 a 51.063881,48.245316 0 0 0 -21.28906,3.7246 51.063881,48.245316 0 0 0 -27.45313,62.94727 l 0.34375,0.76758 a 51.063881,48.245316 0 0 0 66.97657,25.11914 51.063881,48.245316 0 0 0 26.875,-63.16797 51.063881,48.245316 0 0 0 -45.45313,-29.39062 z m 394.48826,12.39257 a 21.88452,20.676565 0 0 0 -9.123,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1484,0.32813 a 21.88452,20.676565 0 0 0 28.7031,10.76562 21.88452,20.676565 0 0 0 11.5195,-27.07226 21.88452,20.676565 0 0 0 -19.4824,-12.59571 z m 87.5391,0 a 21.88452,20.676565 0 0 0 -9.1231,1.59766 21.88452,20.676565 0 0 0 -11.7656,26.97656 l 0.1465,0.32813 a 21.88452,20.676565 0 0 0 28.7031,10.76562 21.88452,20.676565 0 0 0 11.5196,-27.07226 21.88452,20.676565 0 0 0 -19.4805,-12.59571 z m -43.7695,41.35352 a 21.88452,20.676565 0 0 0 -9.1231,1.5957 21.88452,20.676565 0 0 0 -11.7656,26.97852 l 0.1484,0.32812 a 21.88452,20.676565 0 0 0 28.7031,10.76563 21.88452,20.676565 0 0 0 11.5176,-27.07227 21.88452,20.676565 0 0 0 -19.4804,-12.5957 z m -358.91997,70.29101 c -7.63655,10e-6 -13.78515,6.14862 -13.78515,13.78516 v 22.05469 h -28.92579 c -7.41435,0 -13.38281,5.96845 -13.38281,13.38281 v 10.45117 c 0,7.41435 5.96846,13.38282 13.38281,13.38282 h 28.92579 v 23.43359 c 0,7.63654 6.1486,13.78516 13.78515,13.78516 h 8.90625 c 7.63655,0 13.7832,-6.14862 13.7832,-13.78516 v -23.43359 h 24.55079 c 7.41435,0 13.38281,-5.96847 13.38281,-13.38282 v -10.45117 c 0,-7.41436 -5.96846,-13.38281 -13.38281,-13.38281 h -24.55079 v -22.05469 c 0,-7.63654 -6.14665,-13.78516 -13.7832,-13.78516 z m 281.52147,2.25 a 51.063881,48.245316 0 0 0 -21.2871,3.72461 51.063881,48.245316 0 0 0 -27.4531,62.94727 l 0.3437,0.76758 a 51.063881,48.245316 0 0 0 66.9766,25.11914 51.063881,48.245316 0 0 0 26.875,-63.16797 51.063881,48.245316 0 0 0 -45.4551,-29.39063 z" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1152"
|
||||
id="linearGradient894"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(398,-56)"
|
||||
x1="-398"
|
||||
y1="56"
|
||||
x2="1522"
|
||||
y2="1136" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:url(#linearGradient1154);fill-opacity:1"
|
||||
id="rect1033"
|
||||
width="1920"
|
||||
height="1080"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2078"
|
||||
width="671.12531"
|
||||
height="330.82504"
|
||||
x="576.42297"
|
||||
y="227.78436"
|
||||
ry="110.27502"
|
||||
sodipodi:type="rect"
|
||||
clip-path="url(#clipPath890)"
|
||||
inkscape:path-effect="#path-effect904"
|
||||
d="m 686.69799,227.78436 h 450.57531 c 61.0923,0 110.275,49.18266 110.275,110.27502 v 110.27501 c 0,61.09236 -49.1827,110.27502 -110.275,110.27502 H 686.69799 c -61.09236,0 -110.27502,-49.18266 -110.27502,-110.27502 V 338.05938 c 0,-61.09236 49.18266,-110.27502 110.27502,-110.27502 z" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41852"
|
||||
id="rect2146"
|
||||
width="199.30905"
|
||||
height="504.61328"
|
||||
x="917.35657"
|
||||
y="488.93158"
|
||||
ry="97.030388"
|
||||
transform="matrix(0.97214881,-0.23436445,0.26615211,0.96393104,0,0)"
|
||||
sodipodi:type="rect"
|
||||
clip-path="url(#clipPath884)"
|
||||
inkscape:path-effect="#path-effect900"
|
||||
d="m 1014.387,488.93158 h 5.2482 c 53.7549,0 97.0304,43.27555 97.0304,97.03039 v 310.5525 c 0,53.75484 -43.2755,97.03039 -97.0304,97.03039 h -5.2482 c -53.75488,0 -97.03043,-43.27555 -97.03043,-97.03039 v -310.5525 c 0,-53.75484 43.27555,-97.03039 97.03043,-97.03039 z" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41852"
|
||||
id="rect2146-3"
|
||||
width="200.94308"
|
||||
height="502.92538"
|
||||
x="641.0108"
|
||||
y="61.943474"
|
||||
ry="97.050156"
|
||||
transform="matrix(0.97237519,0.23342343,-0.26569554,0.96405699,0,0)"
|
||||
sodipodi:type="rect"
|
||||
clip-path="url(#clipPath878)"
|
||||
inkscape:path-effect="#path-effect896"
|
||||
d="m 738.06096,61.943474 h 6.84277 c 53.76579,0 97.05016,43.284366 97.05016,97.050156 V 467.8187 c 0,53.76579 -43.28437,97.05016 -97.05016,97.05016 h -6.84277 c -53.76579,0 -97.05016,-43.28437 -97.05016,-97.05016 V 158.99363 c 0,-53.76579 43.28437,-97.050156 97.05016,-97.050156 z" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2384"
|
||||
width="87.538078"
|
||||
height="55.137505"
|
||||
x="649.37134"
|
||||
y="214"
|
||||
ry="27.568752" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.41813"
|
||||
id="rect2384-6"
|
||||
width="87.538078"
|
||||
height="55.137505"
|
||||
x="1089.9797"
|
||||
y="214"
|
||||
ry="27.568752" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -2,17 +2,29 @@
|
||||
org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/use
|
||||
minecraft_version=1.15.1
|
||||
yarn_mappings=1.15.1+build.3:v2
|
||||
loader_version=0.7.2+build.174
|
||||
# check these on https://fabricmc.net/use
|
||||
minecraft_version=1.19
|
||||
yarn_mappings=1.19+build.1
|
||||
loader_version=0.14.7
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0.1
|
||||
maven_group = me.lambdaurora
|
||||
archives_base_name = lambdacontrols
|
||||
mod_version = 1.5.0
|
||||
maven_group = eu.midnightdust
|
||||
archives_base_name = midnightcontrols
|
||||
modrinth_id=bXX9h73M
|
||||
|
||||
# Dependencies
|
||||
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
||||
fabric_version=0.4.25+build.282-1.15
|
||||
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
||||
fabric_version=0.55.2+1.19
|
||||
sodium_version=mc1.19-0.4.2
|
||||
spruceui_version=4.0.0+1.19
|
||||
midnightlib_version=0.5.2
|
||||
modmenu_version=4.0.0
|
||||
emotecraft_version=2.1.3-SNAPSHOT-build.29-MC1.19-fabric
|
||||
bendylib_version=2.0.+
|
||||
emi_version=0.2.0+1.19
|
||||
libgui_version=6.0.0+1.19
|
||||
|
||||
# Ok Zoomer and LibZoomer are temporarily disabled for the time being, as we are currently using Reflection at runtime instead in OkZoomerCompat due to there being two major, completely incompatible API versions.
|
||||
# ok_zoomer_version=5.0.0-beta.3+1.17.1
|
||||
# libzoomer_version=0.3.0+1.17.1
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Mon Oct 28 18:23:18 CET 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
|
||||
|
||||
270
gradlew
vendored
Normal file → Executable file
270
gradlew
vendored
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,78 +17,113 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -105,84 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
189
gradlew.bat
vendored
189
gradlew.bat
vendored
@@ -1,100 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
BIN
icon.png
BIN
icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 20 KiB |
BIN
images/controller_controls.png
Normal file
BIN
images/controller_controls.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 KiB |
BIN
images/controller_options.png
Normal file
BIN
images/controller_options.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
@@ -1,10 +1,11 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
name 'Fabric'
|
||||
url 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'midnightcontrols'
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols;
|
||||
|
||||
import dev.lambdaurora.spruceui.util.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the controls mode.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum ControlsMode implements Nameable {
|
||||
DEFAULT,
|
||||
CONTROLLER,
|
||||
TOUCHSCREEN;
|
||||
|
||||
/**
|
||||
* Returns the next controls mode available.
|
||||
*
|
||||
* @return the next available controls mode
|
||||
*/
|
||||
public ControlsMode next() {
|
||||
var v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translation key of this controls mode.
|
||||
*
|
||||
* @return the translated key of this controls mode
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public String getTranslationKey() {
|
||||
return "midnightcontrols.controls_mode." + this.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controls mode from its identifier.
|
||||
*
|
||||
* @param id the identifier of the controls mode
|
||||
* @return the controls mode if found, else empty
|
||||
*/
|
||||
public static Optional<ControlsMode> byId(@NotNull String id) {
|
||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.event.PlayerChangeControlsModeCallback;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the MidnightControls mod.
|
||||
*
|
||||
* @author LambdAurora & Motschen
|
||||
* @version 1.8.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class MidnightControls implements ModInitializer {
|
||||
private static MidnightControls INSTANCE;
|
||||
public static final Identifier CONTROLS_MODE_CHANNEL = new Identifier(MidnightControlsConstants.CONTROLS_MODE_CHANNEL.toString());
|
||||
public static final Identifier FEATURE_CHANNEL = new Identifier(MidnightControlsConstants.FEATURE_CHANNEL.toString());
|
||||
public static final Identifier HELLO_CHANNEL = new Identifier(MidnightControlsConstants.HELLO_CHANNEL.toString());
|
||||
public static boolean isExtrasLoaded;
|
||||
|
||||
public final Logger logger = LogManager.getLogger("MidnightControls");
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
INSTANCE = this;
|
||||
isExtrasLoaded = FabricLoader.getInstance().isModLoaded("midnightcontrols-extra");
|
||||
this.log("Initializing MidnightControls...");
|
||||
|
||||
ServerPlayNetworking.registerGlobalReceiver(HELLO_CHANNEL, (server, player, handler, buf, responseSender) -> {
|
||||
String version = buf.readString(32);
|
||||
ControlsMode.byId(buf.readString(32))
|
||||
.ifPresent(controlsMode -> server
|
||||
.execute(() -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(player, controlsMode)));
|
||||
server.execute(() -> {
|
||||
ServerPlayNetworking.send(player, FEATURE_CHANNEL, this.makeFeatureBuffer(MidnightControlsFeature.HORIZONTAL_REACHAROUND));
|
||||
});
|
||||
});
|
||||
ServerPlayNetworking.registerGlobalReceiver(CONTROLS_MODE_CHANNEL,
|
||||
(server, player, handler, buf, responseSender) -> ControlsMode.byId(buf.readString(32))
|
||||
.ifPresent(controlsMode -> server
|
||||
.execute(() -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(player, controlsMode))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a message to the terminal.
|
||||
*
|
||||
* @param info the message to print
|
||||
*/
|
||||
public void log(String info) {
|
||||
this.logger.info("[MidnightControls] " + info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a warning to the terminal.
|
||||
*
|
||||
* @param warning the warning to print
|
||||
*/
|
||||
public void warn(String warning) {
|
||||
this.logger.info("[MidnightControls] " + warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a packet byte buffer made for the midnightcontrols:controls_mode plugin message.
|
||||
*
|
||||
* @param controlsMode the controls mode to send
|
||||
* @return the packet byte buffer
|
||||
*/
|
||||
public PacketByteBuf makeControlsModeBuffer(@NotNull ControlsMode controlsMode) {
|
||||
Objects.requireNonNull(controlsMode, "Controls mode cannot be null.");
|
||||
return new PacketByteBuf(Unpooled.buffer()).writeString(controlsMode.getName(), 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a packet byte buffer made for the midnightcontrols:feature plugin message.
|
||||
*
|
||||
* @param features the features data to send
|
||||
* @return the packet byte buffer
|
||||
*/
|
||||
public PacketByteBuf makeFeatureBuffer(MidnightControlsFeature... features) {
|
||||
if (features.length == 0)
|
||||
throw new IllegalArgumentException("At least one feature must be provided.");
|
||||
var buffer = new PacketByteBuf(Unpooled.buffer());
|
||||
buffer.writeVarInt(features.length);
|
||||
for (var feature : features) {
|
||||
buffer.writeString(feature.getName(), 64);
|
||||
buffer.writeBoolean(feature.isAllowed());
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public PacketByteBuf makeHello(@NotNull ControlsMode controlsMode) {
|
||||
var version = "";
|
||||
Optional<ModContainer> container;
|
||||
if ((container = FabricLoader.getInstance().getModContainer(MidnightControlsConstants.NAMESPACE)).isPresent()) {
|
||||
version = container.get().getMetadata().getVersion().getFriendlyString();
|
||||
}
|
||||
return new PacketByteBuf(Unpooled.buffer()).writeString(version, 32).writeString(controlsMode.getName(), 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MidnightControls instance.
|
||||
*
|
||||
* @return the MidnightControls instance
|
||||
*/
|
||||
public static MidnightControls get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols;
|
||||
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents the constants used by MidnightControls.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.1.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class MidnightControlsConstants {
|
||||
public static final String NAMESPACE = "midnightcontrols";
|
||||
public static final Identifier CONTROLS_MODE_CHANNEL = new Identifier(NAMESPACE, "controls_mode");
|
||||
public static final Identifier FEATURE_CHANNEL = new Identifier(NAMESPACE, "feature");
|
||||
public static final Identifier HELLO_CHANNEL = new Identifier("lambdacontrols", "hello");
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols;
|
||||
|
||||
import dev.lambdaurora.spruceui.util.Nameable;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a feature.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class MidnightControlsFeature implements Nameable {
|
||||
private static final List<MidnightControlsFeature> FEATURES = new ArrayList<>();
|
||||
public static final MidnightControlsFeature FAST_BLOCK_PLACING = new MidnightControlsFeature("fast_block_placing", true, MidnightControlsConfig.fastBlockPlacing);
|
||||
public static final MidnightControlsFeature HORIZONTAL_REACHAROUND = new MidnightControlsFeature("horizontal_reacharound", true, MidnightControlsConfig.horizontalReacharound);
|
||||
public static final MidnightControlsFeature VERTICAL_REACHAROUND = new MidnightControlsFeature("vertical_reacharound", true, MidnightControlsConfig.verticalReacharound);
|
||||
|
||||
private final String key;
|
||||
private final boolean defaultAllowed;
|
||||
private boolean allowed;
|
||||
private final boolean defaultEnabled;
|
||||
private boolean enabled;
|
||||
|
||||
public MidnightControlsFeature(@NotNull String key, boolean allowed, boolean enabled) {
|
||||
Objects.requireNonNull(key, "Feature key cannot be null.");
|
||||
this.key = key;
|
||||
this.setAllowed(this.defaultAllowed = allowed);
|
||||
this.setEnabled(this.defaultEnabled = enabled);
|
||||
}
|
||||
|
||||
public MidnightControlsFeature(@NotNull String key) {
|
||||
this(key, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the feature.
|
||||
*/
|
||||
public void allow() {
|
||||
this.setAllowed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this feature is allowed.
|
||||
*
|
||||
* @return {@code true} if this feature is allowed, else {@code false}
|
||||
*/
|
||||
public boolean isAllowed() {
|
||||
return MidnightControls.isExtrasLoaded && this.allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this feature is allowed.
|
||||
*
|
||||
* @param allowed {@code true} if this feature is allowed, else {@code false}
|
||||
*/
|
||||
public void setAllowed(boolean allowed) {
|
||||
this.allowed = allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets allowed state to default.
|
||||
*/
|
||||
public void resetAllowed() {
|
||||
this.setAllowed(this.defaultAllowed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this feature is enabled.
|
||||
*
|
||||
* @return {@code true} if this feature is enabled, else {@code false}
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this feature is enabled.
|
||||
*
|
||||
* @param enabled {@code true} if this feature is enabled, else {@code false}
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the enabled values from the config.
|
||||
*/
|
||||
public static void refreshEnabled() {
|
||||
MidnightControlsFeature.VERTICAL_REACHAROUND.setEnabled(MidnightControlsConfig.verticalReacharound);
|
||||
MidnightControlsFeature.FAST_BLOCK_PLACING.setEnabled(MidnightControlsConfig.fastBlockPlacing);
|
||||
MidnightControlsFeature.HORIZONTAL_REACHAROUND.setEnabled(MidnightControlsConfig.horizontalReacharound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this feature is available or not.
|
||||
*
|
||||
* @return {@code true} if this feature is available, else {@code false}
|
||||
* @see #isAllowed()
|
||||
* @see #isEnabled()
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
return this.isAllowed() && this.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the feature to its default values.
|
||||
*/
|
||||
public void reset() {
|
||||
this.resetAllowed();
|
||||
this.setEnabled(this.defaultEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public static @NotNull Optional<MidnightControlsFeature> fromName(@NotNull String key) {
|
||||
Objects.requireNonNull(key, "Cannot find features with a null name.");
|
||||
return FEATURES.parallelStream().filter(feature -> feature.getName().equals(key)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all features to their default values.
|
||||
*/
|
||||
public static void resetAll() {
|
||||
FEATURES.parallelStream().forEach(MidnightControlsFeature::reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all features to allow state.
|
||||
*/
|
||||
public static void resetAllAllowed() {
|
||||
FEATURES.parallelStream().forEach(MidnightControlsFeature::resetAllowed);
|
||||
}
|
||||
|
||||
static {
|
||||
FEATURES.add(FAST_BLOCK_PLACING);
|
||||
FEATURES.add(HORIZONTAL_REACHAROUND);
|
||||
FEATURES.add(VERTICAL_REACHAROUND);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
/**
|
||||
* Represents a button state.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.1.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public enum ButtonState {
|
||||
NONE(0),
|
||||
PRESS(1),
|
||||
RELEASE(2),
|
||||
REPEAT(3);
|
||||
|
||||
public final int id;
|
||||
|
||||
ButtonState(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this state is a pressed state.
|
||||
*
|
||||
* @return true if this state is a pressed state, else false
|
||||
*/
|
||||
public boolean isPressed() {
|
||||
return this == PRESS || this == REPEAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this state is an unpressed state.
|
||||
*
|
||||
* @return true if this state is an unpressed state, else false
|
||||
*/
|
||||
public boolean isUnpressed() {
|
||||
return this == RELEASE || this == NONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a controller type.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.4.3
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum ControllerType implements Nameable {
|
||||
DEFAULT(0),
|
||||
DUALSHOCK(1),
|
||||
DUALSENSE(2),
|
||||
SWITCH(3),
|
||||
XBOX_360(4),
|
||||
XBOX(5),
|
||||
STEAM_DECK(6),
|
||||
STEAM_CONTROLLER(7),
|
||||
OUYA(8);
|
||||
|
||||
private final int id;
|
||||
private final Text text;
|
||||
|
||||
ControllerType(int id) {
|
||||
this.id = id;
|
||||
this.text = Text.translatable("midnightcontrols.controller_type." + this.getName());
|
||||
}
|
||||
|
||||
ControllerType(int id, @NotNull Text text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the controller type's identifier.
|
||||
*
|
||||
* @return the controller type's identifier
|
||||
*/
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next controller type available.
|
||||
*
|
||||
* @return the next available controller type
|
||||
*/
|
||||
public @NotNull ControllerType next() {
|
||||
var v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated text of this controller type.
|
||||
*
|
||||
* @return the translated text of this controller type
|
||||
*/
|
||||
public @NotNull Text getTranslatedText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller type from its identifier.
|
||||
*
|
||||
* @param id the identifier of the controller type
|
||||
* @return the controller type if found, else empty
|
||||
*/
|
||||
public static @NotNull Optional<ControllerType> byId(@NotNull String id) {
|
||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the hud side which is the side where the movements buttons are.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.4.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum HudSide implements Nameable {
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
private final Text text;
|
||||
|
||||
HudSide() {
|
||||
this.text = Text.translatable(this.getTranslationKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next side available.
|
||||
*
|
||||
* @return the next available side
|
||||
*/
|
||||
public @NotNull HudSide next() {
|
||||
var v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation key of this hud side.
|
||||
*
|
||||
* @return the translation key of this hude side
|
||||
*/
|
||||
public @NotNull String getTranslationKey() {
|
||||
return "midnightcontrols.hud_side." + this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated text of this hud side.
|
||||
*
|
||||
* @return the translated text of this hud side
|
||||
*/
|
||||
public @NotNull Text getTranslatedText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hud side from its identifier.
|
||||
*
|
||||
* @param id the identifier of the hud side
|
||||
* @return the hud side if found, else empty
|
||||
*/
|
||||
public static @NotNull Optional<HudSide> byId(@NotNull String id) {
|
||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import dev.lambdaurora.spruceui.event.OpenScreenCallback;
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsHud;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
|
||||
import eu.midnightdust.midnightcontrols.client.mixin.KeyBindingRegistryImplAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.ring.KeyBindingRingAction;
|
||||
import eu.midnightdust.midnightcontrols.client.ring.MidnightRing;
|
||||
import dev.lambdaurora.spruceui.hud.HudManager;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents the midnightcontrols client mod.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class MidnightControlsClient extends MidnightControls implements ClientModInitializer {
|
||||
public static boolean lateInitDone = false;
|
||||
private static MidnightControlsClient INSTANCE;
|
||||
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(new Identifier(MidnightControlsConstants.NAMESPACE, "look_up"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement");
|
||||
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(new Identifier(MidnightControlsConstants.NAMESPACE, "look_right"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement");
|
||||
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(new Identifier(MidnightControlsConstants.NAMESPACE, "look_down"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement");
|
||||
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(new Identifier(MidnightControlsConstants.NAMESPACE, "look_left"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement");
|
||||
public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(new Identifier(MidnightControlsConstants.NAMESPACE, "ring"),
|
||||
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.misc");
|
||||
public static final Identifier CONTROLLER_BUTTONS = new Identifier(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_buttons.png");
|
||||
public static final Identifier CONTROLLER_EXPANDED = new Identifier(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_expanded.png");
|
||||
public static final Identifier CONTROLLER_AXIS = new Identifier(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_axis.png");
|
||||
public static final Identifier CURSOR_TEXTURE = new Identifier(MidnightControlsConstants.NAMESPACE, "textures/gui/cursor.png");
|
||||
public final static File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt");
|
||||
public final MidnightInput input = new MidnightInput();
|
||||
public final MidnightRing ring = new MidnightRing(this);
|
||||
public final MidnightReacharound reacharound = new MidnightReacharound();
|
||||
private MidnightControlsHud hud;
|
||||
private ControlsMode previousControlsMode;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
INSTANCE = this;
|
||||
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_UP);
|
||||
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_RIGHT);
|
||||
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_DOWN);
|
||||
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_LEFT);
|
||||
//KeyBindingHelper.registerKeyBinding(BINDING_RING);
|
||||
|
||||
this.ring.registerAction("keybinding", KeyBindingRingAction.FACTORY);
|
||||
this.ring.load();
|
||||
|
||||
ClientPlayNetworking.registerGlobalReceiver(CONTROLS_MODE_CHANNEL, (client, handler, buf, responseSender) ->
|
||||
responseSender.sendPacket(CONTROLS_MODE_CHANNEL, this.makeControlsModeBuffer(MidnightControlsConfig.controlsMode)));
|
||||
ClientPlayNetworking.registerGlobalReceiver(FEATURE_CHANNEL, (client, handler, buf, responseSender) -> {
|
||||
int features = buf.readVarInt();
|
||||
for (int i = 0; i < features; i++) {
|
||||
var name = buf.readString(64);
|
||||
boolean allowed = buf.readBoolean();
|
||||
MidnightControlsFeature.fromName(name).ifPresent(feature -> client.execute(() -> feature.setAllowed(allowed)));
|
||||
}
|
||||
});
|
||||
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
||||
sender.sendPacket(HELLO_CHANNEL, this.makeHello(MidnightControlsConfig.controlsMode));
|
||||
sender.sendPacket(CONTROLS_MODE_CHANNEL, this.makeControlsModeBuffer(MidnightControlsConfig.controlsMode));
|
||||
});
|
||||
ClientPlayConnectionEvents.DISCONNECT.register(this::onLeave);
|
||||
|
||||
ClientTickEvents.START_CLIENT_TICK.register(this.reacharound::tick);
|
||||
ClientTickEvents.START_CLIENT_TICK.register(this::onTick);
|
||||
|
||||
OpenScreenCallback.EVENT.register((client, screen) -> {
|
||||
if (screen == null && MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN) {
|
||||
screen = new TouchscreenOverlay(this);
|
||||
screen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
|
||||
client.skipGameRender = false;
|
||||
client.currentScreen = screen;
|
||||
} else if (screen != null) {
|
||||
this.input.onScreenOpen(client, client.getWindow().getWidth(), client.getWindow().getHeight());
|
||||
}
|
||||
});
|
||||
|
||||
HudManager.register(this.hud = new MidnightControlsHud(this));
|
||||
FabricLoader.getInstance().getModContainer("midnightcontrols").ifPresent(modContainer -> {
|
||||
ResourceManagerHelper.registerBuiltinResourcePack(new Identifier("midnightcontrols","bedrock"), modContainer, ResourcePackActivationType.NORMAL);
|
||||
ResourceManagerHelper.registerBuiltinResourcePack(new Identifier("midnightcontrols","legacy"), modContainer, ResourcePackActivationType.NORMAL);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Minecraft is initializing.
|
||||
*/
|
||||
public void onMcInit(@NotNull MinecraftClient client) {
|
||||
ButtonBinding.init(client.options);
|
||||
MidnightControlsConfig.load();
|
||||
this.hud.setVisible(MidnightControlsConfig.hudEnable);
|
||||
Controller.updateMappings();
|
||||
GLFW.glfwSetJoystickCallback((jid, event) -> {
|
||||
if (event == GLFW.GLFW_CONNECTED) {
|
||||
var controller = Controller.byId(jid);
|
||||
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, Text.translatable("midnightcontrols.controller.connected", jid),
|
||||
Text.literal(controller.getName())));
|
||||
} else if (event == GLFW.GLFW_DISCONNECTED) {
|
||||
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, Text.translatable("midnightcontrols.controller.disconnected", jid),
|
||||
null));
|
||||
}
|
||||
|
||||
this.switchControlsMode();
|
||||
});
|
||||
|
||||
MidnightControlsCompat.init(this);
|
||||
}
|
||||
ButtonCategory category;
|
||||
/**
|
||||
* This method is called to initialize keybindings
|
||||
*/
|
||||
public void initKeybindings() {
|
||||
if (lateInitDone) return;
|
||||
if (KeyBindingRegistryImplAccessor.getModdedKeyBindings() == null || KeyBindingRegistryImplAccessor.getModdedKeyBindings().isEmpty()) return;
|
||||
for (int i = 0; i < KeyBindingRegistryImplAccessor.getModdedKeyBindings().size(); ++i) {
|
||||
KeyBinding keyBinding = KeyBindingRegistryImplAccessor.getModdedKeyBindings().get(i);
|
||||
if (!keyBinding.getTranslationKey().contains("midnightcontrols") && !keyBinding.getTranslationKey().contains("ok_zoomer") && !keyBinding.getTranslationKey().contains("okzoomer")) {
|
||||
category = null;
|
||||
InputManager.streamCategories().forEach(buttonCategory -> {
|
||||
if (buttonCategory.getIdentifier().equals(new org.aperlambda.lambdacommon.Identifier("minecraft", keyBinding.getCategory())))
|
||||
category = buttonCategory;
|
||||
});
|
||||
if (category == null) {
|
||||
category = new ButtonCategory(new org.aperlambda.lambdacommon.Identifier("minecraft", keyBinding.getCategory()));
|
||||
InputManager.registerCategory(category);
|
||||
}
|
||||
ButtonBinding buttonBinding = new ButtonBinding.Builder(keyBinding.getTranslationKey()).category(category).linkKeybind(keyBinding).register();
|
||||
if (MidnightControlsConfig.debug) {
|
||||
logger.info(keyBinding.getTranslationKey());
|
||||
logger.info(buttonBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
InputManager.loadButtonBindings();
|
||||
lateInitDone = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick.
|
||||
*
|
||||
* @param client the client instance
|
||||
*/
|
||||
public void onTick(@NotNull MinecraftClient client) {
|
||||
this.initKeybindings();
|
||||
this.input.tick(client);
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput))
|
||||
this.input.tickController(client);
|
||||
|
||||
// if (BINDING_RING.wasPressed()) {
|
||||
// client.setScreen(new RingScreen());
|
||||
// }
|
||||
if (client.world != null && MidnightControlsConfig.enableHints && !MidnightControlsConfig.autoSwitchMode && MidnightControlsConfig.controlsMode == ControlsMode.DEFAULT && MidnightControlsConfig.getController().isGamepad()) {
|
||||
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.tutorial.title"),
|
||||
Text.translatable("midnightcontrols.controller.tutorial.description", Text.translatable("options.title"), Text.translatable("controls.title"),
|
||||
Text.translatable("midnightcontrols.menu.title.controller"))));
|
||||
MidnightControlsConfig.enableHints = false;
|
||||
MidnightControlsConfig.save();
|
||||
}
|
||||
}
|
||||
public void onRender(MinecraftClient client) {
|
||||
this.input.onRender(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when leaving a server.
|
||||
*/
|
||||
public void onLeave(ClientPlayNetworkHandler handler, MinecraftClient client) {
|
||||
MidnightControlsFeature.resetAllAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the controls mode if the auto switch is enabled.
|
||||
*/
|
||||
public void switchControlsMode() {
|
||||
if (MidnightControlsConfig.autoSwitchMode) {
|
||||
if (MidnightControlsConfig.getController().isGamepad()) {
|
||||
this.previousControlsMode = MidnightControlsConfig.controlsMode;
|
||||
MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
|
||||
} else {
|
||||
if (this.previousControlsMode == null) {
|
||||
this.previousControlsMode = ControlsMode.DEFAULT;
|
||||
}
|
||||
|
||||
MidnightControlsConfig.controlsMode = this.previousControlsMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the HUD is enabled or not.
|
||||
*
|
||||
* @param enabled true if the HUD is enabled, else false
|
||||
*/
|
||||
public void setHudEnabled(boolean enabled) {
|
||||
MidnightControlsConfig.hudEnable = enabled;
|
||||
this.hud.setVisible(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the midnightcontrols client instance.
|
||||
*
|
||||
* @return the midnightcontrols client instance
|
||||
*/
|
||||
public static MidnightControlsClient get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import eu.midnightdust.lib.config.MidnightConfig;
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Represents MidnightControls configuration.
|
||||
*/
|
||||
public class MidnightControlsConfig extends MidnightConfig {
|
||||
public static boolean isEditing = false;
|
||||
// General
|
||||
@Entry(name = "midnightcontrols.menu.controls_mode") public static ControlsMode controlsMode = ControlsMode.DEFAULT;
|
||||
@Entry(name = "midnightcontrols.menu.auto_switch_mode") public static boolean autoSwitchMode = true;
|
||||
@Entry(name = "Debug") public static boolean debug = false;
|
||||
// HUD
|
||||
@Entry(name = "midnightcontrols.menu.hud_enable") public static boolean hudEnable = true;
|
||||
@Entry(name = "midnightcontrols.menu.hud_side") public static HudSide hudSide = HudSide.LEFT;
|
||||
// Gameplay
|
||||
@Entry(name = "midnightcontrols.menu.analog_movement") public static boolean analogMovement = true;
|
||||
@Entry(name = "midnightcontrols.menu.double_tap_to_sprint") public static boolean doubleTapToSprint = true;
|
||||
@Entry(name = "midnightcontrols.menu.fast_block_placing") public static boolean fastBlockPlacing = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
|
||||
@Entry(name = "midnightcontrols.menu.fly_drifting") public static boolean flyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers. It can also conflict with some other mods.
|
||||
@Entry(name = "midnightcontrols.menu.fly_drifting_vertical") public static boolean verticalFlyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers.
|
||||
@Entry(name = "midnightcontrols.menu.reacharound.horizontal") public static boolean horizontalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
|
||||
@Entry(name = "midnightcontrols.menu.reacharound.vertical") public static boolean verticalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
|
||||
@Entry(name = "Reacharound Outline") public static boolean shouldRenderReacharoundOutline = true;
|
||||
@Entry(name = "Reacharound Outline Color") public static int[] reacharoundOutlineColor = new int[]{255, 255, 255, 102};
|
||||
@Entry(name = "midnightcontrols.menu.right_dead_zone") public static double rightDeadZone = 0.25;
|
||||
@Entry(name = "midnightcontrols.menu.left_dead_zone") public static double leftDeadZone = 0.25;
|
||||
@Entry(name = "midnightcontrols.menu.invert_right_y_axis") public static boolean invertRightYAxis = false;
|
||||
@Entry(name = "midnightcontrols.menu.invert_right_x_axis") public static boolean invertRightXAxis = false;
|
||||
@Entry(name = "midnightcontrols.menu.rotation_speed") public static double rotationSpeed = 40.0; //used for x axis, name kept for compatability
|
||||
@Entry(name = "midnightcontrols.menu.y_axis_rotation_speed") public static double yAxisRotationSpeed = rotationSpeed;
|
||||
@Entry(name = "midnightcontrols.menu.mouse_speed") public static double mouseSpeed = 25.0;
|
||||
@Entry(name = "midnightcontrols.menu.unfocused_input") public static boolean unfocusedInput = false;
|
||||
@Entry(name = "midnightcontrols.menu.virtual_mouse") public static boolean virtualMouse = false;
|
||||
@Entry(name = "midnightcontrols.menu.virtual_mouse.skin") public static VirtualMouseSkin virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
|
||||
@Entry(name = "Controller ID") public static Object controllerID = 0;
|
||||
@Entry(name = "2nd Controller ID") public static Object secondControllerID = -1;
|
||||
@Entry(name = "midnightcontrols.menu.controller_type") public static ControllerType controllerType = ControllerType.DEFAULT;
|
||||
@Entry(name = "Mouse screens") public static List<String> mouseScreens = Lists.newArrayList("me.jellysquid.mods.sodium.client.gui",
|
||||
"net.coderbot.iris.gui", "net.minecraft.client.gui.screen.advancement", "net.minecraft.client.gui.screen.pack.PackScreen", "net.minecraft.class_5375",
|
||||
"net.minecraft.class_457", "net.minecraft.class_408", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen",
|
||||
"hardcorequesting.client.interfaces.GuiQuestBook", "hardcorequesting.client.interfaces.GuiReward", "hardcorequesting.client.interfaces.EditTrackerScreen",
|
||||
"io.github.kosmx.emotes.arch.gui.screen.ingame.FastChosseScreen");
|
||||
@Entry(name = "Keybindings") public static Map<String, String> BINDINGS = new HashMap<>();
|
||||
|
||||
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
|
||||
@Entry(name = "Max analog values") public static double[] maxAnalogValues = new double[]{1, 1, 1, 1};
|
||||
@Entry(name = "Trigger button fix") public static boolean triggerFix = true;
|
||||
@Entry(name = "Enable Hints") public static boolean enableHints = true;
|
||||
@Entry(name = "Command Keybinds") public static List<String> commandKeybinds = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* Loads the configuration
|
||||
*/
|
||||
public static void load() {
|
||||
MidnightControlsConfig.init("midnightcontrols", MidnightControlsConfig.class);
|
||||
MidnightControlsClient.get().log("Configuration loaded.");
|
||||
// Controller controls.
|
||||
InputManager.loadButtonBindings();
|
||||
//this.mod.ring.load(this.config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the configuration.
|
||||
*/
|
||||
public static void save() {
|
||||
MidnightControlsConfig.write("midnightcontrols");
|
||||
MidnightControlsClient.get().log("Configuration saved.");
|
||||
MidnightControlsFeature.refreshEnabled();
|
||||
}
|
||||
/**
|
||||
* Gets the used controller.
|
||||
*
|
||||
* @return the controller
|
||||
*/
|
||||
public static Controller getController() {
|
||||
var raw = MidnightControlsConfig.controllerID;
|
||||
Controller controller = Controller.byId(GLFW.GLFW_JOYSTICK_1);
|
||||
if (raw instanceof Number) {
|
||||
controller = Controller.byId(((Number) raw).intValue());
|
||||
} else if (raw instanceof String) {
|
||||
controller = Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1));
|
||||
}
|
||||
if ((!controller.isConnected() || !controller.isGamepad()) && MidnightControlsConfig.autoSwitchMode && !isEditing) {
|
||||
for (int i = 0; i < GLFW.GLFW_JOYSTICK_LAST; ++i) {
|
||||
Controller gamepad = Controller.byId(i);
|
||||
if (gamepad.isConnected() && gamepad.isGamepad()) {
|
||||
controller = gamepad;
|
||||
i = GLFW_JOYSTICK_LAST;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controller.isConnected() && controller.isGamepad() && MidnightControlsConfig.autoSwitchMode && !isEditing) MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
|
||||
return controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the used controller.
|
||||
*
|
||||
* @param controller the controller
|
||||
*/
|
||||
public static void setController(Controller controller) {
|
||||
MidnightControlsConfig.controllerID = controller.id();
|
||||
MidnightControlsConfig.write("midnightcontrols");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the second controller (for Joy-Con supports).
|
||||
*
|
||||
* @return the second controller
|
||||
*/
|
||||
public static Optional<Controller> getSecondController() {
|
||||
var raw = MidnightControlsConfig.secondControllerID;
|
||||
if (raw instanceof Number) {
|
||||
if (((Number) raw).intValue() == -1)
|
||||
return Optional.empty();
|
||||
return Optional.of(Controller.byId(((Number) raw).intValue()));
|
||||
} else if (raw instanceof String) {
|
||||
return Optional.of(Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1)));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the second controller.
|
||||
*
|
||||
* @param controller the second controller
|
||||
*/
|
||||
public static void setSecondController(@Nullable Controller controller) {
|
||||
MidnightControlsConfig.secondControllerID = controller == null ? -1 : controller.id();
|
||||
}
|
||||
/**
|
||||
* Gets the right X axis sign.
|
||||
*
|
||||
* @return the right X axis sign
|
||||
*/
|
||||
public static double getRightXAxisSign() {
|
||||
return MidnightControlsConfig.invertRightXAxis ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right Y axis sign.
|
||||
*
|
||||
* @return the right Y axis sign
|
||||
*/
|
||||
public static double getRightYAxisSign() {
|
||||
return MidnightControlsConfig.invertRightYAxis ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
public static double getAxisMaxValue(int axis) {
|
||||
if (axis >= MidnightControlsConfig.maxAnalogValues.length)
|
||||
return 1;
|
||||
return MidnightControlsConfig.maxAnalogValues[axis];
|
||||
}
|
||||
|
||||
public static void setAxisMaxValue(int axis, double value) {
|
||||
if (axis < MidnightControlsConfig.maxAnalogValues.length)
|
||||
MidnightControlsConfig.maxAnalogValues[axis] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the button binding from configuration.
|
||||
*
|
||||
* @param button the button binding
|
||||
*/
|
||||
public static void loadButtonBinding(@NotNull ButtonBinding button) {
|
||||
button.setButton(button.getDefaultButton());
|
||||
var code = MidnightControlsConfig.BINDINGS.getOrDefault("controller.controls." + button.getName(), button.getButtonCode());
|
||||
|
||||
var matcher = BUTTON_BINDING_PATTERN.matcher(code);
|
||||
|
||||
try {
|
||||
var buttons = new int[1];
|
||||
int count = 0;
|
||||
while (matcher.find()) {
|
||||
count++;
|
||||
if (count > buttons.length)
|
||||
buttons = Arrays.copyOf(buttons, count);
|
||||
String current;
|
||||
if (!MidnightControlsConfig.checkValidity(button, code, current = matcher.group(1)))
|
||||
return;
|
||||
buttons[count - 1] = Integer.parseInt(current);
|
||||
}
|
||||
if (count == 0) {
|
||||
MidnightControlsClient.get().warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
|
||||
MidnightControlsConfig.setButtonBinding(button, new int[]{-1});
|
||||
}
|
||||
|
||||
button.setButton(buttons);
|
||||
} catch (Exception e) {
|
||||
MidnightControlsClient.get().warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
|
||||
MidnightControlsConfig.BINDINGS.put("controller.controls." + button.getName(), button.getButtonCode());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkValidity(@NotNull ButtonBinding binding, @NotNull String input, String group) {
|
||||
if (group == null) {
|
||||
MidnightControlsClient.get().warn("Malformed config value \"" + input + "\" for binding \"" + binding.getName() + "\".");
|
||||
MidnightControlsConfig.BINDINGS.put("controller.controls." + binding.getName(), binding.getButtonCode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the button binding in configuration.
|
||||
*
|
||||
* @param binding the button binding
|
||||
* @param button the button
|
||||
*/
|
||||
public static void setButtonBinding(@NotNull ButtonBinding binding, int[] button) {
|
||||
binding.setButton(button);
|
||||
MidnightControlsConfig.BINDINGS.put("controller.controls." + binding.getName(), binding.getButtonCode());
|
||||
}
|
||||
|
||||
public static boolean isBackButton(int btn, boolean isBtn, int state) {
|
||||
if (!isBtn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axisAsButton(btn, state == 1);
|
||||
}
|
||||
|
||||
public static boolean isForwardButton(int btn, boolean isBtn, int state) {
|
||||
if (!isBtn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axisAsButton(btn, state == 1);
|
||||
}
|
||||
|
||||
public static boolean isLeftButton(int btn, boolean isBtn, int state) {
|
||||
if (!isBtn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axisAsButton(btn, state == 1);
|
||||
}
|
||||
|
||||
public static boolean isRightButton(int btn, boolean isBtn, int state) {
|
||||
if (!isBtn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axisAsButton(btn, state == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified axis is an axis used for movements.
|
||||
*
|
||||
* @param axis the axis index
|
||||
* @return true if the axis is used for movements, else false
|
||||
*/
|
||||
public static boolean isMovementAxis(int axis) {
|
||||
return axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X;
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
controlsMode = ControlsMode.DEFAULT;
|
||||
autoSwitchMode = true;
|
||||
debug = false;
|
||||
hudEnable = true;
|
||||
hudSide = HudSide.LEFT;
|
||||
analogMovement = true;
|
||||
doubleTapToSprint = true;
|
||||
fastBlockPlacing = false;
|
||||
flyDrifting = true;
|
||||
verticalFlyDrifting = true;
|
||||
horizontalReacharound = false;
|
||||
verticalReacharound = false;
|
||||
shouldRenderReacharoundOutline = true;
|
||||
reacharoundOutlineColor = new int[]{255, 255, 255, 102};
|
||||
rightDeadZone = 0.25;
|
||||
leftDeadZone = 0.25;
|
||||
invertRightYAxis = false;
|
||||
invertRightXAxis = false;
|
||||
rotationSpeed = 40.0;
|
||||
yAxisRotationSpeed = rotationSpeed;
|
||||
mouseSpeed = 25.0;
|
||||
unfocusedInput = false;
|
||||
virtualMouse = false;
|
||||
virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
|
||||
controllerID = 0;
|
||||
secondControllerID = -1;
|
||||
controllerType = ControllerType.DEFAULT;
|
||||
mouseScreens = Lists.newArrayList("me.jellysquid.mods.sodium.client.gui", "net.coderbot.iris.gui", "net.minecraft.client.gui.screen.advancement", "net.minecraft.client.gui.screen.pack.PackScreen", "net.minecraft.class_5375", "net.minecraft.class_457", "net.minecraft.class_408", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen");
|
||||
BINDINGS = new HashMap<>();
|
||||
maxAnalogValues = new double[]{1, 1, 1, 1};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller type from the controller's identifier.
|
||||
*
|
||||
* @return the controller name matches a type, else empty
|
||||
*/
|
||||
public static @NotNull ControllerType matchControllerToType() {
|
||||
String controller = getController().getName().toLowerCase();
|
||||
if (controller.contains("xbox 360")) return ControllerType.XBOX_360;
|
||||
else if (controller.contains("xbox") || controller.contains("afterglow")) return ControllerType.XBOX;
|
||||
else if (controller.contains("steam deck")) return ControllerType.STEAM_DECK;
|
||||
else if (controller.contains("steam")) return ControllerType.STEAM_CONTROLLER;
|
||||
else if (controller.contains("dualsense")) return ControllerType.DUALSENSE;
|
||||
else if (controller.contains("dualshock") || controller.contains("ps4")) return ControllerType.DUALSHOCK;
|
||||
else if (controller.contains("switch") || controller.contains("joy-con")) return ControllerType.SWITCH;
|
||||
else if (controller.contains("ouya")) return ControllerType.OUYA;
|
||||
else return ControllerType.DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
|
||||
|
||||
/**
|
||||
* Represents the API implementation of ModMenu for midnightcontrols.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class MidnightControlsModMenu implements ModMenuApi {
|
||||
@Override
|
||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||
return parent -> new MidnightControlsSettingsScreen(parent, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import dev.lambdaurora.spruceui.widget.AbstractSpruceWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceEntryListWidget;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.LibGuiCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.SodiumCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
|
||||
import eu.midnightdust.midnightcontrols.client.mixin.*;
|
||||
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.util.MouseAccessor;
|
||||
import dev.lambdaurora.spruceui.navigation.NavigationDirection;
|
||||
import dev.lambdaurora.spruceui.screen.SpruceScreen;
|
||||
import dev.lambdaurora.spruceui.widget.AbstractSprucePressableButtonWidget;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceElement;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceLabelWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceParentWidget;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.ParentElement;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
|
||||
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
|
||||
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
|
||||
import net.minecraft.client.gui.screen.world.WorldListWidget;
|
||||
import net.minecraft.client.gui.widget.*;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.aperlambda.lambdacommon.utils.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWGamepadState;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Represents the midnightcontrols' input handler.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class MidnightInput {
|
||||
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
|
||||
// Cooldowns
|
||||
public int actionGuiCooldown = 0;
|
||||
public boolean ignoreNextARelease = false;
|
||||
private double targetYaw = 0.0;
|
||||
private double targetPitch = 0.0;
|
||||
private float prevXAxis = 0.f;
|
||||
private float prevYAxis = 0.f;
|
||||
private int targetMouseX = 0;
|
||||
private int targetMouseY = 0;
|
||||
private float mouseSpeedX = 0.f;
|
||||
private float mouseSpeedY = 0.f;
|
||||
public int inventoryInteractionCooldown = 0;
|
||||
public int screenCloseCooldown = 0;
|
||||
|
||||
private ControllerControlsWidget controlsInput = null;
|
||||
|
||||
public MidnightInput() {}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick.
|
||||
*
|
||||
* @param client the client instance
|
||||
*/
|
||||
public void tick(@NotNull MinecraftClient client) {
|
||||
this.targetYaw = 0.F;
|
||||
this.targetPitch = 0.F;
|
||||
|
||||
// Handles the key bindings.
|
||||
if (MidnightControlsClient.BINDING_LOOK_UP.isPressed()) {
|
||||
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
|
||||
} else if (MidnightControlsClient.BINDING_LOOK_DOWN.isPressed()) {
|
||||
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
|
||||
}
|
||||
if (MidnightControlsClient.BINDING_LOOK_LEFT.isPressed()) {
|
||||
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
|
||||
} else if (MidnightControlsClient.BINDING_LOOK_RIGHT.isPressed()) {
|
||||
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 1);
|
||||
}
|
||||
|
||||
InputManager.INPUT_MANAGER.tick(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick for controller input update.
|
||||
*
|
||||
* @param client the client instance
|
||||
*/
|
||||
public void tickController(@NotNull MinecraftClient client) {
|
||||
BUTTON_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0)
|
||||
.forEach(entry -> BUTTON_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
|
||||
// Decreases the cooldown for GUI actions.
|
||||
if (this.actionGuiCooldown > 0)
|
||||
--this.actionGuiCooldown;
|
||||
if (this.screenCloseCooldown > 0)
|
||||
--this.screenCloseCooldown;
|
||||
|
||||
InputManager.updateStates();
|
||||
|
||||
var controller = MidnightControlsConfig.getController();
|
||||
|
||||
if (controller.isConnected()) {
|
||||
var state = controller.getState();
|
||||
this.fetchButtonInput(client, state, false);
|
||||
this.fetchAxeInput(client, state, false);
|
||||
}
|
||||
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
|
||||
.ifPresent(joycon -> {
|
||||
GLFWGamepadState state = joycon.getState();
|
||||
this.fetchButtonInput(client, state, true);
|
||||
this.fetchAxeInput(client, state, true);
|
||||
});
|
||||
|
||||
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
|
||||
|
||||
if (allowInput)
|
||||
InputManager.updateBindings(client);
|
||||
|
||||
if (this.controlsInput != null
|
||||
&& InputManager.STATES.int2ObjectEntrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::isUnpressed)) {
|
||||
if (this.controlsInput.focusedBinding != null && !this.controlsInput.waiting) {
|
||||
int[] buttons = new int[this.controlsInput.currentButtons.size()];
|
||||
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
|
||||
buttons[i] = this.controlsInput.currentButtons.get(i);
|
||||
this.controlsInput.finishBindingEdit(buttons);
|
||||
this.controlsInput = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.inventoryInteractionCooldown > 0)
|
||||
this.inventoryInteractionCooldown--;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before the screen is rendered.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param screen the screen to render
|
||||
*/
|
||||
public void onPreRenderScreen(@NotNull MinecraftClient client, @NotNull Screen screen) {
|
||||
if (!isScreenInteractive(screen)) {
|
||||
InputManager.INPUT_MANAGER.updateMousePosition(client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Minecraft renders.
|
||||
*
|
||||
* @param client the client instance
|
||||
*/
|
||||
public void onRender(@NotNull MinecraftClient client) {
|
||||
if (!(client.currentScreen == null || client.currentScreen instanceof TouchscreenOverlay))
|
||||
return;
|
||||
|
||||
var player = client.player;
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
if (this.targetYaw != 0.f || this.targetPitch != 0.f) {
|
||||
float rotationYaw = (float) (player.prevYaw + (this.targetYaw / 0.10) * client.getTickDelta());
|
||||
float rotationPitch = (float) (player.prevPitch + (this.targetPitch / 0.10) * client.getTickDelta());
|
||||
client.player.setYaw(rotationYaw);
|
||||
client.player.setPitch(MathHelper.clamp(rotationPitch, -90.f, 90.f));
|
||||
if (client.player.isRiding() && client.player.getVehicle() != null) {
|
||||
client.player.getVehicle().onPassengerLookAround(client.player);
|
||||
}
|
||||
client.getTutorialManager().onUpdateMouse(this.targetPitch, this.targetYaw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a Screen is opened.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param windowWidth the window width
|
||||
* @param windowHeight the window height
|
||||
*/
|
||||
public void onScreenOpen(@NotNull MinecraftClient client, int windowWidth, int windowHeight) {
|
||||
if (client.currentScreen == null) {
|
||||
this.mouseSpeedX = this.mouseSpeedY = 0.0F;
|
||||
InputManager.INPUT_MANAGER.resetMousePosition(windowWidth, windowHeight);
|
||||
} else if (isScreenInteractive(client.currentScreen) && MidnightControlsConfig.virtualMouse) {
|
||||
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), 0, 0);
|
||||
InputManager.INPUT_MANAGER.resetMouseTarget(client);
|
||||
}
|
||||
this.inventoryInteractionCooldown = 5;
|
||||
}
|
||||
|
||||
public void beginControlsInput(ControllerControlsWidget widget) {
|
||||
this.controlsInput = widget;
|
||||
if (widget != null) {
|
||||
this.controlsInput.currentButtons.clear();
|
||||
this.controlsInput.waiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchButtonInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
|
||||
var buffer = gamepadState.buttons();
|
||||
for (int i = 0; i < buffer.limit(); i++) {
|
||||
int btn = leftJoycon ? ButtonBinding.controller2Button(i) : i;
|
||||
boolean btnState = buffer.get() == (byte) 1;
|
||||
var state = ButtonState.NONE;
|
||||
var previousState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
|
||||
|
||||
if (btnState != previousState.isPressed()) {
|
||||
state = btnState ? ButtonState.PRESS : ButtonState.RELEASE;
|
||||
this.handleButton(client, btn, btnState ? 0 : 1, btnState);
|
||||
if (btnState)
|
||||
BUTTON_COOLDOWNS.put(btn, 5);
|
||||
} else if (btnState) {
|
||||
state = ButtonState.REPEAT;
|
||||
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
|
||||
BUTTON_COOLDOWNS.put(btn, 5);
|
||||
this.handleButton(client, btn, 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
InputManager.STATES.put(btn, state);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchAxeInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
|
||||
var buffer = gamepadState.axes();
|
||||
for (int i = 0; i < buffer.limit(); i++) {
|
||||
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
|
||||
float value = buffer.get();
|
||||
float absValue = Math.abs(value);
|
||||
|
||||
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
|
||||
value *= -1.0F;
|
||||
|
||||
int state = value > MidnightControlsConfig.rightDeadZone ? 1 : (value < -MidnightControlsConfig.rightDeadZone ? 2 : 0);
|
||||
this.handleAxe(client, axis, value, absValue, state);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleButton(@NotNull MinecraftClient client, int button, int action, boolean state) {
|
||||
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
|
||||
if (action == 0 && !this.controlsInput.currentButtons.contains(button)) {
|
||||
this.controlsInput.currentButtons.add(button);
|
||||
|
||||
var buttons = new int[this.controlsInput.currentButtons.size()];
|
||||
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
|
||||
buttons[i] = this.controlsInput.currentButtons.get(i);
|
||||
this.controlsInput.focusedBinding.setButton(buttons);
|
||||
|
||||
this.controlsInput.waiting = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == 0 || action == 2) {
|
||||
if (client.currentScreen != null
|
||||
&& (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN
|
||||
|| button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) {
|
||||
if (this.actionGuiCooldown == 0) {
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) {
|
||||
this.changeFocus(client.currentScreen, NavigationDirection.UP);
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) {
|
||||
this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
|
||||
} else this.handleLeftRight(client.currentScreen, button != GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == 1) {
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) {
|
||||
if (this.actionGuiCooldown == 0) {
|
||||
var focused = client.currentScreen.getFocused();
|
||||
if (focused != null && isScreenInteractive(client.currentScreen)) {
|
||||
if (this.handleAButton(client.currentScreen, focused)) {
|
||||
this.actionGuiCooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (FabricLoader.getInstance().isModLoaded("libgui")) LibGuiCompat.handlePress(client.currentScreen);
|
||||
}
|
||||
}
|
||||
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
|
||||
if (client.currentScreen != null && client.currentScreen.getClass() != TitleScreen.class) {
|
||||
if (!MidnightControlsCompat.handleMenuBack(client, client.currentScreen))
|
||||
if (!this.tryGoBack(client.currentScreen))
|
||||
client.currentScreen.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !isScreenInteractive(client.currentScreen)
|
||||
&& this.actionGuiCooldown == 0) {
|
||||
if (client.currentScreen instanceof HandledScreen<?> handledScreen && ((HandledScreenAccessor) handledScreen).midnightcontrols$getSlotAt(
|
||||
client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth(),
|
||||
client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()) != null) return;
|
||||
if (!this.ignoreNextARelease) {
|
||||
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
|
||||
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
|
||||
if (action == 0) {
|
||||
Screen.wrapScreenError(() -> client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1),
|
||||
"mouseClicked event handler", client.currentScreen.getClass().getCanonicalName());
|
||||
} else if (action == 1) {
|
||||
Screen.wrapScreenError(() -> client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1),
|
||||
"mouseReleased event handler", client.currentScreen.getClass().getCanonicalName());
|
||||
}
|
||||
this.screenCloseCooldown = 5;
|
||||
} else {
|
||||
this.ignoreNextARelease = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
/**
|
||||
* Tries to go back.
|
||||
*
|
||||
* @param screen the current screen
|
||||
* @return true if successful, else false
|
||||
*/
|
||||
public boolean tryGoBack(@NotNull Screen screen) {
|
||||
var set = ImmutableSet.of("gui.back", "gui.done", "gui.cancel", "gui.toTitle", "gui.toMenu");
|
||||
return screen.children().stream().filter(element -> element instanceof PressableWidget)
|
||||
.map(element -> (PressableWidget) element)
|
||||
.filter(element -> element.getMessage() != null && element.getMessage().getContent() != null)
|
||||
.anyMatch(element -> {
|
||||
if (set.stream().anyMatch(key -> element.getMessage().getContent().toString().equals(key))) {
|
||||
element.onPress();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private double getDeadZoneValue(int axis) {
|
||||
return (axis == GLFW_GAMEPAD_AXIS_LEFT_X || axis == GLFW_GAMEPAD_AXIS_LEFT_Y) ? MidnightControlsConfig.leftDeadZone
|
||||
: MidnightControlsConfig.rightDeadZone;
|
||||
}
|
||||
|
||||
private void handleAxe(@NotNull MinecraftClient client, int axis, float value, float absValue, int state) {
|
||||
int asButtonState = value > .5f ? 1 : (value < -.5f ? 2 : 0);
|
||||
|
||||
|
||||
if (axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER
|
||||
|| axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER)
|
||||
|| axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
|
||||
if (asButtonState == 2) {
|
||||
asButtonState = 0;
|
||||
}
|
||||
else {
|
||||
// Fixes Triggers not working correctly on some controllers
|
||||
if (MidnightControlsConfig.triggerFix) {
|
||||
value = 1.0f;
|
||||
absValue = 1.0f;
|
||||
state = 1;
|
||||
asButtonState = 1;
|
||||
}
|
||||
if (MidnightControlsConfig.debug) System.out.println(axis + " "+ value + " " + absValue + " " + state);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
boolean currentPlusState = asButtonState == 1;
|
||||
boolean currentMinusState = asButtonState == 2;
|
||||
var previousPlusState = InputManager.STATES.getOrDefault(ButtonBinding.axisAsButton(axis, true), ButtonState.NONE);
|
||||
var previousMinusState = InputManager.STATES.getOrDefault(ButtonBinding.axisAsButton(axis, false), ButtonState.NONE);
|
||||
|
||||
if (currentPlusState != previousPlusState.isPressed()) {
|
||||
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, true), currentPlusState ? ButtonState.PRESS : ButtonState.RELEASE);
|
||||
if (currentPlusState)
|
||||
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, true), 5);
|
||||
} else if (currentPlusState) {
|
||||
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, true), ButtonState.REPEAT);
|
||||
if (BUTTON_COOLDOWNS.getOrDefault(ButtonBinding.axisAsButton(axis, true), 0) == 0) {
|
||||
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, true), 5);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMinusState != previousMinusState.isPressed()) {
|
||||
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, false), currentMinusState ? ButtonState.PRESS : ButtonState.RELEASE);
|
||||
if (currentMinusState)
|
||||
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, false), 5);
|
||||
} else if (currentMinusState) {
|
||||
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, false), ButtonState.REPEAT);
|
||||
if (BUTTON_COOLDOWNS.getOrDefault(ButtonBinding.axisAsButton(axis, false), 0) == 0) {
|
||||
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, false), 5);
|
||||
}
|
||||
}
|
||||
|
||||
double deadZone = this.getDeadZoneValue(axis);
|
||||
float axisValue = absValue < deadZone ? 0.f : (float) (absValue - deadZone);
|
||||
axisValue /= (1.0 - deadZone);
|
||||
|
||||
axisValue = (float) Math.min(axisValue / MidnightControlsConfig.getAxisMaxValue(axis), 1);
|
||||
if (currentPlusState)
|
||||
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, true), axisValue);
|
||||
else
|
||||
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, true), 0.f);
|
||||
if (currentMinusState)
|
||||
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, false), axisValue);
|
||||
else
|
||||
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, false), 0.f);
|
||||
}
|
||||
|
||||
double deadZone = this.getDeadZoneValue(axis);
|
||||
|
||||
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
|
||||
if (asButtonState != 0 && !this.controlsInput.currentButtons.contains(ButtonBinding.axisAsButton(axis, asButtonState == 1))) {
|
||||
|
||||
this.controlsInput.currentButtons.add(ButtonBinding.axisAsButton(axis, asButtonState == 1));
|
||||
|
||||
int[] buttons = new int[this.controlsInput.currentButtons.size()];
|
||||
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
|
||||
buttons[i] = this.controlsInput.currentButtons.get(i);
|
||||
this.controlsInput.focusedBinding.setButton(buttons);
|
||||
|
||||
this.controlsInput.waiting = false;
|
||||
}
|
||||
return;
|
||||
} else if (client.currentScreen instanceof CreativeInventoryScreen creativeInventoryScreen) {
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||
var accessor = (CreativeInventoryScreenAccessor) creativeInventoryScreen;
|
||||
// @TODO allow rebinding to left stick
|
||||
if (accessor.midnightcontrols$hasScrollbar() && absValue >= deadZone) {
|
||||
creativeInventoryScreen.mouseScrolled(0.0, 0.0, -value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (client.currentScreen instanceof MerchantScreen merchantScreen) {
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||
// @TODO allow rebinding to left stick
|
||||
if (absValue >= deadZone) {
|
||||
merchantScreen.mouseScrolled(0.0, 0.0, -(value * 1.5f));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (client.currentScreen instanceof AdvancementsScreen advancementsScreen) {
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X || axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||
var accessor = (AdvancementsScreenAccessor) advancementsScreen;
|
||||
if (absValue >= deadZone) {
|
||||
AdvancementTab tab = accessor.getSelectedTab();
|
||||
tab.move(axis == GLFW_GAMEPAD_AXIS_RIGHT_X ? -value * 5.0 : 0.0, axis == GLFW_GAMEPAD_AXIS_RIGHT_Y ? -value * 5.0 : 0.0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (client.currentScreen != null) {
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y && absValue >= deadZone) {
|
||||
float finalValue = value;
|
||||
client.currentScreen.children().stream().filter(element -> element instanceof SpruceEntryListWidget)
|
||||
.map(element -> (SpruceEntryListWidget<?>) element)
|
||||
.filter(AbstractSpruceWidget::isFocusedOrHovered)
|
||||
.anyMatch(element -> {
|
||||
MidnightControls.get().log(String.valueOf(finalValue));
|
||||
element.mouseScrolled(0.0, 0.0, -finalValue);
|
||||
return true;
|
||||
});
|
||||
client.currentScreen.children().stream().filter(element -> element instanceof EntryListWidget)
|
||||
.map(element -> (EntryListWidget<?>) element)
|
||||
.filter(element -> element.getType().isFocused())
|
||||
.anyMatch(element -> {
|
||||
element.mouseScrolled(0.0, 0.0, -finalValue);
|
||||
return true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
absValue -= deadZone;
|
||||
absValue /= (1.0 - deadZone);
|
||||
absValue = (float) MathHelper.clamp(absValue / MidnightControlsConfig.getAxisMaxValue(axis), 0.f, 1.f);
|
||||
if (client.currentScreen == null) {
|
||||
// Handles the look direction.
|
||||
this.handleLook(client, axis, absValue, state);
|
||||
} else {
|
||||
boolean allowMouseControl = true;
|
||||
|
||||
if (this.actionGuiCooldown == 0 && MidnightControlsConfig.isMovementAxis(axis) && isScreenInteractive(client.currentScreen)) {
|
||||
if (MidnightControlsConfig.isForwardButton(axis, false, asButtonState)) {
|
||||
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.UP);
|
||||
} else if (MidnightControlsConfig.isBackButton(axis, false, asButtonState)) {
|
||||
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
|
||||
} else if (MidnightControlsConfig.isLeftButton(axis, false, asButtonState)) {
|
||||
allowMouseControl = this.handleLeftRight(client.currentScreen, false);
|
||||
} else if (MidnightControlsConfig.isRightButton(axis, false, asButtonState)) {
|
||||
allowMouseControl = this.handleLeftRight(client.currentScreen, true);
|
||||
}
|
||||
}
|
||||
|
||||
float movementX = 0.f;
|
||||
float movementY = 0.f;
|
||||
|
||||
if (MidnightControlsConfig.isBackButton(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movementY = absValue;
|
||||
} else if (MidnightControlsConfig.isForwardButton(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movementY = -absValue;
|
||||
} else if (MidnightControlsConfig.isLeftButton(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movementX = -absValue;
|
||||
} else if (MidnightControlsConfig.isRightButton(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movementX = absValue;
|
||||
}
|
||||
|
||||
if (client.currentScreen != null && allowMouseControl) {
|
||||
boolean moving = movementY != 0 || movementX != 0;
|
||||
if (moving) {
|
||||
/*
|
||||
Updates the target mouse position when the initial movement stick movement is detected.
|
||||
It prevents the cursor to jump to the old target mouse position if the user moves the cursor with the mouse.
|
||||
*/
|
||||
if (Math.abs(prevXAxis) < deadZone && Math.abs(prevYAxis) < deadZone) {
|
||||
InputManager.INPUT_MANAGER.resetMouseTarget(client);
|
||||
}
|
||||
|
||||
this.mouseSpeedX = movementX;
|
||||
this.mouseSpeedY = movementY;
|
||||
} else {
|
||||
this.mouseSpeedX = 0.f;
|
||||
this.mouseSpeedY = 0.f;
|
||||
}
|
||||
|
||||
if (Math.abs(this.mouseSpeedX) >= .05f || Math.abs(this.mouseSpeedY) >= .05f) {
|
||||
InputManager.queueMoveMousePosition(
|
||||
this.mouseSpeedX * MidnightControlsConfig.mouseSpeed,
|
||||
this.mouseSpeedY * MidnightControlsConfig.mouseSpeed
|
||||
);
|
||||
}
|
||||
|
||||
this.moveMouseToClosestSlot(client, client.currentScreen);
|
||||
}
|
||||
|
||||
this.prevXAxis = movementX;
|
||||
this.prevYAxis = movementY;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handleAButton(@NotNull Screen screen, @NotNull Element focused) {
|
||||
if (focused instanceof PressableWidget widget) {
|
||||
widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
|
||||
widget.onPress();
|
||||
return true;
|
||||
} else if (focused instanceof AbstractSprucePressableButtonWidget widget) {
|
||||
widget.playDownSound();
|
||||
widget.onPress();
|
||||
return true;
|
||||
} else if (focused instanceof SpruceLabelWidget labelWidget) {
|
||||
labelWidget.onPress();
|
||||
return true;
|
||||
} else if (focused instanceof WorldListWidget list) {
|
||||
list.getSelectedAsOptional().ifPresent(WorldListWidget.WorldEntry::play);
|
||||
return true;
|
||||
} else if (focused instanceof MultiplayerServerListWidget list) {
|
||||
var entry = list.getSelectedOrNull();
|
||||
if (entry instanceof MultiplayerServerListWidget.LanServerEntry || entry instanceof MultiplayerServerListWidget.ServerEntry) {
|
||||
((MultiplayerScreen) screen).select(entry);
|
||||
((MultiplayerScreen) screen).connect();
|
||||
}
|
||||
} else if (focused instanceof SpruceParentWidget) {
|
||||
var childFocused = ((SpruceParentWidget<?>) focused).getFocused();
|
||||
if (childFocused != null)
|
||||
return this.handleAButton(screen, childFocused);
|
||||
} else if (focused instanceof ParentElement widget) {
|
||||
var childFocused = widget.getFocused();
|
||||
if (childFocused != null)
|
||||
return this.handleAButton(screen, childFocused);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the left and right buttons.
|
||||
*
|
||||
* @param screen the current screen
|
||||
* @param right true if the right button is pressed, else false
|
||||
*/
|
||||
private boolean handleLeftRight(@NotNull Screen screen, boolean right) {
|
||||
if (screen instanceof SpruceScreen spruceScreen) {
|
||||
spruceScreen.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
|
||||
this.actionGuiCooldown = 5;
|
||||
return false;
|
||||
}
|
||||
var focused = screen.getFocused();
|
||||
if (focused != null)
|
||||
if (this.handleRightLeftElement(focused, right))
|
||||
return this.changeFocus(screen, right ? NavigationDirection.RIGHT : NavigationDirection.LEFT);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleRightLeftElement(@NotNull Element element, boolean right) {
|
||||
if (element instanceof SpruceElement spruceElement) {
|
||||
if (spruceElement.requiresCursor())
|
||||
return true;
|
||||
return !spruceElement.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
|
||||
}
|
||||
if (element instanceof SliderWidget slider) {
|
||||
slider.keyPressed(right ? 262 : 263, 0, 0);
|
||||
this.actionGuiCooldown = 2; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
|
||||
return false;
|
||||
} else if (element instanceof AlwaysSelectedEntryListWidget) {
|
||||
((EntryListWidgetAccessor) element).midnightcontrols$moveSelection(right ? EntryListWidget.MoveDirection.DOWN : EntryListWidget.MoveDirection.UP);
|
||||
return false;
|
||||
} else if (element instanceof ParentElement entryList) {
|
||||
var focused = entryList.getFocused();
|
||||
if (focused == null)
|
||||
return true;
|
||||
return this.handleRightLeftElement(focused, right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the look direction input.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param axis the axis to change
|
||||
* @param value the value of the look
|
||||
* @param state the state
|
||||
*/
|
||||
public void handleLook(@NotNull MinecraftClient client, int axis, float value, int state) {
|
||||
// Handles the look direction.
|
||||
if (client.player != null) {
|
||||
double powValue = Math.pow(value, 2.0);
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||
if (state == 2) {
|
||||
this.targetPitch = -MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powValue) * 0.11D;
|
||||
} else if (state == 1) {
|
||||
this.targetPitch = MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powValue) * 0.11D;
|
||||
}
|
||||
}
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
|
||||
if (state == 2) {
|
||||
this.targetYaw = -MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powValue) * 0.11D;
|
||||
} else if (state == 1) {
|
||||
this.targetYaw = MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powValue) * 0.11D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean changeFocus(@NotNull Screen screen, NavigationDirection direction) {
|
||||
if (!isScreenInteractive(screen) && !screen.getClass().getCanonicalName().contains("me.jellysquid.mods.sodium.client.gui")) return false;
|
||||
try {
|
||||
if (screen instanceof SpruceScreen spruceScreen) {
|
||||
if (spruceScreen.onNavigation(direction, false)) {
|
||||
this.actionGuiCooldown = 5;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (FabricLoader.getInstance().isModLoaded("sodium"))
|
||||
SodiumCompat.handleInput(screen, direction.isLookingForward());
|
||||
// This still needs some work
|
||||
// ScreenAccessor accessor = (ScreenAccessor) screen;
|
||||
// if (accessor.getSelected() != null && accessor.getSelected() instanceof ClickableWidget selected && accessor.getSelectables() != null) {
|
||||
// //System.out.println(direction);
|
||||
// if (accessor.getSelectables().size() >= 2) {
|
||||
// //System.out.println(direction + " 2");
|
||||
// int xDifference = Integer.MAX_VALUE;
|
||||
// int yDifference = Integer.MAX_VALUE;
|
||||
// ClickableWidget newWidget = null;
|
||||
// for (int i = 0; i < accessor.getSelectables().size(); ++i) {
|
||||
// if (accessor.getSelectables().get(i) instanceof ClickableWidget candidate) {
|
||||
// if (!(candidate.x == selected.x && candidate.y == selected.y)) {
|
||||
// int canXDifference = Math.abs(candidate.x - selected.x);
|
||||
// int canYDifference = Math.abs(candidate.y - selected.y);
|
||||
// if (direction.isHorizontal()) {
|
||||
// if (direction.isLookingForward()) {
|
||||
// if (candidate.x >= selected.x && canYDifference <= yDifference && canXDifference <= xDifference) {
|
||||
// System.out.println(direction + " forward horizontal " + candidate);
|
||||
// newWidget = candidate;
|
||||
// xDifference = canXDifference;
|
||||
// yDifference = canYDifference;
|
||||
// }
|
||||
// } else {
|
||||
// if (candidate.x <= selected.x && canYDifference <= yDifference && canXDifference >= xDifference) {
|
||||
// System.out.println(direction + " backward horizontal " + candidate);
|
||||
// newWidget = candidate;
|
||||
// xDifference = canXDifference;
|
||||
// yDifference = canYDifference;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if (direction.isLookingForward()) {
|
||||
// if (candidate.y >= selected.y && canYDifference <= yDifference && canXDifference <= xDifference) {
|
||||
// System.out.println(direction + " forward vertical " + candidate);
|
||||
// newWidget = candidate;
|
||||
// xDifference = canXDifference;
|
||||
// yDifference = canYDifference;
|
||||
// }
|
||||
// } else {
|
||||
// if (candidate.y <= selected.y && canYDifference >= yDifference && canXDifference <= xDifference) {
|
||||
// System.out.println(direction + " backward vertical " + candidate);
|
||||
// newWidget = candidate;
|
||||
// xDifference = canXDifference;
|
||||
// yDifference = canYDifference;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (newWidget != null) {
|
||||
// //selected.changeFocus(true);
|
||||
// ((ClickableWidgetAccessor) selected).setFocused(false);
|
||||
// ((ClickableWidgetAccessor) newWidget).setFocused(true);
|
||||
// screen.setFocused(newWidget);
|
||||
// screen.changeFocus(false);
|
||||
// screen.changeFocus(true);
|
||||
// this.actionGuiCooldown = 5;
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
if (!screen.changeFocus(direction.isLookingForward())) {
|
||||
if (screen.changeFocus(direction.isLookingForward())) {
|
||||
this.actionGuiCooldown = 5;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
this.actionGuiCooldown = 5;
|
||||
return false;
|
||||
}
|
||||
} catch (Exception exception) {MidnightControls.get().warn("Unknown exception encountered while trying to change focus: "+exception);}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isScreenInteractive(@NotNull Screen screen) {
|
||||
return !(screen instanceof HandledScreen || MidnightControlsConfig.mouseScreens.stream().anyMatch(a -> screen.getClass().toString().contains(a))
|
||||
|| (screen instanceof SpruceScreen && ((SpruceScreen) screen).requiresCursor())
|
||||
|| MidnightControlsCompat.requireMouseOnScreen(screen));
|
||||
}
|
||||
|
||||
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
|
||||
private void moveMouseToClosestSlot(@NotNull MinecraftClient client, @Nullable Screen screen) {
|
||||
// Makes the mouse attracted to slots. This helps with selecting items when using a controller.
|
||||
if (screen instanceof HandledScreen inventoryScreen) {
|
||||
var accessor = (HandledScreenAccessor) inventoryScreen;
|
||||
int guiLeft = accessor.getX();
|
||||
int guiTop = accessor.getY();
|
||||
int mouseX = (int) (targetMouseX * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
|
||||
int mouseY = (int) (targetMouseY * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
|
||||
|
||||
// Finds the closest slot in the GUI within 14 pixels.
|
||||
Optional<Pair<Slot, Double>> closestSlot = inventoryScreen.getScreenHandler().slots.parallelStream()
|
||||
.map(slot -> {
|
||||
int x = guiLeft + slot.x + 8;
|
||||
int y = guiTop + slot.y + 8;
|
||||
|
||||
// Distance between the slot and the cursor.
|
||||
double distance = Math.sqrt(Math.pow(x - mouseX, 2) + Math.pow(y - mouseY, 2));
|
||||
return Pair.of(slot, distance);
|
||||
}).filter(entry -> entry.value <= 14.0)
|
||||
.min(Comparator.comparingDouble(p -> p.value));
|
||||
|
||||
if (closestSlot.isPresent()) {
|
||||
var slot = closestSlot.get().key;
|
||||
if (slot.hasStack() || !client.player.getInventory().getMainHandStack().isEmpty()) {
|
||||
int slotCenterXScaled = guiLeft + slot.x + 8;
|
||||
int slotCenterYScaled = guiTop + slot.y + 8;
|
||||
int slotCenterX = (int) (slotCenterXScaled / ((double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));
|
||||
int slotCenterY = (int) (slotCenterYScaled / ((double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()));
|
||||
double deltaX = slotCenterX - targetMouseX;
|
||||
double deltaY = slotCenterY - targetMouseY;
|
||||
|
||||
if (mouseX != slotCenterXScaled || mouseY != slotCenterYScaled) {
|
||||
this.targetMouseX += deltaX * 0.75;
|
||||
this.targetMouseY += deltaY * 0.75;
|
||||
} else {
|
||||
this.mouseSpeedX *= 0.3F;
|
||||
this.mouseSpeedY *= 0.3F;
|
||||
}
|
||||
this.mouseSpeedX *= .75F;
|
||||
this.mouseSpeedY *= .75F;
|
||||
} else {
|
||||
this.mouseSpeedX *= .1F;
|
||||
this.mouseSpeedY *= .1F;
|
||||
}
|
||||
} else {
|
||||
this.mouseSpeedX *= .3F;
|
||||
this.mouseSpeedY *= .3F;
|
||||
}
|
||||
} else {
|
||||
this.mouseSpeedX = 0.F;
|
||||
this.mouseSpeedY = 0.F;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FluidBlock;
|
||||
import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.RaycastContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the reach-around API of midnightcontrols.
|
||||
*
|
||||
* @version 1.7.0
|
||||
* @since 1.3.2
|
||||
*/
|
||||
public class MidnightReacharound {
|
||||
private BlockHitResult lastReacharoundResult = null;
|
||||
private boolean lastReacharoundVertical = false;
|
||||
private boolean onSlab = false;
|
||||
|
||||
public void tick(@NotNull MinecraftClient client) {
|
||||
this.lastReacharoundResult = this.tryVerticalReachAround(client);
|
||||
if (this.lastReacharoundResult == null) {
|
||||
this.lastReacharoundResult = this.tryHorizontalReachAround(client);
|
||||
this.lastReacharoundVertical = false;
|
||||
} else this.lastReacharoundVertical = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last reach around result.
|
||||
*
|
||||
* @return the last reach around result
|
||||
*/
|
||||
public @Nullable BlockHitResult getLastReacharoundResult() {
|
||||
return this.lastReacharoundResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the last reach around is vertical.
|
||||
*
|
||||
* @return {@code true} if the reach around is vertical
|
||||
*/
|
||||
public boolean isLastReacharoundVertical() {
|
||||
return this.lastReacharoundVertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether reacharound is available or not.
|
||||
*
|
||||
* @return {@code true} if reacharound is available, else {@code false}
|
||||
*/
|
||||
public boolean isReacharoundAvailable() {
|
||||
return MidnightControlsFeature.HORIZONTAL_REACHAROUND.isAvailable() || MidnightControlsFeature.VERTICAL_REACHAROUND.isAvailable();
|
||||
}
|
||||
|
||||
private float getPlayerRange(@NotNull MinecraftClient client) {
|
||||
return client.interactionManager != null ? client.interactionManager.getReachDistance() : 0.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nullable block hit result if vertical reach-around is possible.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @return a block hit result if vertical reach-around is possible, else {@code null}
|
||||
*/
|
||||
public @Nullable BlockHitResult tryVerticalReachAround(@NotNull MinecraftClient client) {
|
||||
if (!MidnightControlsFeature.VERTICAL_REACHAROUND.isAvailable())
|
||||
return null;
|
||||
if (client.player == null || client.world == null || client.crosshairTarget == null || client.crosshairTarget.getType() != HitResult.Type.MISS
|
||||
|| !client.player.isOnGround() || client.player.getPitch(0.f) < 80.0F
|
||||
|| client.player.isRiding())
|
||||
return null;
|
||||
|
||||
Vec3d pos = client.player.getCameraPosVec(1.0F);
|
||||
Vec3d rotationVec = client.player.getRotationVec(1.0F);
|
||||
float range = getPlayerRange(client);
|
||||
var rayVec = pos.add(rotationVec.x * range, rotationVec.y * range, rotationVec.z * range).add(0, 0.75, 0);
|
||||
var result = client.world.raycast(new RaycastContext(pos, rayVec, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, client.player));
|
||||
|
||||
if (result.getType() == HitResult.Type.BLOCK) {
|
||||
BlockPos blockPos = result.getBlockPos().down();
|
||||
BlockState state = client.world.getBlockState(blockPos);
|
||||
|
||||
if (client.player.getBlockPos().getY() - blockPos.getY() > 1 && (client.world.isAir(blockPos) || state.getMaterial().isReplaceable())) {
|
||||
return new BlockHitResult(result.getPos(), Direction.DOWN, blockPos, false);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nullable block hit result if horizontal reach-around is possible.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @return a block hit result if horizontal reach-around is possible
|
||||
*/
|
||||
public @Nullable BlockHitResult tryHorizontalReachAround(@NotNull MinecraftClient client) {
|
||||
if (!MidnightControlsFeature.HORIZONTAL_REACHAROUND.isAvailable())
|
||||
return null;
|
||||
|
||||
if (client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS
|
||||
&& client.player.isOnGround() && client.player.getPitch(0.f) > 35.f) {
|
||||
if (client.player.isRiding())
|
||||
return null;
|
||||
var playerPos = client.player.getBlockPos().down();
|
||||
if (client.player.getY() - playerPos.getY() - 1.0 >= 0.25) {
|
||||
playerPos = playerPos.up();
|
||||
this.onSlab = true;
|
||||
} else {
|
||||
this.onSlab = false;
|
||||
}
|
||||
var targetPos = new BlockPos(client.crosshairTarget.getPos()).subtract(playerPos);
|
||||
var vector = new BlockPos.Mutable(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
|
||||
var blockPos = playerPos.add(vector);
|
||||
|
||||
var direction = client.player.getHorizontalFacing();
|
||||
|
||||
var state = client.world.getBlockState(blockPos);
|
||||
if (!state.isAir())
|
||||
return null;
|
||||
var adjacentBlockState = client.world.getBlockState(blockPos.offset(direction.getOpposite()));
|
||||
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BlockHitResult(new Vec3d(blockPos.getX(),blockPos.getY(),blockPos.getZ()), direction, blockPos, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public @NotNull BlockHitResult withSideForReacharound(@NotNull BlockHitResult result, @Nullable ItemStack stack) {
|
||||
if (stack == null || stack.isEmpty() || !(stack.getItem() instanceof BlockItem))
|
||||
return result;
|
||||
return withSideForReacharound(result, Block.getBlockFromItem(stack.getItem()));
|
||||
}
|
||||
|
||||
public @NotNull BlockHitResult withSideForReacharound(@NotNull BlockHitResult result, @NotNull Block block) {
|
||||
if (block instanceof SlabBlock) {
|
||||
if (this.onSlab) result = result.withSide(Direction.UP);
|
||||
else result = result.withSide(Direction.DOWN);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the virtual mouse skins.
|
||||
*
|
||||
* @version 1.7.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public enum VirtualMouseSkin implements Nameable {
|
||||
DEFAULT_LIGHT("default_light"),
|
||||
DEFAULT_DARK("default_dark"),
|
||||
SECOND_LIGHT("second_light"),
|
||||
SECOND_DARK("second_dark");
|
||||
|
||||
private final String name;
|
||||
private final Text text;
|
||||
|
||||
VirtualMouseSkin(String name) {
|
||||
this.name = name;
|
||||
this.text = Text.translatable(this.getTranslationKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next virtual mouse skin available.
|
||||
*
|
||||
* @return the next available virtual mouse skin
|
||||
*/
|
||||
public @NotNull VirtualMouseSkin next() {
|
||||
var v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation key of this virtual mouse skin.
|
||||
*
|
||||
* @return the virtual mouse skin's translation key
|
||||
*/
|
||||
public @NotNull String getTranslationKey() {
|
||||
return "midnightcontrols.virtual_mouse.skin." + this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated text of this virtual mouse skin.
|
||||
*
|
||||
* @return the translated text of this virtual mouse skin
|
||||
*/
|
||||
public @NotNull Text getTranslatedText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the virtual mouse skin from its identifier.
|
||||
*
|
||||
* @param id the identifier of the virtual mouse skin
|
||||
* @return the virtual mouse skin if found, else empty
|
||||
*/
|
||||
public static @NotNull Optional<VirtualMouseSkin> byId(@NotNull String id) {
|
||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a compatibility handler for a mod.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public interface CompatHandler {
|
||||
/**
|
||||
* Handles compatibility of a mod.
|
||||
*
|
||||
* @param mod this mod instance
|
||||
*/
|
||||
void handle(@NotNull MidnightControlsClient mod);
|
||||
|
||||
/**
|
||||
* Returns whether the mouse is required on the specified screen.
|
||||
*
|
||||
* @param screen the screen
|
||||
* @return true if the mouse is required on the specified screen, else false
|
||||
*/
|
||||
default boolean requireMouseOnScreen(Screen screen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a slot at the specified location if possible.
|
||||
*
|
||||
* @param screen the screen
|
||||
* @param mouseX the mouse X-coordinate
|
||||
* @param mouseY the mouse Y-coordinate
|
||||
* @return a slot if present, else null
|
||||
* @since 1.5.0
|
||||
*/
|
||||
default @Nullable CompatHandler.SlotPos getSlotAt(@NotNull Screen screen, int mouseX, int mouseY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current slot is a creative slot or not.
|
||||
*
|
||||
* @param screen the screen
|
||||
* @param slot the slot to check
|
||||
* @return true if the slot is a creative slot, else false
|
||||
*/
|
||||
default boolean isCreativeSlot(@NotNull HandledScreen screen, @NotNull Slot slot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a custom translation key to make custom attack action strings on the HUD.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param placeResult the last place block result
|
||||
* @return null if untouched, else a translation key
|
||||
*/
|
||||
default String getAttackActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a custom translation key to make custom use action strings on the HUD.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param placeResult the last place block result
|
||||
* @return null if untouched, else a translation key
|
||||
*/
|
||||
default String getUseActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the menu back button.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param screen the screen
|
||||
* @return true if the handle was fired and succeed, else false
|
||||
*/
|
||||
default boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
record SlotPos(int x, int y) {
|
||||
public static final SlotPos INVALID_SLOT = new SlotPos(-1, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import dev.emi.emi.screen.EmiScreen;
|
||||
import dev.emi.emi.screen.EmiScreenManager;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import org.aperlambda.lambdacommon.Identifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
public class EMICompat implements CompatHandler {
|
||||
public static boolean handleTabs(boolean direction) {
|
||||
if (MidnightControlsClient.get().input.actionGuiCooldown == 0 &&EmiScreenManager.searchLeft != null && EmiScreenManager.searchRight != null) {
|
||||
if (direction) EmiScreenManager.searchRight.onPress();
|
||||
else EmiScreenManager.searchLeft.onPress();
|
||||
MidnightControlsClient.get().input.actionGuiCooldown = 5;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void handle(@NotNull MidnightControlsClient mod) {
|
||||
ButtonCategory category = new ButtonCategory(new Identifier("midnightcontrols","category.emi"));
|
||||
InputManager.registerCategory(category);
|
||||
new ButtonBinding.Builder("emi_page_left")
|
||||
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
|
||||
.category(category)
|
||||
.action((client,action,value,buttonState)->handleTabs(false)).cooldown()
|
||||
.filter(((client, buttonBinding) -> client.currentScreen instanceof HandledScreen<?> || client.currentScreen instanceof EmiScreen))
|
||||
.register();
|
||||
new ButtonBinding.Builder("emi_page_right")
|
||||
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
|
||||
.category(category)
|
||||
.action((client,action,value,buttonState)->handleTabs(true)).cooldown()
|
||||
.filter(((client, buttonBinding) -> client.currentScreen instanceof HandledScreen<?> || client.currentScreen instanceof EmiScreen))
|
||||
.register();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents HQM compatibility handler.
|
||||
* <p>
|
||||
* This is bad.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.2
|
||||
* @since 1.3.2
|
||||
*/
|
||||
public class HQMCompat implements CompatHandler {
|
||||
public static final String GUI_BASE_CLASS_PATH = "hardcorequesting.client.interfaces.GuiBase";
|
||||
private Optional<Class<?>> guiBaseClass;
|
||||
|
||||
@Override
|
||||
public void handle(@NotNull MidnightControlsClient mod) {
|
||||
this.guiBaseClass = LambdaReflection.getClass(GUI_BASE_CLASS_PATH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireMouseOnScreen(Screen screen) {
|
||||
return this.guiBaseClass.map(clazz -> clazz.isInstance(screen)).orElse(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl;
|
||||
import io.github.cottonmc.cotton.gui.widget.WButton;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.sound.PositionedSoundInstance;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class LibGuiCompat {
|
||||
public static boolean handlePress(@NotNull Screen screen) {
|
||||
if (screen instanceof CottonScreenImpl cottonScreen) {
|
||||
if (cottonScreen.getDescription() != null && cottonScreen.getDescription().getFocus() != null) {
|
||||
if (cottonScreen.getDescription().getFocus() instanceof WButton button && button.getOnClick() != null) {
|
||||
button.getOnClick().run();
|
||||
MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a compatibility handler.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class MidnightControlsCompat {
|
||||
public static final List<CompatHandler> HANDLERS = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initializes compatibility with other mods if needed.
|
||||
*
|
||||
* @param mod the mod instance
|
||||
*/
|
||||
public static void init(@NotNull MidnightControlsClient mod) {
|
||||
// "okzoomer" is the mod ID used by Fabric-compatible versions of Ok Zoomer. (5.0.0-beta.6 and below.)
|
||||
// "ok_zoomer" is the mod ID used by Quilt-exclusive versions of Ok Zoomer. (5.0.0-beta.7 and above.)
|
||||
if (FabricLoader.getInstance().isModLoaded("okzoomer") || FabricLoader.getInstance().isModLoaded("ok_zoomer")) {
|
||||
mod.log("Adding Ok Zoomer compatibility...");
|
||||
HANDLERS.add(new OkZoomerCompat());
|
||||
}
|
||||
if (isEMIPresent()) {
|
||||
mod.log("Adding EMI compatibility...");
|
||||
HANDLERS.add(new EMICompat());
|
||||
}
|
||||
if (FabricLoader.getInstance().isModLoaded("hardcorequesting") && LambdaReflection.doesClassExist(HQMCompat.GUI_BASE_CLASS_PATH)) {
|
||||
mod.log("Adding HQM compatibility...");
|
||||
HANDLERS.add(new HQMCompat());
|
||||
}
|
||||
HANDLERS.forEach(handler -> handler.handle(mod));
|
||||
InputManager.loadButtonBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new compatibility handler.
|
||||
*
|
||||
* @param handler the compatibility handler to register
|
||||
*/
|
||||
public static void registerCompatHandler(@NotNull CompatHandler handler) {
|
||||
HANDLERS.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams through compatibility handlers.
|
||||
*
|
||||
* @return a stream of compatibility handlers
|
||||
*/
|
||||
public static Stream<CompatHandler> streamCompatHandlers() {
|
||||
return HANDLERS.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the mouse is required on the specified screen.
|
||||
*
|
||||
* @param screen the screen
|
||||
* @return true if the mouse is requried on the specified screen, else false
|
||||
*/
|
||||
public static boolean requireMouseOnScreen(Screen screen) {
|
||||
return HANDLERS.stream().anyMatch(handler -> handler.requireMouseOnScreen(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a slot at the specified location if possible.
|
||||
*
|
||||
* @param screen the screen
|
||||
* @param mouseX the mouse X-coordinate
|
||||
* @param mouseY the mouse Y-coordinate
|
||||
* @return a slot if present, else null
|
||||
*/
|
||||
public static @Nullable CompatHandler.SlotPos getSlotAt(@NotNull Screen screen, int mouseX, int mouseY) {
|
||||
for (var handler : HANDLERS) {
|
||||
var slot = handler.getSlotAt(screen, mouseX, mouseY);
|
||||
if (slot != null)
|
||||
return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a custom translation key to make custom attack action strings on the HUD.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param placeResult the last place block result
|
||||
* @return null if untouched, else a translation key
|
||||
*/
|
||||
public static String getAttackActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
|
||||
for (CompatHandler handler : HANDLERS) {
|
||||
String action = handler.getAttackActionAt(client, placeResult);
|
||||
if (action != null) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a custom translation key to make custom use action strings on the HUD.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param placeResult the last place block result
|
||||
* @return null if untouched, else a translation key
|
||||
*/
|
||||
public static String getUseActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
|
||||
for (CompatHandler handler : HANDLERS) {
|
||||
String action = handler.getUseActionAt(client, placeResult);
|
||||
if (action != null) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the menu back button.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param screen the screen
|
||||
* @return true if the handle was fired and succeed, else false
|
||||
*/
|
||||
public static boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen) {
|
||||
for (CompatHandler handler : HANDLERS) {
|
||||
if (handler.handleMenuBack(client, screen))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Roughly Enough Items is present.
|
||||
*
|
||||
* @return true if Roughly Enough Items is present, else false
|
||||
*/
|
||||
public static boolean isReiPresent() {
|
||||
return FabricLoader.getInstance().isModLoaded("roughlyenoughitems");
|
||||
}
|
||||
/**
|
||||
* Returns whether EMI is present.
|
||||
*
|
||||
* @return true if EMI is present, else false
|
||||
*/
|
||||
public static boolean isEMIPresent() {
|
||||
return FabricLoader.getInstance().isModLoaded("emi");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This plugin is only present for the conditional mixins.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class MidnightControlsMixinPlugin implements IMixinConfigPlugin {
|
||||
private final HashMap<String, Boolean> conditionalMixins = new HashMap<>();
|
||||
|
||||
public MidnightControlsMixinPlugin() {
|
||||
this.putConditionalMixin("SodiumOptionsGUIAccessor", FabricLoader.getInstance().isModLoaded("sodium"));
|
||||
}
|
||||
|
||||
private void putConditionalMixin(@NotNull String path, boolean condition) {
|
||||
this.conditionalMixins.put("eu.midnightdust.midnightcontrols.client.compat.mixin." + path, condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
return this.conditionalMixins.getOrDefault(mixinClassName, Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Karen/あけみ <karen@akemi.ai>, LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of MidnightControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Represents a compatibility handler for Ok Zoomer.
|
||||
*
|
||||
* @author Karen/あけみ, LambdAurora
|
||||
* @version 1.4.3
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class OkZoomerCompat implements CompatHandler {
|
||||
private boolean didAllReflectionCallsSucceed = false;
|
||||
|
||||
private KeyBinding okZoomerZoomKey;
|
||||
private KeyBinding okZoomerIncreaseZoomKey;
|
||||
private KeyBinding okZoomerDecreaseZoomKey;
|
||||
private KeyBinding okZoomerResetZoomKey;
|
||||
|
||||
private Method okZoomerAreExtraKeyBindsEnabledMethod;
|
||||
|
||||
public OkZoomerCompat() {
|
||||
// These strings represent the names of the classes, fields, and methods we use from the Ok Zoomer API
|
||||
String okZoomerZoomKeybindsClassString;
|
||||
|
||||
String okZoomerZoomKeyFieldString;
|
||||
String okZoomerIncreaseZoomKeyFieldString;
|
||||
String okZoomerDecreaseZoomKeyFieldString;
|
||||
String okZoomerResetZoomKeyFieldString;
|
||||
|
||||
String okZoomerAreExtraKeyBindsEnabledMethodNameString;
|
||||
|
||||
// These variables represent the actual objects that we reflect to
|
||||
Class<?> okZoomerZoomKeybindsClass;
|
||||
|
||||
Field okZoomerZoomKeyField;
|
||||
Field okZoomerIncreaseZoomKeyField;
|
||||
Field okZoomerDecreaseZoomKeyField;
|
||||
Field okZoomerResetZoomKeyField;
|
||||
|
||||
// First, we need to determine which version of the Ok Zoomer API we're dealing with here.
|
||||
if (LambdaReflection.doesClassExist("io.github.ennuil.okzoomer.keybinds.ZoomKeybinds")) {
|
||||
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.3+1.17.1/src/main/java/io/github/ennuil/okzoomer/keybinds/ZoomKeybinds.java
|
||||
MidnightControlsClient.get().log("Ok Zoomer version 5.0.0-beta.3 or below detected!");
|
||||
|
||||
okZoomerZoomKeybindsClassString = "io.github.ennuil.okzoomer.keybinds.ZoomKeybinds";
|
||||
|
||||
okZoomerZoomKeyFieldString = "zoomKey";
|
||||
okZoomerIncreaseZoomKeyFieldString = "increaseZoomKey";
|
||||
okZoomerDecreaseZoomKeyFieldString = "decreaseZoomKey";
|
||||
okZoomerResetZoomKeyFieldString = "resetZoomKey";
|
||||
|
||||
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeybindsEnabled";
|
||||
} else if (LambdaReflection.doesClassExist("io.github.ennuil.okzoomer.key_binds.ZoomKeyBinds")) {
|
||||
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.6+1.18.2/src/main/java/io/github/ennuil/okzoomer/key_binds/ZoomKeyBinds.java
|
||||
MidnightControlsClient.get().log("Ok Zoomer version 5.0.0-beta.6, 5.0.0-beta.5, or 5.0.0-beta.4 detected!");
|
||||
|
||||
okZoomerZoomKeybindsClassString = "io.github.ennuil.okzoomer.key_binds.ZoomKeyBinds";
|
||||
|
||||
okZoomerZoomKeyFieldString = "ZOOM_KEY";
|
||||
okZoomerIncreaseZoomKeyFieldString = "INCREASE_ZOOM_KEY";
|
||||
okZoomerDecreaseZoomKeyFieldString = "DECREASE_ZOOM_KEY";
|
||||
okZoomerResetZoomKeyFieldString = "RESET_ZOOM_KEY";
|
||||
|
||||
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeyBindsEnabled";
|
||||
} else if (LambdaReflection.doesClassExist("io.github.ennuil.ok_zoomer.key_binds.ZoomKeyBinds")) {
|
||||
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.7+1.18.2/src/main/java/io/github/ennuil/ok_zoomer/key_binds/ZoomKeyBinds.java
|
||||
MidnightControlsClient.get().log("Ok Zoomer version 5.0.0-beta.7 (Quilt) or above detected!");
|
||||
|
||||
okZoomerZoomKeybindsClassString = "io.github.ennuil.ok_zoomer.key_binds.ZoomKeyBinds";
|
||||
|
||||
okZoomerZoomKeyFieldString = "ZOOM_KEY";
|
||||
okZoomerIncreaseZoomKeyFieldString = "INCREASE_ZOOM_KEY";
|
||||
okZoomerDecreaseZoomKeyFieldString = "DECREASE_ZOOM_KEY";
|
||||
okZoomerResetZoomKeyFieldString = "RESET_ZOOM_KEY";
|
||||
|
||||
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeyBindsEnabled";
|
||||
} else {
|
||||
// If all of the above checks fail, then the version of the Ok Zoomer API that the user is trying to use is too new.
|
||||
MidnightControlsClient.get().warn("The version of Ok Zoomer that you are currently using is too new, and is not yet supported by MidnightControls!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reflect to the ZoomKeyBinds (>= 5.0.0-beta.4) / ZoomKeybinds (<= 5.0.0-beta.3) class.
|
||||
try {
|
||||
okZoomerZoomKeybindsClass = Class.forName(okZoomerZoomKeybindsClassString);
|
||||
} catch (ClassNotFoundException exception) {
|
||||
// This theoretically should never happen.
|
||||
MidnightControlsClient.get().warn("MidnightControls failed to reflect to the Ok Zoomer keybinds class!");
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reflect to all of the keybind fields.
|
||||
try {
|
||||
okZoomerZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerZoomKeyFieldString);
|
||||
okZoomerIncreaseZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerIncreaseZoomKeyFieldString);
|
||||
okZoomerDecreaseZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerDecreaseZoomKeyFieldString);
|
||||
okZoomerResetZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerResetZoomKeyFieldString);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
MidnightControlsClient.get().warn("MidnightControls failed to reflect to the Ok Zoomer keybind fields!");
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialise KeyBinding objects
|
||||
try {
|
||||
okZoomerZoomKey = (KeyBinding) okZoomerZoomKeyField.get(null);
|
||||
okZoomerIncreaseZoomKey = (KeyBinding) okZoomerIncreaseZoomKeyField.get(null);
|
||||
okZoomerDecreaseZoomKey = (KeyBinding) okZoomerDecreaseZoomKeyField.get(null);
|
||||
okZoomerResetZoomKey = (KeyBinding) okZoomerResetZoomKeyField.get(null);
|
||||
} catch (IllegalAccessException exception) {
|
||||
MidnightControlsClient.get().warn("MidnightControls failed to reflect to the Ok Zoomer keybind objects!");
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reflect to the areExtraKeyBindsEnabled (>= 5.0.0-beta.4) / areExtraKeybindsEnabled (<= 5.0.0-beta.3) method.
|
||||
// TODO: Consider replacing this entirely with getExtraKeyBind (>= 5.0.0-beta.4) / getExtraKeybind (<= 5.0.0-beta.3) in the future.
|
||||
try {
|
||||
okZoomerAreExtraKeyBindsEnabledMethod = okZoomerZoomKeybindsClass.getDeclaredMethod(okZoomerAreExtraKeyBindsEnabledMethodNameString);
|
||||
} catch (NoSuchMethodException exception) {
|
||||
MidnightControlsClient.get().warn("MidnightControls failed to reflect to an Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
didAllReflectionCallsSucceed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(@NotNull MidnightControlsClient mod) {
|
||||
if (didAllReflectionCallsSucceed) {
|
||||
new ButtonBinding.Builder("zoom")
|
||||
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X)
|
||||
.onlyInGame()
|
||||
.cooldown(true)
|
||||
.category(ButtonBinding.MISC_CATEGORY)
|
||||
.linkKeybind(okZoomerZoomKey)
|
||||
.register();
|
||||
|
||||
boolean okZoomerAreExtraKeyBindsEnabled = false;
|
||||
try {
|
||||
okZoomerAreExtraKeyBindsEnabled = (boolean) okZoomerAreExtraKeyBindsEnabledMethod.invoke(null);
|
||||
} catch (IllegalAccessException exception) {
|
||||
MidnightControlsClient.get().warn("MidnightControls encountered an IllegalAccessException while attempting to invoke a reflected Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
|
||||
exception.printStackTrace();
|
||||
} catch (InvocationTargetException exception) {
|
||||
MidnightControlsClient.get().warn("MidnightControls encountered an InvocationTargetException while attempting to invoke a reflected Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
if (okZoomerAreExtraKeyBindsEnabled) {
|
||||
new ButtonBinding.Builder("zoom_in")
|
||||
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
|
||||
.onlyInGame()
|
||||
.cooldown(true)
|
||||
.category(ButtonBinding.MISC_CATEGORY)
|
||||
.linkKeybind(okZoomerIncreaseZoomKey)
|
||||
.register();
|
||||
new ButtonBinding.Builder("zoom_out")
|
||||
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
|
||||
.onlyInGame()
|
||||
.cooldown(true)
|
||||
.category(ButtonBinding.MISC_CATEGORY)
|
||||
.linkKeybind(okZoomerDecreaseZoomKey)
|
||||
.register();
|
||||
new ButtonBinding.Builder("zoom_reset")
|
||||
.onlyInGame()
|
||||
.cooldown(true)
|
||||
.category(ButtonBinding.MISC_CATEGORY)
|
||||
.linkKeybind(okZoomerResetZoomKey)
|
||||
.register();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package eu.midnightdust.midnightcontrols.client.compat;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.mixin.SodiumOptionsGUIAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
|
||||
public class SodiumCompat {
|
||||
private static final MinecraftClient client = MinecraftClient.getInstance();
|
||||
public static void handleInput(Screen screen, boolean direction) {
|
||||
if (screen instanceof SodiumOptionsGUI optionsGUI) {
|
||||
SodiumOptionsGUIAccessor accessor = (SodiumOptionsGUIAccessor) optionsGUI;
|
||||
final int max = accessor.getControls().size()-1;
|
||||
|
||||
var option = accessor.getControls().stream().filter(ControlElement::isHovered).findFirst().orElse(accessor.getControls().get(0));
|
||||
int i = accessor.getControls().indexOf(option);
|
||||
i = (direction ? ((max > i) ? ++i : 0) : (i > 0 ? --i : max));
|
||||
|
||||
var dimensions = accessor.getControls().get(i).getDimensions();
|
||||
int x = (int) (client.getWindow().getScaleFactor() * dimensions.getCenterX());
|
||||
int y = (int) (client.getWindow().getScaleFactor() * dimensions.getCenterY());
|
||||
InputManager.queueMousePosition(x,y);
|
||||
InputManager.INPUT_MANAGER.updateMousePosition(client);
|
||||
MidnightControlsClient.get().input.actionGuiCooldown = 5;
|
||||
if (MidnightControlsConfig.debug) MidnightControls.get().log(i+" "+accessor.getControls().size()+" | " + dimensions.getCenterX() + " " + dimensions.getCenterY());
|
||||
}
|
||||
}
|
||||
public static void handleTabs(Screen screen, boolean direction) {
|
||||
if (screen instanceof SodiumOptionsGUI optionsGUI) {
|
||||
SodiumOptionsGUIAccessor accessor = (SodiumOptionsGUIAccessor) optionsGUI;
|
||||
final int max = accessor.getPages().size()-1;
|
||||
int i = accessor.getPages().indexOf(accessor.getCurrentPage());
|
||||
i = (direction ? ((max > i) ? ++i : 0) : (i > 0 ? --i : max));
|
||||
if (MidnightControlsConfig.debug) MidnightControls.get().log(""+i);
|
||||
optionsGUI.setPage(accessor.getPages().get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package eu.midnightdust.midnightcontrols.client.compat.mixin;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(value = SodiumOptionsGUI.class, remap = false)
|
||||
public interface SodiumOptionsGUIAccessor {
|
||||
@Accessor
|
||||
List<ControlElement<?>> getControls();
|
||||
@Accessor
|
||||
List<OptionPage> getPages();
|
||||
@Accessor
|
||||
OptionPage getCurrentPage();
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.ButtonState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
|
||||
import org.aperlambda.lambdacommon.utils.function.Predicates;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Represents a button binding.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ButtonBinding {
|
||||
public static final ButtonCategory MOVEMENT_CATEGORY;
|
||||
public static final ButtonCategory GAMEPLAY_CATEGORY;
|
||||
public static final ButtonCategory INVENTORY_CATEGORY;
|
||||
public static final ButtonCategory MULTIPLAYER_CATEGORY;
|
||||
public static final ButtonCategory MISC_CATEGORY;
|
||||
|
||||
public static final ButtonBinding ATTACK = new Builder("attack").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)).onlyInGame().register();
|
||||
public static final ButtonBinding BACK = new Builder("back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false))
|
||||
.action(MovementHandler.HANDLER).onlyInGame().register();
|
||||
public static final ButtonBinding CHAT = new Builder("chat").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding DROP_ITEM = new Builder("drop_item").buttons(GLFW_GAMEPAD_BUTTON_B).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding FORWARD = new Builder("forward").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true))
|
||||
.action(MovementHandler.HANDLER).onlyInGame().register();
|
||||
public static final ButtonBinding HOTBAR_LEFT = new Builder("hotbar_left").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
|
||||
.action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
|
||||
.action(InputHandlers.handleHotbar(true)).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding INVENTORY = new Builder("inventory").buttons(GLFW_GAMEPAD_BUTTON_Y).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding JUMP = new Builder("jump").buttons(GLFW_GAMEPAD_BUTTON_A).onlyInGame().register();
|
||||
public static final ButtonBinding LEFT = new Builder("left").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false))
|
||||
.action(MovementHandler.HANDLER).onlyInGame().register();
|
||||
public static final ButtonBinding PAUSE_GAME = new Builder("pause_game").buttons(GLFW_GAMEPAD_BUTTON_START).action(InputHandlers::handlePauseGame).cooldown().register();
|
||||
public static final ButtonBinding PICK_BLOCK = new Builder("pick_block").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding PLAYER_LIST = new Builder("player_list").buttons(GLFW_GAMEPAD_BUTTON_BACK).onlyInGame().register();
|
||||
public static final ButtonBinding RIGHT = new Builder("right").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true))
|
||||
.action(MovementHandler.HANDLER).onlyInGame().register();
|
||||
public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
|
||||
.action(InputHandlers::handleScreenshot).cooldown().register();
|
||||
|
||||
public static final ButtonBinding DEBUG_SCREEN = new Builder("debug_screen").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_B)
|
||||
.action((client,binding,value,action) -> {if (action == ButtonState.PRESS) client.options.debugEnabled = !client.options.debugEnabled; return true;}).cooldown().register();
|
||||
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN)
|
||||
.action(InputHandlers.handleInventorySlotPad(1)).onlyInInventory().cooldown().register();
|
||||
public static final ButtonBinding SLOT_LEFT = new Builder("slot_left").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT)
|
||||
.action(InputHandlers.handleInventorySlotPad(3)).onlyInInventory().cooldown().register();
|
||||
public static final ButtonBinding SLOT_RIGHT = new Builder("slot_right").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)
|
||||
.action(InputHandlers.handleInventorySlotPad(2)).onlyInInventory().cooldown().register();
|
||||
public static final ButtonBinding SLOT_UP = new Builder("slot_up").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP)
|
||||
.action(InputHandlers.handleInventorySlotPad(0)).onlyInInventory().cooldown().register();
|
||||
public static final ButtonBinding SMOOTH_CAMERA = new Builder("toggle_smooth_camera").onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding SNEAK = new Builder("sneak").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_THUMB)
|
||||
.actions(InputHandlers::handleToggleSneak).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding SPRINT = new Builder("sprint").buttons(GLFW_GAMEPAD_BUTTON_LEFT_THUMB).onlyInGame().register();
|
||||
public static final ButtonBinding SWAP_HANDS = new Builder("swap_hands").buttons(GLFW_GAMEPAD_BUTTON_X).onlyInGame().cooldown().register();
|
||||
public static final ButtonBinding TAB_LEFT = new Builder("tab_back").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
|
||||
.action(InputHandlers.handleHotbar(false)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements).or((client, binding) -> client.currentScreen != null && client.currentScreen.getClass().toString().contains("sodium"))).cooldown().register();
|
||||
public static final ButtonBinding TAB_RIGHT = new Builder("tab_next").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
|
||||
.action(InputHandlers.handleHotbar(true)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements).or((client, binding) -> client.currentScreen != null && client.currentScreen.getClass().toString().contains("sodium"))).cooldown().register();
|
||||
public static final ButtonBinding PAGE_LEFT = new Builder("page_back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
|
||||
.action(InputHandlers.handlePage(false)).filter(InputHandlers::inInventory).cooldown().register();
|
||||
public static final ButtonBinding PAGE_RIGHT = new Builder("page_next").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
|
||||
.action(InputHandlers.handlePage(true)).filter(InputHandlers::inInventory).cooldown().register();
|
||||
public static final ButtonBinding TAKE = new Builder("take").buttons(GLFW_GAMEPAD_BUTTON_X)
|
||||
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
|
||||
public static final ButtonBinding TAKE_ALL = new Builder("take_all").buttons(GLFW_GAMEPAD_BUTTON_A)
|
||||
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
|
||||
public static final ButtonBinding QUICK_MOVE = new Builder("quick_move").buttons(GLFW_GAMEPAD_BUTTON_Y)
|
||||
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
|
||||
public static final ButtonBinding TOGGLE_PERSPECTIVE = new Builder("toggle_perspective").filter(InputHandlers::inGame).buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_Y).cooldown().register();
|
||||
public static final ButtonBinding USE = new Builder("use").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)).register();
|
||||
|
||||
private int[] button;
|
||||
private final int[] defaultButton;
|
||||
private final String key;
|
||||
private final Text text;
|
||||
private KeyBinding mcKeyBinding = null;
|
||||
protected PairPredicate<MinecraftClient, ButtonBinding> filter;
|
||||
private final List<PressAction> actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION));
|
||||
private boolean hasCooldown;
|
||||
private int cooldown = 0;
|
||||
boolean pressed = false;
|
||||
|
||||
public ButtonBinding(String key, int[] defaultButton, List<PressAction> actions, PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
|
||||
this.setButton(this.defaultButton = defaultButton);
|
||||
this.key = key;
|
||||
this.text = Text.translatable(this.key);
|
||||
this.filter = filter;
|
||||
this.actions.addAll(actions);
|
||||
this.hasCooldown = hasCooldown;
|
||||
}
|
||||
|
||||
public ButtonBinding(String key, int[] defaultButton, boolean hasCooldown) {
|
||||
this(key, defaultButton, Collections.emptyList(), Predicates.pairAlwaysTrue(), hasCooldown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the button bound.
|
||||
*
|
||||
* @return the bound button
|
||||
*/
|
||||
public int[] getButton() {
|
||||
return this.button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bound button.
|
||||
*
|
||||
* @param button the bound button
|
||||
*/
|
||||
public void setButton(int[] button) {
|
||||
this.button = button;
|
||||
|
||||
if (InputManager.hasBinding(this))
|
||||
InputManager.sortBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the bound button is the specified button or not.
|
||||
*
|
||||
* @param button the button to check
|
||||
* @return true if the bound button is the specified button, else false
|
||||
*/
|
||||
public boolean isButton(int[] button) {
|
||||
return InputManager.areButtonsEquivalent(button, this.button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this button is down or not.
|
||||
*
|
||||
* @return true if the button is down, else false
|
||||
*/
|
||||
public boolean isButtonDown() {
|
||||
return this.pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this button binding is bound or not.
|
||||
*
|
||||
* @return true if this button binding is bound, else false
|
||||
*/
|
||||
public boolean isNotBound() {
|
||||
return this.button.length == 0 || this.button[0] == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default button assigned to this binding.
|
||||
*
|
||||
* @return the default button
|
||||
*/
|
||||
public int[] getDefaultButton() {
|
||||
return this.defaultButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the assigned button is the default button.
|
||||
*
|
||||
* @return true if the assigned button is the default button, else false
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
return this.button.length == this.defaultButton.length && InputManager.areButtonsEquivalent(this.button, this.defaultButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the button code.
|
||||
*
|
||||
* @return the button code
|
||||
*/
|
||||
public String getButtonCode() {
|
||||
return Arrays.stream(this.button)
|
||||
.mapToObj(btn -> Integer.valueOf(btn).toString())
|
||||
.collect(Collectors.joining("+"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key binding to emulate with this button binding.
|
||||
*
|
||||
* @param keyBinding the optional key binding
|
||||
*/
|
||||
public void setKeyBinding(@Nullable KeyBinding keyBinding) {
|
||||
this.mcKeyBinding = keyBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the button binding is available in the current context.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @return true if the button binding is available, else false
|
||||
*/
|
||||
public boolean isAvailable(@NotNull MinecraftClient client) {
|
||||
return this.filter.test(client, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the button binding cooldown.
|
||||
*/
|
||||
public void update() {
|
||||
if (this.hasCooldown && this.cooldown > 0)
|
||||
this.cooldown--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the button binding.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param state the state
|
||||
*/
|
||||
public void handle(@NotNull MinecraftClient client, float value, @NotNull ButtonState state) {
|
||||
if (state == ButtonState.REPEAT && this.hasCooldown && this.cooldown != 0)
|
||||
return;
|
||||
if (this.hasCooldown && state.isPressed()) {
|
||||
this.cooldown = 5;
|
||||
}
|
||||
for (int i = this.actions.size() - 1; i >= 0; i--) {
|
||||
if (this.actions.get(i).press(client, this, value, state))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull String getName() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation key of this button binding.
|
||||
*
|
||||
* @return the translation key
|
||||
*/
|
||||
public @NotNull String getTranslationKey() {
|
||||
return I18n.hasTranslation("midnightcontrols.action." + this.getName()) ? "midnightcontrols.action." + this.getName() : this.getName();
|
||||
}
|
||||
|
||||
public @NotNull Text getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key binding equivalent of this button binding.
|
||||
*
|
||||
* @return the key binding equivalent
|
||||
*/
|
||||
public @NotNull Optional<KeyBinding> asKeyBinding() {
|
||||
return Optional.ofNullable(this.mcKeyBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ButtonBinding{id=\"" + this.key + "\","
|
||||
+ "hasCooldown=" + this.hasCooldown
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified axis as a button.
|
||||
*
|
||||
* @param axis the axis
|
||||
* @param positive true if the axis part is positive, else false
|
||||
* @return the axis as a button
|
||||
*/
|
||||
public static int axisAsButton(int axis, boolean positive) {
|
||||
return positive ? 100 + axis : 200 + axis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified button is an axis or not.
|
||||
*
|
||||
* @param button the button
|
||||
* @return true if the button is an axis, else false
|
||||
*/
|
||||
public static boolean isAxis(int button) {
|
||||
button %= 500;
|
||||
return button >= 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the second Joycon's specified button code.
|
||||
*
|
||||
* @param button the raw button code
|
||||
* @return the second Joycon's button code
|
||||
*/
|
||||
public static int controller2Button(int button) {
|
||||
return 500 + button;
|
||||
}
|
||||
|
||||
public static void init(@NotNull GameOptions options) {
|
||||
ATTACK.mcKeyBinding = options.attackKey;
|
||||
BACK.mcKeyBinding = options.backKey;
|
||||
CHAT.mcKeyBinding = options.chatKey;
|
||||
DROP_ITEM.mcKeyBinding = options.dropKey;
|
||||
FORWARD.mcKeyBinding = options.forwardKey;
|
||||
INVENTORY.mcKeyBinding = options.inventoryKey;
|
||||
JUMP.mcKeyBinding = options.jumpKey;
|
||||
LEFT.mcKeyBinding = options.leftKey;
|
||||
PICK_BLOCK.mcKeyBinding = options.pickItemKey;
|
||||
PLAYER_LIST.mcKeyBinding = options.playerListKey;
|
||||
RIGHT.mcKeyBinding = options.rightKey;
|
||||
SCREENSHOT.mcKeyBinding = options.screenshotKey;
|
||||
SMOOTH_CAMERA.mcKeyBinding = options.smoothCameraKey;
|
||||
SNEAK.mcKeyBinding = options.sneakKey;
|
||||
SPRINT.mcKeyBinding = options.sprintKey;
|
||||
SWAP_HANDS.mcKeyBinding = options.swapHandsKey;
|
||||
TOGGLE_PERSPECTIVE.mcKeyBinding = options.togglePerspectiveKey;
|
||||
USE.mcKeyBinding = options.useKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized name of the specified button.
|
||||
*
|
||||
* @param button the button
|
||||
* @return the localized name of the button
|
||||
*/
|
||||
public static @NotNull Text getLocalizedButtonName(int button) {
|
||||
return switch (button % 500) {
|
||||
case -1 -> Text.translatable("key.keyboard.unknown");
|
||||
case GLFW_GAMEPAD_BUTTON_A -> Text.translatable("midnightcontrols.button.a");
|
||||
case GLFW_GAMEPAD_BUTTON_B -> Text.translatable("midnightcontrols.button.b");
|
||||
case GLFW_GAMEPAD_BUTTON_X -> Text.translatable("midnightcontrols.button.x");
|
||||
case GLFW_GAMEPAD_BUTTON_Y -> Text.translatable("midnightcontrols.button.y");
|
||||
case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER -> Text.translatable("midnightcontrols.button.left_bumper");
|
||||
case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER -> Text.translatable("midnightcontrols.button.right_bumper");
|
||||
case GLFW_GAMEPAD_BUTTON_BACK -> Text.translatable("midnightcontrols.button.back");
|
||||
case GLFW_GAMEPAD_BUTTON_START -> Text.translatable("midnightcontrols.button.start");
|
||||
case GLFW_GAMEPAD_BUTTON_GUIDE -> Text.translatable("midnightcontrols.button.guide");
|
||||
case GLFW_GAMEPAD_BUTTON_LEFT_THUMB -> Text.translatable("midnightcontrols.button.left_thumb");
|
||||
case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB -> Text.translatable("midnightcontrols.button.right_thumb");
|
||||
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> Text.translatable("midnightcontrols.button.dpad_up");
|
||||
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> Text.translatable("midnightcontrols.button.dpad_right");
|
||||
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> Text.translatable("midnightcontrols.button.dpad_down");
|
||||
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> Text.translatable("midnightcontrols.button.dpad_left");
|
||||
case 100 -> Text.translatable("midnightcontrols.axis.left_x+");
|
||||
case 101 -> Text.translatable("midnightcontrols.axis.left_y+");
|
||||
case 102 -> Text.translatable("midnightcontrols.axis.right_x+");
|
||||
case 103 -> Text.translatable("midnightcontrols.axis.right_y+");
|
||||
case 104 -> Text.translatable("midnightcontrols.axis.left_trigger");
|
||||
case 105 -> Text.translatable("midnightcontrols.axis.right_trigger");
|
||||
case 200 -> Text.translatable("midnightcontrols.axis.left_x-");
|
||||
case 201 -> Text.translatable("midnightcontrols.axis.left_y-");
|
||||
case 202 -> Text.translatable("midnightcontrols.axis.right_x-");
|
||||
case 203 -> Text.translatable("midnightcontrols.axis.right_y-");
|
||||
case 15 -> Text.translatable("midnightcontrols.button.l4");
|
||||
case 16 -> Text.translatable("midnightcontrols.button.l5");
|
||||
case 17 -> Text.translatable("midnightcontrols.button.r4");
|
||||
case 18 -> Text.translatable("midnightcontrols.button.r5");
|
||||
default -> Text.translatable("midnightcontrols.button.unknown", button);
|
||||
};
|
||||
}
|
||||
|
||||
static {
|
||||
MOVEMENT_CATEGORY = InputManager.registerDefaultCategory("key.categories.movement", category -> category.registerAllBindings(
|
||||
ButtonBinding.FORWARD,
|
||||
ButtonBinding.BACK,
|
||||
ButtonBinding.LEFT,
|
||||
ButtonBinding.RIGHT,
|
||||
ButtonBinding.JUMP,
|
||||
ButtonBinding.SNEAK,
|
||||
ButtonBinding.SPRINT));
|
||||
GAMEPLAY_CATEGORY = InputManager.registerDefaultCategory("key.categories.gameplay", category -> category.registerAllBindings(
|
||||
ButtonBinding.ATTACK,
|
||||
ButtonBinding.PICK_BLOCK,
|
||||
ButtonBinding.USE
|
||||
));
|
||||
INVENTORY_CATEGORY = InputManager.registerDefaultCategory("key.categories.inventory", category -> category.registerAllBindings(
|
||||
ButtonBinding.DROP_ITEM,
|
||||
ButtonBinding.HOTBAR_LEFT,
|
||||
ButtonBinding.HOTBAR_RIGHT,
|
||||
ButtonBinding.INVENTORY,
|
||||
ButtonBinding.SWAP_HANDS,
|
||||
ButtonBinding.TAB_LEFT,
|
||||
ButtonBinding.TAB_RIGHT,
|
||||
ButtonBinding.PAGE_LEFT,
|
||||
ButtonBinding.PAGE_RIGHT,
|
||||
ButtonBinding.TAKE,
|
||||
ButtonBinding.TAKE_ALL,
|
||||
ButtonBinding.QUICK_MOVE,
|
||||
ButtonBinding.SLOT_UP,
|
||||
ButtonBinding.SLOT_DOWN,
|
||||
ButtonBinding.SLOT_LEFT,
|
||||
ButtonBinding.SLOT_RIGHT
|
||||
));
|
||||
MULTIPLAYER_CATEGORY = InputManager.registerDefaultCategory("key.categories.multiplayer",
|
||||
category -> category.registerAllBindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST));
|
||||
MISC_CATEGORY = InputManager.registerDefaultCategory("key.categories.misc", category -> category.registerAllBindings(
|
||||
ButtonBinding.SCREENSHOT,
|
||||
ButtonBinding.TOGGLE_PERSPECTIVE,
|
||||
ButtonBinding.PAUSE_GAME,
|
||||
//SMOOTH_CAMERA,
|
||||
ButtonBinding.DEBUG_SCREEN
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder instance.
|
||||
*
|
||||
* @param identifier the identifier of the button binding
|
||||
* @return the builder instance
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static Builder builder(@NotNull Identifier identifier) {
|
||||
return new Builder(identifier);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a quick {@link ButtonBinding} builder.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static class Builder {
|
||||
private final String key;
|
||||
private int[] buttons = new int[0];
|
||||
private final List<PressAction> actions = new ArrayList<>();
|
||||
private PairPredicate<MinecraftClient, ButtonBinding> filter = Predicates.pairAlwaysTrue();
|
||||
private boolean cooldown = false;
|
||||
private ButtonCategory category = null;
|
||||
private KeyBinding mcBinding = null;
|
||||
|
||||
/**
|
||||
* This constructor shouldn't be used for other mods.
|
||||
*
|
||||
* @param key the key with format {@code "<namespace>.<name>"}
|
||||
*/
|
||||
public Builder(@NotNull String key) {
|
||||
this.key = key;
|
||||
this.unbound();
|
||||
}
|
||||
|
||||
public Builder(@NotNull Identifier identifier) {
|
||||
this(identifier.getNamespace() + "." + identifier.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the default buttons of the {@link ButtonBinding}.
|
||||
*
|
||||
* @param buttons the default buttons
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder buttons(int... buttons) {
|
||||
this.buttons = buttons;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ButtonBinding} to unbound.
|
||||
*
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder unbound() {
|
||||
return this.buttons(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the actions to the {@link ButtonBinding}.
|
||||
*
|
||||
* @param actions the actions to add
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder actions(@NotNull PressAction... actions) {
|
||||
this.actions.addAll(Arrays.asList(actions));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action to the {@link ButtonBinding}.
|
||||
*
|
||||
* @param action the action to add
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder action(@NotNull PressAction action) {
|
||||
this.actions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a filter for the {@link ButtonBinding}.
|
||||
*
|
||||
* @param filter the filter
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder filter(@NotNull PairPredicate<MinecraftClient, ButtonBinding> filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter of {@link ButtonBinding} to only in game.
|
||||
*
|
||||
* @return the builder instance
|
||||
* @see #filter(PairPredicate)
|
||||
* @see InputHandlers#inGame(MinecraftClient, ButtonBinding)
|
||||
*/
|
||||
public Builder onlyInGame() {
|
||||
return this.filter(InputHandlers::inGame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter of {@link ButtonBinding} to only in inventory.
|
||||
*
|
||||
* @return the builder instance
|
||||
* @see #filter(PairPredicate)
|
||||
* @see InputHandlers#inInventory(MinecraftClient, ButtonBinding)
|
||||
*/
|
||||
public Builder onlyInInventory() {
|
||||
return this.filter(InputHandlers::inInventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the {@link ButtonBinding} has a cooldown or not.
|
||||
*
|
||||
* @param cooldown true if the {@link ButtonBinding} has a cooldown, else false
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder cooldown(boolean cooldown) {
|
||||
this.cooldown = cooldown;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a cooldown on the {@link ButtonBinding}.
|
||||
*
|
||||
* @return the builder instance
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public Builder cooldown() {
|
||||
return this.cooldown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the category of the {@link ButtonBinding}.
|
||||
*
|
||||
* @param category the category
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder category(@Nullable ButtonCategory category) {
|
||||
this.category = category;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keybinding linked to the {@link ButtonBinding}.
|
||||
*
|
||||
* @param binding the keybinding to link
|
||||
* @return the builder instance
|
||||
*/
|
||||
public Builder linkKeybind(@Nullable KeyBinding binding) {
|
||||
this.mcBinding = binding;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link ButtonBinding}.
|
||||
*
|
||||
* @return the built {@link ButtonBinding}
|
||||
*/
|
||||
public ButtonBinding build() {
|
||||
var binding = new ButtonBinding(this.key, this.buttons, this.actions, this.filter, this.cooldown);
|
||||
if (this.category != null)
|
||||
this.category.registerBinding(binding);
|
||||
if (this.mcBinding != null)
|
||||
binding.setKeyBinding(this.mcBinding);
|
||||
return binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and registers the {@link ButtonBinding}.
|
||||
*
|
||||
* @return the built {@link ButtonBinding}
|
||||
* @see #build()
|
||||
*/
|
||||
public ButtonBinding register() {
|
||||
return InputManager.registerBinding(this.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import org.aperlambda.lambdacommon.Identifier;
|
||||
import org.aperlambda.lambdacommon.utils.Identifiable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a button binding category
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.1.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class ButtonCategory implements Identifiable {
|
||||
private final List<ButtonBinding> bindings = new ArrayList<>();
|
||||
private final Identifier id;
|
||||
private final int priority;
|
||||
|
||||
public ButtonCategory(@NotNull Identifier id, int priority) {
|
||||
this.id = id;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public ButtonCategory(@NotNull Identifier id) {
|
||||
this(id, 100);
|
||||
}
|
||||
|
||||
public void registerBinding(@NotNull ButtonBinding binding) {
|
||||
if (this.bindings.contains(binding))
|
||||
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
|
||||
this.bindings.add(binding);
|
||||
}
|
||||
|
||||
public void registerAllBindings(@NotNull ButtonBinding... bindings) {
|
||||
this.registerAllBindings(Arrays.asList(bindings));
|
||||
}
|
||||
|
||||
public void registerAllBindings(@NotNull List<ButtonBinding> bindings) {
|
||||
bindings.forEach(this::registerBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bindings assigned to this category.
|
||||
*
|
||||
* @return the bindings assigned to this category
|
||||
*/
|
||||
public @NotNull List<ButtonBinding> getBindings() {
|
||||
return Collections.unmodifiableList(this.bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this category.
|
||||
* <p>
|
||||
* The translation key should be `modid.identifier_name`.
|
||||
*
|
||||
* @return the translated name
|
||||
*/
|
||||
public @NotNull String getTranslatedName() {
|
||||
if (this.id.getNamespace().equals("minecraft"))
|
||||
return I18n.translate(this.id.getName());
|
||||
else
|
||||
return I18n.translate(this.id.getNamespace() + "." + this.id.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the priority display of this category.
|
||||
* It will defines in which order the categories will display on the controls screen.
|
||||
*
|
||||
* @return the priority of this category
|
||||
*/
|
||||
public int getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Identifier getIdentifier() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWGamepadState;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.lwjgl.BufferUtils.createByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents a controller.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public record Controller(int id) implements Nameable {
|
||||
private static final Map<Integer, Controller> CONTROLLERS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Gets the controller's globally unique identifier.
|
||||
*
|
||||
* @return the controller's GUID
|
||||
*/
|
||||
public String getGuid() {
|
||||
String guid = GLFW.glfwGetJoystickGUID(this.id);
|
||||
return guid == null ? "" : guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this controller is connected or not.
|
||||
*
|
||||
* @return true if this controller is connected, else false
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return GLFW.glfwJoystickPresent(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this controller is a gamepad or not.
|
||||
*
|
||||
* @return true if this controller is a gamepad, else false
|
||||
*/
|
||||
public boolean isGamepad() {
|
||||
return this.isConnected() && GLFW.glfwJoystickIsGamepad(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the controller.
|
||||
*
|
||||
* @return the controller's name
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
var name = this.isGamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
|
||||
return name == null ? String.valueOf(this.id()) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the controller.
|
||||
*
|
||||
* @return the state of the controller input
|
||||
*/
|
||||
public GLFWGamepadState getState() {
|
||||
var state = GLFWGamepadState.create();
|
||||
if (this.isGamepad())
|
||||
GLFW.glfwGetGamepadState(this.id, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Controller byId(int id) {
|
||||
if (id > GLFW.GLFW_JOYSTICK_LAST) {
|
||||
MidnightControlsClient.get().log("Controller '" + id + "' doesn't exist.");
|
||||
id = GLFW.GLFW_JOYSTICK_LAST;
|
||||
}
|
||||
Controller controller;
|
||||
if (CONTROLLERS.containsKey(id))
|
||||
return CONTROLLERS.get(id);
|
||||
else {
|
||||
controller = new Controller(id);
|
||||
CONTROLLERS.put(id, controller);
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Controller> byGuid(@NotNull String guid) {
|
||||
return CONTROLLERS.values().stream().filter(Controller::isConnected)
|
||||
.filter(controller -> controller.getGuid().equals(guid))
|
||||
.max(Comparator.comparingInt(Controller::id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified resource and returns the raw data as a ByteBuffer.
|
||||
*
|
||||
* @param resource the resource to read
|
||||
* @param bufferSize the initial buffer size
|
||||
* @return the resource data
|
||||
* @throws IOException If an IO error occurs.
|
||||
*/
|
||||
private static ByteBuffer ioResourceToBuffer(String resource, int bufferSize) throws IOException {
|
||||
ByteBuffer buffer = null;
|
||||
|
||||
var path = Paths.get(resource);
|
||||
if (Files.isReadable(path)) {
|
||||
try (var fc = Files.newByteChannel(path)) {
|
||||
buffer = createByteBuffer((int) fc.size() + 2);
|
||||
while (fc.read(buffer) != -1) ;
|
||||
buffer.put((byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer != null) buffer.flip(); // Force Java 8 >.<
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the controller mappings.
|
||||
*/
|
||||
public static void updateMappings() {
|
||||
try {
|
||||
MidnightControlsClient.get().log("Updating controller mappings...");
|
||||
File databaseFile = new File("config/gamecontrollerdatabase.txt");
|
||||
try {
|
||||
BufferedInputStream in = new BufferedInputStream(new URL("https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt").openStream());
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(databaseFile));
|
||||
byte[] dataBuffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
|
||||
out.write(dataBuffer, 0, bytesRead);
|
||||
}
|
||||
out.close();
|
||||
} catch (Exception ignored) {/* Just continue when internet connection is not available */}
|
||||
var database = ioResourceToBuffer(databaseFile.getPath(), 1024);
|
||||
if (database != null) GLFW.glfwUpdateGamepadMappings(database);
|
||||
if (!MidnightControlsClient.MAPPINGS_FILE.exists())
|
||||
return;
|
||||
var buffer = ioResourceToBuffer(MidnightControlsClient.MAPPINGS_FILE.getPath(), 1024);
|
||||
if (buffer != null) GLFW.glfwUpdateGamepadMappings(buffer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try (var memoryStack = MemoryStack.stackPush()) {
|
||||
var pointerBuffer = memoryStack.mallocPointer(1);
|
||||
int i = GLFW.glfwGetError(pointerBuffer);
|
||||
if (i != 0) {
|
||||
long l = pointerBuffer.get();
|
||||
var string = l == 0L ? "" : MemoryUtil.memUTF8(l);
|
||||
var client = MinecraftClient.getInstance();
|
||||
if (client != null) {
|
||||
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.TUTORIAL_HINT,
|
||||
Text.translatable("midnightcontrols.controller.mappings.error"), Text.literal(string)));
|
||||
MidnightControls.get().log(I18n.translate("midnightcontrols.controller.mappings.error")+string);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
/* Ignored :concern: */
|
||||
}
|
||||
|
||||
if (MidnightControlsConfig.debug) {
|
||||
for (int i = GLFW.GLFW_JOYSTICK_1; i <= GLFW.GLFW_JOYSTICK_16; i++) {
|
||||
var controller = byId(i);
|
||||
|
||||
if (!controller.isConnected())
|
||||
continue;
|
||||
|
||||
MidnightControls.get().log(String.format("Controller #%d name: \"%s\"\n GUID: %s\n Gamepad: %s",
|
||||
controller.id,
|
||||
controller.getName(),
|
||||
controller.getGuid(),
|
||||
controller.isGamepad()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import eu.midnightdust.midnightcontrols.client.ButtonState;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.EMICompat;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.SodiumCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.mixin.AdvancementsScreenAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.mixin.RecipeBookWidgetAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||
import eu.midnightdust.midnightcontrols.client.util.MouseAccessor;
|
||||
import net.fabricmc.fabric.impl.item.group.CreativeGuiExtensions;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gl.Framebuffer;
|
||||
import net.minecraft.client.gui.hud.DebugHud;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
|
||||
import net.minecraft.client.gui.widget.PressableWidget;
|
||||
import net.minecraft.client.util.ScreenshotRecorder;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
import org.aperlambda.lambdacommon.utils.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_2;
|
||||
|
||||
/**
|
||||
* Represents some input handlers.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class InputHandlers {
|
||||
private InputHandlers() {
|
||||
}
|
||||
|
||||
public static PressAction handleHotbar(boolean next) {
|
||||
return (client, button, value, action) -> {
|
||||
if (action == ButtonState.RELEASE)
|
||||
return false;
|
||||
|
||||
// When ingame
|
||||
if (client.currentScreen == null && client.player != null) {
|
||||
if (next)
|
||||
client.player.getInventory().selectedSlot = client.player.getInventory().selectedSlot == 8 ? 0 : client.player.getInventory().selectedSlot + 1;
|
||||
else
|
||||
client.player.getInventory().selectedSlot = client.player.getInventory().selectedSlot == 0 ? 8 : client.player.getInventory().selectedSlot - 1;
|
||||
return true;
|
||||
} else if (client.currentScreen instanceof CreativeInventoryScreenAccessor inventory) {
|
||||
int currentTab = CreativeInventoryScreenAccessor.getSelectedTab();
|
||||
int nextTab = currentTab + (next ? 1 : -1);
|
||||
if (nextTab < 0)
|
||||
nextTab = ItemGroup.GROUPS.length - 1;
|
||||
else if (nextTab >= ItemGroup.GROUPS.length)
|
||||
nextTab = 0;
|
||||
inventory.midnightcontrols$setSelectedTab(ItemGroup.GROUPS[nextTab]);
|
||||
return true;
|
||||
} else if (client.currentScreen instanceof InventoryScreen inventoryScreen) {
|
||||
var recipeBook = (RecipeBookWidgetAccessor) inventoryScreen.getRecipeBookWidget();
|
||||
var tabs = recipeBook.getTabButtons();
|
||||
var currentTab = recipeBook.getCurrentTab();
|
||||
if (currentTab == null)
|
||||
return false;
|
||||
int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1);
|
||||
if (nextTab < 0)
|
||||
nextTab = tabs.size() - 1;
|
||||
else if (nextTab >= tabs.size())
|
||||
nextTab = 0;
|
||||
currentTab.setToggled(false);
|
||||
recipeBook.setCurrentTab(currentTab = tabs.get(nextTab));
|
||||
currentTab.setToggled(true);
|
||||
recipeBook.midnightcontrols$refreshResults(true);
|
||||
return true;
|
||||
} else if (client.currentScreen instanceof AdvancementsScreenAccessor screen) {
|
||||
var tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList());
|
||||
var tab = screen.getSelectedTab();
|
||||
if (tab == null)
|
||||
return false;
|
||||
for (int i = 0; i < tabs.size(); i++) {
|
||||
if (tabs.get(i).equals(tab)) {
|
||||
int nextTab = i + (next ? 1 : -1);
|
||||
if (nextTab < 0)
|
||||
nextTab = tabs.size() - 1;
|
||||
else if (nextTab >= tabs.size())
|
||||
nextTab = 0;
|
||||
screen.getAdvancementManager().selectTab(tabs.get(nextTab).getRoot(), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (FabricLoader.getInstance().isModLoaded("sodium")) SodiumCompat.handleTabs(client.currentScreen, next);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
public static PressAction handlePage(boolean next) {
|
||||
return (client, button, value, action) -> {
|
||||
if (client.currentScreen instanceof CreativeInventoryScreen) {
|
||||
var screen = (HandledScreenAccessor) client.currentScreen;
|
||||
try {
|
||||
if (next) {
|
||||
return client.currentScreen.children().stream().filter(element -> element instanceof PressableWidget)
|
||||
.map(element -> (PressableWidget) element)
|
||||
.filter(element -> element.getMessage() != null && element.getMessage().getContent() != null)
|
||||
.anyMatch(element -> {
|
||||
if (element.getMessage().getString().equals(">")) {
|
||||
element.onPress();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return client.currentScreen.children().stream().filter(element -> element instanceof PressableWidget)
|
||||
.map(element -> (PressableWidget) element)
|
||||
.filter(element -> element.getMessage() != null && element.getMessage().getContent() != null)
|
||||
.anyMatch(element -> {
|
||||
if (element.getMessage().getString().equals("<")) {
|
||||
element.onPress();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
public static PressAction handleActions() {
|
||||
return (client, button, value, action) -> {
|
||||
if (!(client.currentScreen instanceof HandledScreen<?> screen)) return false;
|
||||
if (client.interactionManager == null || client.player == null)
|
||||
return false;
|
||||
|
||||
if (MidnightControlsClient.get().input.inventoryInteractionCooldown > 0)
|
||||
return true;
|
||||
double x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
|
||||
double y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
|
||||
|
||||
var accessor = (HandledScreenAccessor) screen;
|
||||
Slot slot = ((HandledScreenAccessor) screen).midnightcontrols$getSlotAt(x, y);
|
||||
|
||||
int slotId;
|
||||
if (slot == null) {
|
||||
if (button.getName().equals("take_all")) {
|
||||
((MouseAccessor) client.mouse).setLeftButtonClicked(true);
|
||||
return false;
|
||||
}
|
||||
slotId = accessor.midnightcontrols$isClickOutsideBounds(x, y, accessor.getX(), accessor.getY(), GLFW_MOUSE_BUTTON_1) ? -999 : -1;
|
||||
} else {
|
||||
slotId = slot.id;
|
||||
}
|
||||
var actionType = SlotActionType.PICKUP;
|
||||
int clickData = GLFW.GLFW_MOUSE_BUTTON_1;
|
||||
|
||||
MidnightControlsClient.get().input.inventoryInteractionCooldown = 5;
|
||||
switch (button.getName()) {
|
||||
case "take_all": {
|
||||
if (screen instanceof CreativeInventoryScreen) {
|
||||
if (slot != null && (((CreativeInventoryScreenAccessor) accessor).midnightcontrols$isCreativeInventorySlot(slot) || MidnightControlsCompat.streamCompatHandlers().anyMatch(handler -> handler.isCreativeSlot(screen, slot))))
|
||||
actionType = SlotActionType.CLONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "take": {
|
||||
clickData = GLFW_MOUSE_BUTTON_2;
|
||||
break;
|
||||
}
|
||||
case "quick_move": {
|
||||
actionType = SlotActionType.QUICK_MOVE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
accessor.midnightcontrols$onMouseClick(slot, slotId, clickData, actionType);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean handlePauseGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action) {
|
||||
if (action == ButtonState.PRESS) {
|
||||
// If in game, then pause the game.
|
||||
if (client.currentScreen == null)
|
||||
client.openPauseMenu(false);
|
||||
else if (client.currentScreen instanceof HandledScreen && client.player != null) // If the current screen is a container then close it.
|
||||
client.player.closeHandledScreen();
|
||||
else // Else just close the current screen.
|
||||
client.currentScreen.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the screenshot action.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the binding which fired the action
|
||||
* @param action the action done on the binding
|
||||
* @return true if handled, else false
|
||||
*/
|
||||
public static boolean handleScreenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action) {
|
||||
if (action == ButtonState.RELEASE)
|
||||
ScreenshotRecorder.saveScreenshot(client.runDirectory, client.getFramebuffer(),
|
||||
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean handleToggleSneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
|
||||
button.asKeyBinding().ifPresent(binding -> {
|
||||
boolean sneakToggled = client.options.getSneakToggled().getValue();
|
||||
if (client.player.getAbilities().flying && sneakToggled)
|
||||
client.options.getSneakToggled().setValue(false);
|
||||
binding.setPressed(button.pressed);
|
||||
if (client.player.getAbilities().flying && sneakToggled)
|
||||
client.options.getSneakToggled().setValue(true);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public static PressAction handleInventorySlotPad(int direction) {
|
||||
return (client, binding, value, action) -> {
|
||||
if (!(client.currentScreen instanceof HandledScreen inventory && action != ButtonState.RELEASE))
|
||||
return false;
|
||||
|
||||
var accessor = (HandledScreenAccessor) inventory;
|
||||
int guiLeft = accessor.getX();
|
||||
int guiTop = accessor.getY();
|
||||
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
|
||||
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
|
||||
|
||||
// Finds the hovered slot.
|
||||
var mouseSlot = accessor.midnightcontrols$getSlotAt(mouseX, mouseY);
|
||||
|
||||
// Finds the closest slot in the GUI within 14 pixels.
|
||||
Optional<Slot> closestSlot = inventory.getScreenHandler().slots.parallelStream()
|
||||
.filter(Predicate.isEqual(mouseSlot).negate())
|
||||
.map(slot -> {
|
||||
int posX = guiLeft + slot.x + 8;
|
||||
int posY = guiTop + slot.y + 8;
|
||||
|
||||
int otherPosX = (int) mouseX;
|
||||
int otherPosY = (int) mouseY;
|
||||
if (mouseSlot != null) {
|
||||
otherPosX = guiLeft + mouseSlot.x + 8;
|
||||
otherPosY = guiTop + mouseSlot.y + 8;
|
||||
}
|
||||
|
||||
// Distance between the slot and the cursor.
|
||||
double distance = Math.sqrt(Math.pow(posX - otherPosX, 2) + Math.pow(posY - otherPosY, 2));
|
||||
return Pair.of(slot, distance);
|
||||
}).filter(entry -> {
|
||||
var slot = entry.key;
|
||||
int posX = guiLeft + slot.x + 8;
|
||||
int posY = guiTop + slot.y + 8;
|
||||
int otherPosX = (int) mouseX;
|
||||
int otherPosY = (int) mouseY;
|
||||
if (mouseSlot != null) {
|
||||
otherPosX = guiLeft + mouseSlot.x + 8;
|
||||
otherPosY = guiTop + mouseSlot.y + 8;
|
||||
}
|
||||
if (direction == 0)
|
||||
return posY < otherPosY;
|
||||
else if (direction == 1)
|
||||
return posY > otherPosY;
|
||||
else if (direction == 2)
|
||||
return posX > otherPosX;
|
||||
else if (direction == 3)
|
||||
return posX < otherPosX;
|
||||
else
|
||||
return false;
|
||||
})
|
||||
.min(Comparator.comparingDouble(p -> p.value))
|
||||
.map(p -> p.key);
|
||||
|
||||
if (closestSlot.isPresent()) {
|
||||
var slot = closestSlot.get();
|
||||
int x = guiLeft + slot.x + 8;
|
||||
int y = guiTop + slot.y + 8;
|
||||
InputManager.queueMousePosition(x * (double) client.getWindow().getWidth() / (double) client.getWindow().getScaledWidth(),
|
||||
y * (double) client.getWindow().getHeight() / (double) client.getWindow().getScaledHeight());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns always true to the filter.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the affected binding
|
||||
* @return true
|
||||
*/
|
||||
public static boolean always(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the client is in game or not.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the affected binding
|
||||
* @return true if the client is in game, else false
|
||||
*/
|
||||
public static boolean inGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
|
||||
return client.currentScreen == null && MidnightControlsClient.get().input.screenCloseCooldown <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the client is in a non-interactive screen (which means require mouse input) or not.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the affected binding
|
||||
* @return true if the client is in a non-interactive screen, else false
|
||||
*/
|
||||
public static boolean inNonInteractiveScreens(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
|
||||
if (client.currentScreen == null)
|
||||
return false;
|
||||
return !MidnightInput.isScreenInteractive(client.currentScreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the client is in an inventory or not.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the affected binding
|
||||
* @return true if the client is in an inventory, else false
|
||||
*/
|
||||
public static boolean inInventory(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
|
||||
return client.currentScreen instanceof HandledScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the client is in the advancements screen or not.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param binding the affected binding
|
||||
* @return true if the client is in the advancements screen, else false
|
||||
*/
|
||||
public static boolean inAdvancements(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
|
||||
return client.currentScreen instanceof AdvancementsScreen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.client.ButtonState;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.util.MouseAccessor;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.aperlambda.lambdacommon.Identifier;
|
||||
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents an input manager for controllers.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class InputManager {
|
||||
public static final InputManager INPUT_MANAGER = new InputManager();
|
||||
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
|
||||
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
|
||||
public static final Int2ObjectMap<ButtonState> STATES = new Int2ObjectOpenHashMap<>();
|
||||
public static final Int2FloatMap BUTTON_VALUES = new Int2FloatOpenHashMap();
|
||||
public int prevTargetMouseX = 0;
|
||||
public int prevTargetMouseY = 0;
|
||||
public int targetMouseX = 0;
|
||||
public int targetMouseY = 0;
|
||||
|
||||
protected InputManager() {
|
||||
}
|
||||
|
||||
public void tick(@NotNull MinecraftClient client) {
|
||||
if (MidnightControlsConfig.autoSwitchMode && !MidnightControlsConfig.isEditing && MidnightControlsConfig.controlsMode != ControlsMode.TOUCHSCREEN)
|
||||
if (MidnightControlsConfig.getController().isConnected() && MidnightControlsConfig.getController().isGamepad())
|
||||
MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
|
||||
else MidnightControlsConfig.controlsMode = ControlsMode.DEFAULT;
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
|
||||
this.controllerTick(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void controllerTick(@NotNull MinecraftClient client) {
|
||||
this.prevTargetMouseX = this.targetMouseX;
|
||||
this.prevTargetMouseY = this.targetMouseY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the mouse position. Should only be called on pre render of a screen.
|
||||
*
|
||||
* @param client the client instance
|
||||
*/
|
||||
public void updateMousePosition(@NotNull MinecraftClient client) {
|
||||
Objects.requireNonNull(client, "Client instance cannot be null.");
|
||||
if (this.prevTargetMouseX != this.targetMouseX || this.prevTargetMouseY != this.targetMouseY) {
|
||||
double mouseX = this.prevTargetMouseX + (this.targetMouseX - this.prevTargetMouseX) * client.getTickDelta() + 0.5;
|
||||
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getTickDelta() + 0.5;
|
||||
if (!MidnightControlsConfig.virtualMouse)
|
||||
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
|
||||
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the mouse position.
|
||||
*
|
||||
* @param windowWidth the window width
|
||||
* @param windowHeight the window height
|
||||
*/
|
||||
public void resetMousePosition(int windowWidth, int windowHeight) {
|
||||
this.targetMouseX = this.prevTargetMouseX = (int) (windowWidth / 2.F);
|
||||
this.targetMouseY = this.prevTargetMouseY = (int) (windowHeight / 2.F);
|
||||
}
|
||||
|
||||
public void resetMouseTarget(@NotNull MinecraftClient client) {
|
||||
double mouseX = client.mouse.getX();
|
||||
double mouseY = client.mouse.getY();
|
||||
this.prevTargetMouseX = this.targetMouseX = (int) mouseX;
|
||||
this.prevTargetMouseY = this.targetMouseY = (int) mouseY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified binding is registered or not.
|
||||
*
|
||||
* @param binding the binding to check
|
||||
* @return true if the binding is registered, else false
|
||||
*/
|
||||
public static boolean hasBinding(@NotNull ButtonBinding binding) {
|
||||
return BINDINGS.contains(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified binding is registered or not.
|
||||
*
|
||||
* @param name the name of the binding to check
|
||||
* @return true if the binding is registered, else false
|
||||
*/
|
||||
public static boolean hasBinding(@NotNull String name) {
|
||||
return BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified binding is registered or not.
|
||||
*
|
||||
* @param identifier the identifier of the binding to check
|
||||
* @return true if the binding is registered, else false
|
||||
*/
|
||||
public static boolean hasBinding(@NotNull Identifier identifier) {
|
||||
return hasBinding(identifier.getNamespace() + "." + identifier.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a button binding.
|
||||
*
|
||||
* @param binding the binding to register
|
||||
* @return the registered binding
|
||||
*/
|
||||
public static @NotNull ButtonBinding registerBinding(@NotNull ButtonBinding binding) {
|
||||
if (hasBinding(binding))
|
||||
throw new IllegalStateException("Cannot register twice a button binding in the registry.");
|
||||
BINDINGS.add(binding);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
|
||||
return registerBinding(new ButtonBinding(id.getNamespace() + "." + id.getName(), defaultButton, actions, filter, hasCooldown));
|
||||
}
|
||||
|
||||
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, boolean hasCooldown) {
|
||||
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
|
||||
}
|
||||
|
||||
public static @NotNull ButtonBinding registerBinding(@NotNull net.minecraft.util.Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
|
||||
return registerBinding(new Identifier(id.getNamespace(), id.getPath()), defaultButton, actions, filter, hasCooldown);
|
||||
}
|
||||
|
||||
public static @NotNull ButtonBinding registerBinding(@NotNull net.minecraft.util.Identifier id, int[] defaultButton, boolean hasCooldown) {
|
||||
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts bindings to get bindings with the higher button counts first.
|
||||
*/
|
||||
public static void sortBindings() {
|
||||
synchronized (BINDINGS) {
|
||||
var sorted = BINDINGS.stream()
|
||||
.sorted(Collections.reverseOrder(Comparator.comparingInt(binding -> binding.getButton().length))).toList();
|
||||
BINDINGS.clear();
|
||||
BINDINGS.addAll(sorted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a category of button bindings.
|
||||
*
|
||||
* @param category the category to register
|
||||
* @return the registered category
|
||||
*/
|
||||
public static ButtonCategory registerCategory(@NotNull ButtonCategory category) {
|
||||
CATEGORIES.add(category);
|
||||
return category;
|
||||
}
|
||||
|
||||
public static ButtonCategory registerCategory(@NotNull Identifier identifier, int priority) {
|
||||
return registerCategory(new ButtonCategory(identifier, priority));
|
||||
}
|
||||
|
||||
public static ButtonCategory registerCategory(@NotNull Identifier identifier) {
|
||||
return registerCategory(new ButtonCategory(identifier));
|
||||
}
|
||||
|
||||
protected static ButtonCategory registerDefaultCategory(@NotNull String key, @NotNull Consumer<ButtonCategory> keyAdder) {
|
||||
var category = registerCategory(new Identifier("minecraft", key), CATEGORIES.size());
|
||||
keyAdder.accept(category);
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the button bindings from configuration.
|
||||
*/
|
||||
public static void loadButtonBindings() {
|
||||
var queue = new ArrayList<>(BINDINGS);
|
||||
queue.forEach(MidnightControlsConfig::loadButtonBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binding state.
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the current state of the binding
|
||||
*/
|
||||
public static @NotNull ButtonState getBindingState(@NotNull ButtonBinding binding) {
|
||||
var state = ButtonState.REPEAT;
|
||||
for (int btn : binding.getButton()) {
|
||||
var btnState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
|
||||
if (btnState == ButtonState.PRESS)
|
||||
state = ButtonState.PRESS;
|
||||
else if (btnState == ButtonState.RELEASE) {
|
||||
state = ButtonState.RELEASE;
|
||||
break;
|
||||
} else if (btnState == ButtonState.NONE) {
|
||||
state = ButtonState.NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
public static float getBindingValue(@NotNull ButtonBinding binding, @NotNull ButtonState state) {
|
||||
if (state.isUnpressed())
|
||||
return 0.f;
|
||||
|
||||
float value = 0.f;
|
||||
for (int btn : binding.getButton()) {
|
||||
if (ButtonBinding.isAxis(btn)) {
|
||||
value = BUTTON_VALUES.getOrDefault(btn, 1.f);
|
||||
break;
|
||||
} else {
|
||||
value = 1.f;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the button has duplicated bindings.
|
||||
*
|
||||
* @param button the button to check
|
||||
* @return true if the button has duplicated bindings, else false
|
||||
*/
|
||||
public static boolean hasDuplicatedBindings(int[] button) {
|
||||
return BINDINGS.parallelStream().filter(binding -> areButtonsEquivalent(binding.getButton(), button)).count() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the button has duplicated bindings.
|
||||
*
|
||||
* @param binding the binding to check
|
||||
* @return true if the button has duplicated bindings, else false
|
||||
*/
|
||||
public static boolean hasDuplicatedBindings(ButtonBinding binding) {
|
||||
return BINDINGS.parallelStream().filter(other -> areButtonsEquivalent(other.getButton(), binding.getButton()) && other.filter.equals(binding.filter)).count() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified buttons are equivalent or not.
|
||||
*
|
||||
* @param buttons1 first set of buttons
|
||||
* @param buttons2 second set of buttons
|
||||
* @return true if the two sets of buttons are equivalent, else false
|
||||
*/
|
||||
public static boolean areButtonsEquivalent(int[] buttons1, int[] buttons2) {
|
||||
if (buttons1.length != buttons2.length)
|
||||
return false;
|
||||
int count = 0;
|
||||
for (int btn : buttons1) {
|
||||
for (int btn2 : buttons2) {
|
||||
if (btn == btn2) {
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count == buttons1.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the button set contains the specified button or not.
|
||||
*
|
||||
* @param buttons the button set
|
||||
* @param button the button to check
|
||||
* @return true if the button set contains the specified button, else false
|
||||
*/
|
||||
public static boolean containsButton(int[] buttons, int button) {
|
||||
return Arrays.stream(buttons).anyMatch(btn -> btn == button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the button states.
|
||||
*/
|
||||
public static void updateStates() {
|
||||
for (var entry : STATES.int2ObjectEntrySet()) {
|
||||
if (entry.getValue() == ButtonState.PRESS)
|
||||
STATES.put(entry.getIntKey(), ButtonState.REPEAT);
|
||||
else if (entry.getValue() == ButtonState.RELEASE)
|
||||
STATES.put(entry.getIntKey(), ButtonState.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateBindings(@NotNull MinecraftClient client) {
|
||||
var skipButtons = new IntArrayList();
|
||||
record ButtonStateValue(ButtonState state, float value) {
|
||||
}
|
||||
var states = new Object2ObjectOpenHashMap<ButtonBinding, ButtonStateValue>();
|
||||
for (var binding : BINDINGS) {
|
||||
var state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE;
|
||||
if (skipButtons.intStream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
|
||||
if (binding.pressed)
|
||||
state = ButtonState.RELEASE;
|
||||
else
|
||||
state = ButtonState.NONE;
|
||||
}
|
||||
|
||||
if (state == ButtonState.RELEASE && !binding.pressed) {
|
||||
state = ButtonState.NONE;
|
||||
}
|
||||
|
||||
binding.pressed = state.isPressed();
|
||||
binding.update();
|
||||
if (binding.pressed)
|
||||
Arrays.stream(binding.getButton()).forEach(skipButtons::add);
|
||||
|
||||
float value = getBindingValue(binding, state);
|
||||
|
||||
states.put(binding, new ButtonStateValue(state, value));
|
||||
}
|
||||
|
||||
states.forEach((binding, state) -> {
|
||||
if (state.state() != ButtonState.NONE) {
|
||||
binding.handle(client, state.value(), state.state());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void queueMousePosition(double x, double y) {
|
||||
INPUT_MANAGER.targetMouseX = (int) MathHelper.clamp(x, 0, MinecraftClient.getInstance().getWindow().getWidth());
|
||||
INPUT_MANAGER.targetMouseY = (int) MathHelper.clamp(y, 0, MinecraftClient.getInstance().getWindow().getHeight());
|
||||
}
|
||||
|
||||
public static void queueMoveMousePosition(double x, double y) {
|
||||
queueMousePosition(INPUT_MANAGER.targetMouseX + x, INPUT_MANAGER.targetMouseY + y);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<ButtonBinding> streamBindings() {
|
||||
return BINDINGS.stream();
|
||||
}
|
||||
|
||||
public static @NotNull Stream<ButtonCategory> streamCategories() {
|
||||
return CATEGORIES.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new key binding instance.
|
||||
*
|
||||
* @param id the identifier of the key binding
|
||||
* @param type the type
|
||||
* @param code the code
|
||||
* @param category the category of the key binding
|
||||
* @return the key binding
|
||||
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String)
|
||||
*/
|
||||
public static @NotNull KeyBinding makeKeyBinding(@NotNull net.minecraft.util.Identifier id, InputUtil.Type type, int code, @NotNull String category) {
|
||||
return makeKeyBinding(new Identifier(id.getNamespace(), id.getPath()), type, code, category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new key binding instance.
|
||||
*
|
||||
* @param id the identifier of the key binding
|
||||
* @param type the type
|
||||
* @param code the code
|
||||
* @param category the category of the key binding
|
||||
* @return the key binding
|
||||
* @see #makeKeyBinding(net.minecraft.util.Identifier, InputUtil.Type, int, String)
|
||||
*/
|
||||
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull String category) {
|
||||
return new KeyBinding(String.format("key.%s.%s", id.getNamespace(), id.getName()), type, code, category);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.ButtonState;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.input.Input;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.tutorial.MovementTutorialStepHandler;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.enchantment.SoulSpeedEnchantment;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.nbt.scanner.SimpleNbtScanner;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents the movement handler.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.6.0
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class MovementHandler implements PressAction {
|
||||
public static final MovementHandler HANDLER = new MovementHandler();
|
||||
private boolean shouldOverrideMovement = false;
|
||||
private boolean pressingForward = false;
|
||||
private boolean pressingBack = false;
|
||||
private boolean pressingLeft = false;
|
||||
private boolean pressingRight = false;
|
||||
private float movementForward = 0.f;
|
||||
private float movementSideways = 0.f;
|
||||
|
||||
private MovementHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies movement input of this handler to the player's input.
|
||||
*
|
||||
* @param player The client player.
|
||||
*/
|
||||
public void applyMovement(@NotNull ClientPlayerEntity player) {
|
||||
if (!this.shouldOverrideMovement)
|
||||
return;
|
||||
player.input.pressingForward = this.pressingForward;
|
||||
player.input.pressingBack = this.pressingBack;
|
||||
player.input.pressingLeft = this.pressingLeft;
|
||||
player.input.pressingRight = this.pressingRight;
|
||||
player.input.movementForward = this.movementForward;
|
||||
player.input.movementSideways = this.movementSideways;
|
||||
this.shouldOverrideMovement = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
|
||||
if (client.currentScreen != null || client.player == null)
|
||||
return this.shouldOverrideMovement = false;
|
||||
|
||||
int direction = 0;
|
||||
if (button == ButtonBinding.FORWARD || button == ButtonBinding.LEFT)
|
||||
direction = 1;
|
||||
else if (button == ButtonBinding.BACK || button == ButtonBinding.RIGHT)
|
||||
direction = -1;
|
||||
|
||||
if (action.isUnpressed())
|
||||
direction = 0;
|
||||
|
||||
this.shouldOverrideMovement = direction != 0;
|
||||
|
||||
if (MidnightControlsConfig.analogMovement) {
|
||||
value = (float) Math.pow(value, 2);
|
||||
} else value = 1.f;
|
||||
|
||||
if (button == ButtonBinding.FORWARD || button == ButtonBinding.BACK) {
|
||||
// Handle forward movement.
|
||||
this.pressingForward = direction > 0;
|
||||
this.pressingBack = direction < 0;
|
||||
this.movementForward = direction * value;
|
||||
|
||||
// Slowing down if sneaking or crawling.
|
||||
if (client.player.shouldSlowDown()) {
|
||||
this.movementForward *= MathHelper.clamp(0.3F + EnchantmentHelper.getSwiftSneakSpeedBoost(client.player), 0.0F, 1.0F);
|
||||
}
|
||||
} else {
|
||||
// Handle sideways movement.
|
||||
this.pressingLeft = direction > 0;
|
||||
this.pressingRight = direction < 0;
|
||||
this.movementSideways = direction * value;
|
||||
|
||||
// Slowing down if sneaking or crawling.
|
||||
if (client.player.shouldSlowDown()) {
|
||||
this.movementSideways *= MathHelper.clamp(0.3F + EnchantmentHelper.getSwiftSneakSpeedBoost(client.player), 0.0F, 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
return this.shouldOverrideMovement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.controller;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.ButtonState;
|
||||
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.StickyKeyBinding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a press action callback.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PressAction {
|
||||
PressAction DEFAULT_ACTION = (client, button, value, action) -> {
|
||||
if (action == ButtonState.REPEAT || client.currentScreen != null)
|
||||
return false;
|
||||
button.asKeyBinding().ifPresent(binding -> {
|
||||
if (binding instanceof StickyKeyBinding)
|
||||
binding.setPressed(button.pressed);
|
||||
else
|
||||
((KeyBindingAccessor) binding).midnightcontrols$handlePressState(button.isButtonDown());
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles when there is a press action.
|
||||
*
|
||||
* @param client the client instance
|
||||
* @param action the action done
|
||||
*/
|
||||
boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.option.SpruceOption;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
|
||||
import dev.lambdaurora.spruceui.widget.text.SpruceTextAreaWidget;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* Represents the controller mappings file editor screen.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.4.3
|
||||
*/
|
||||
public class MappingsStringInputWidget extends SpruceContainerWidget {
|
||||
private final SpruceOption reloadMappingsOption;
|
||||
private String mappings;
|
||||
private SpruceTextAreaWidget textArea;
|
||||
|
||||
protected MappingsStringInputWidget(Position position, int width, int height) {
|
||||
super(position, width, height);
|
||||
//super(new TranslatableText("midnightcontrols.menu.title.mappings.string"));
|
||||
|
||||
this.reloadMappingsOption = ReloadControllerMappingsOption.newOption(btn -> {
|
||||
this.writeMappings();
|
||||
});
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
public void removed() {
|
||||
this.writeMappings();
|
||||
Controller.updateMappings();
|
||||
}
|
||||
|
||||
public void onClose() {
|
||||
this.removed();
|
||||
}
|
||||
|
||||
public void writeMappings() {
|
||||
if (this.textArea != null) {
|
||||
this.mappings = this.textArea.getText();
|
||||
try {
|
||||
var fw = new FileWriter(MidnightControlsClient.MAPPINGS_FILE, false);
|
||||
fw.write(this.mappings);
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
if (this.client != null)
|
||||
this.client.getToastManager().add(SystemToast.create(this.client, SystemToast.Type.TUTORIAL_HINT,
|
||||
Text.translatable("midnightcontrols.controller.mappings.error.write"), Text.empty()));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
if (this.textArea != null) {
|
||||
this.mappings = this.textArea.getText();
|
||||
}
|
||||
|
||||
var mappings = "";
|
||||
|
||||
if (this.mappings != null)
|
||||
mappings = this.mappings;
|
||||
else if (MidnightControlsClient.MAPPINGS_FILE.exists()) {
|
||||
try {
|
||||
mappings = String.join("\n", Files.readAllLines(MidnightControlsClient.MAPPINGS_FILE.toPath()));
|
||||
this.mappings = mappings;
|
||||
} catch (IOException e) {
|
||||
/* Ignored */
|
||||
}
|
||||
}
|
||||
|
||||
int textFieldWidth = (int) (this.width * (5.0 / 6.0));
|
||||
this.textArea = new SpruceTextAreaWidget(Position.of(this, this.width / 2 - textFieldWidth / 2, 0), textFieldWidth, this.height - 50, Text.literal(mappings));
|
||||
this.textArea.setText(mappings);
|
||||
// Display as many lines as possible
|
||||
this.textArea.setDisplayedLines(this.textArea.getInnerHeight() / this.client.textRenderer.fontHeight);
|
||||
this.addChild(this.textArea);
|
||||
|
||||
this.addChild(this.reloadMappingsOption.createWidget(Position.of(this.width / 2 - 155, this.height - 29), 310));
|
||||
}
|
||||
|
||||
/*public void renderTitle(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 8, 16777215);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
|
||||
import eu.midnightdust.midnightcontrols.client.HudSide;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import dev.lambdaurora.spruceui.hud.Hud;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the midnightcontrols HUD.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class MidnightControlsHud extends Hud {
|
||||
private final MidnightControlsClient mod;
|
||||
private MinecraftClient client;
|
||||
private int attackWidth = 0;
|
||||
private int attackButtonWidth = 0;
|
||||
private int dropItemWidth = 0;
|
||||
private int dropItemButtonWidth = 0;
|
||||
private int inventoryWidth = 0;
|
||||
private int inventoryButtonWidth = 0;
|
||||
private int swapHandsWidth = 0;
|
||||
private int swapHandsButtonWidth = 0;
|
||||
private int useWidth = 0;
|
||||
private int useButtonWidth = 0;
|
||||
private BlockHitResult placeHitResult;
|
||||
private String attackAction = "";
|
||||
private String placeAction = "";
|
||||
private int ticksDisplayedCrosshair = 0;
|
||||
private static float scale = 1f;
|
||||
|
||||
public MidnightControlsHud(@NotNull MidnightControlsClient mod) {
|
||||
super(new Identifier(MidnightControlsConstants.NAMESPACE, "hud/button_indicator"));
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(@NotNull MinecraftClient client, int screenWidth, int screenHeight) {
|
||||
super.init(client, screenWidth, screenHeight);
|
||||
this.client = client;
|
||||
this.inventoryWidth = this.width(ButtonBinding.INVENTORY);
|
||||
this.inventoryButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.INVENTORY);
|
||||
this.swapHandsWidth = this.width(ButtonBinding.SWAP_HANDS);
|
||||
this.swapHandsButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.SWAP_HANDS);
|
||||
this.dropItemWidth = this.width(ButtonBinding.DROP_ITEM);
|
||||
this.dropItemButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.DROP_ITEM);
|
||||
this.attackButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.ATTACK);
|
||||
this.useButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.USE);
|
||||
if (client.options.getGuiScale().getValue() >= 4) {
|
||||
scale = 0.75f;
|
||||
} else scale = 1f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders the midnightcontrols' HUD.
|
||||
*/
|
||||
@Override
|
||||
public void render(MatrixStack matrices, float tickDelta) {
|
||||
if (this.client == null) return;
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && this.client.currentScreen == null) {
|
||||
int y = bottom(2);
|
||||
matrices.push();
|
||||
if (scale != 1f) matrices.scale(scale,scale,scale);
|
||||
this.renderFirstIcons(matrices, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : (int) ((client.getWindow().getScaledWidth() - 2) * (1 / scale)), y);
|
||||
this.renderSecondIcons(matrices, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : (int) ((client.getWindow().getScaledWidth() - 2) * (1 / scale)), y);
|
||||
this.renderFirstSection(matrices, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : (int) ((client.getWindow().getScaledWidth() - 2) * (1 / scale)), y);
|
||||
this.renderSecondSection(matrices, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : (int) ((client.getWindow().getScaledWidth() - 2) * (1 / scale)), y);
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
if (this.mod.reacharound.isLastReacharoundVertical()) {
|
||||
// Render crosshair indicator.
|
||||
var window = this.client.getWindow();
|
||||
var text = "[ ]";
|
||||
|
||||
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickDelta) / 5F;
|
||||
scale *= scale;
|
||||
int opacity = ((int) (255 * scale)) << 24;
|
||||
|
||||
this.client.textRenderer.draw(matrices, text, window.getScaledWidth() / 2.f - this.client.textRenderer.getWidth(text) / 2.f,
|
||||
window.getScaledHeight() / 2.f - 4, 0xCCCCCC | opacity);
|
||||
}
|
||||
}
|
||||
|
||||
public void renderFirstIcons(MatrixStack matrices, int x, int y) {
|
||||
int offset = 2 + this.inventoryWidth + this.inventoryButtonWidth + 4;
|
||||
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x : x - this.inventoryButtonWidth;
|
||||
this.drawButton(matrices, currentX, y, ButtonBinding.INVENTORY, true);
|
||||
this.drawButton(matrices, currentX += (MidnightControlsConfig.hudSide == HudSide.LEFT ? offset : -offset), y, ButtonBinding.SWAP_HANDS, true);
|
||||
offset = 2 + this.swapHandsWidth + this.dropItemButtonWidth + 4;
|
||||
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.RIGHT) {
|
||||
currentX += -offset;
|
||||
} else {
|
||||
currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x : x - this.dropItemButtonWidth;
|
||||
y -= 24;
|
||||
}
|
||||
this.drawButton(matrices, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
|
||||
}
|
||||
|
||||
public void renderSecondIcons(MatrixStack matrices, int x, int y) {
|
||||
int offset;
|
||||
int currentX = x;
|
||||
if (!this.placeAction.isEmpty()) {
|
||||
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
|
||||
currentX -= this.useButtonWidth;
|
||||
this.drawButton(matrices, currentX, y, ButtonBinding.USE, true);
|
||||
offset = 2 + this.useWidth + 4;
|
||||
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.LEFT) {
|
||||
currentX -= offset;
|
||||
} else {
|
||||
currentX = x;
|
||||
y -= 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
|
||||
currentX -= this.attackButtonWidth;
|
||||
|
||||
this.drawButton(matrices, currentX, y, ButtonBinding.ATTACK, this.attackWidth != 0);
|
||||
}
|
||||
|
||||
public void renderFirstSection(MatrixStack matrices, int x, int y) {
|
||||
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.inventoryButtonWidth + 2 : x - this.inventoryButtonWidth - 2 - this.inventoryWidth;
|
||||
this.drawTip(matrices, currentX, y, ButtonBinding.INVENTORY, true);
|
||||
currentX += MidnightControlsConfig.hudSide == HudSide.LEFT ? this.inventoryWidth + 4 + this.swapHandsButtonWidth + 2
|
||||
: -this.swapHandsWidth - 2 - this.swapHandsButtonWidth - 4;
|
||||
this.drawTip(matrices, currentX, y, ButtonBinding.SWAP_HANDS, true);
|
||||
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.RIGHT) {
|
||||
currentX += -this.dropItemWidth - 2 - this.dropItemButtonWidth - 4;
|
||||
} else {
|
||||
y -= 24;
|
||||
currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.dropItemButtonWidth + 2 : x - this.dropItemButtonWidth - 2 - this.dropItemWidth;
|
||||
}
|
||||
this.drawTip(matrices, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
|
||||
}
|
||||
|
||||
public void renderSecondSection(MatrixStack matrices, int x, int y) {
|
||||
int currentX = x;
|
||||
|
||||
if (!this.placeAction.isEmpty()) {
|
||||
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.useButtonWidth + 2 : -this.useButtonWidth - 2 - this.useWidth;
|
||||
|
||||
this.drawTip(matrices, currentX, y, this.placeAction, true);
|
||||
|
||||
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.LEFT) {
|
||||
currentX -= 4;
|
||||
} else {
|
||||
currentX = x;
|
||||
y -= 24;
|
||||
}
|
||||
}
|
||||
|
||||
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.attackButtonWidth + 2 : -this.attackButtonWidth - 2 - this.attackWidth;
|
||||
|
||||
this.drawTip(matrices, currentX, y, this.attackAction, this.attackWidth != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
|
||||
if (this.client.crosshairTarget == null)
|
||||
return;
|
||||
|
||||
String placeAction;
|
||||
|
||||
// Update "Use" tip status.
|
||||
if (this.client.crosshairTarget.getType() == HitResult.Type.MISS) {
|
||||
this.placeHitResult = this.mod.reacharound.getLastReacharoundResult();
|
||||
this.attackAction = "";
|
||||
this.attackWidth = 0;
|
||||
} else {
|
||||
if (this.client.crosshairTarget.getType() == HitResult.Type.BLOCK)
|
||||
this.placeHitResult = (BlockHitResult) this.client.crosshairTarget;
|
||||
else
|
||||
this.placeHitResult = null;
|
||||
|
||||
this.attackAction = this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "midnightcontrols.action.hit" : ButtonBinding.ATTACK.getTranslationKey();
|
||||
this.attackWidth = this.width(attackAction);
|
||||
}
|
||||
|
||||
if (this.mod.reacharound.isLastReacharoundVertical()) {
|
||||
if (this.ticksDisplayedCrosshair < 5)
|
||||
this.ticksDisplayedCrosshair++;
|
||||
} else {
|
||||
this.ticksDisplayedCrosshair = 0;
|
||||
}
|
||||
|
||||
var customAttackAction = MidnightControlsCompat.getAttackActionAt(this.client, this.placeHitResult);
|
||||
if (customAttackAction != null) {
|
||||
this.attackAction = customAttackAction;
|
||||
this.attackWidth = this.width(customAttackAction);
|
||||
}
|
||||
|
||||
ItemStack stack = null;
|
||||
if (this.client.player != null) {
|
||||
stack = this.client.player.getMainHandStack();
|
||||
if (stack == null || stack.isEmpty())
|
||||
stack = this.client.player.getOffHandStack();
|
||||
}
|
||||
if (stack == null || stack.isEmpty()) {
|
||||
placeAction = "";
|
||||
} else {
|
||||
if (this.placeHitResult != null && stack.getItem() instanceof BlockItem) {
|
||||
placeAction = "midnightcontrols.action.place";
|
||||
} else {
|
||||
placeAction = ButtonBinding.USE.getTranslationKey();
|
||||
}
|
||||
}
|
||||
|
||||
var customUseAction = MidnightControlsCompat.getUseActionAt(this.client, this.placeHitResult);
|
||||
if (customUseAction != null)
|
||||
placeAction = customUseAction;
|
||||
|
||||
this.placeAction = placeAction;
|
||||
|
||||
// Cache the "Use" tip width.
|
||||
if (this.placeAction.isEmpty())
|
||||
this.useWidth = 0;
|
||||
else
|
||||
this.useWidth = this.width(this.placeAction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTicks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int bottom(int y) {
|
||||
return (int) ((this.client.getWindow().getScaledHeight() * (1/scale)) - y - MidnightControlsRenderer.ICON_SIZE);
|
||||
}
|
||||
|
||||
private int width(@NotNull ButtonBinding binding) {
|
||||
return this.width(binding.getTranslationKey());
|
||||
}
|
||||
|
||||
private int width(@Nullable String text) {
|
||||
if (text == null || text.isEmpty())
|
||||
return 0;
|
||||
return this.client.textRenderer.getWidth(I18n.translate(text));
|
||||
}
|
||||
|
||||
private void drawButton(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display) {
|
||||
if (display)
|
||||
MidnightControlsRenderer.drawButton(matrices, x, y, button, this.client);
|
||||
}
|
||||
|
||||
private void drawTip(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display) {
|
||||
this.drawTip(matrices, x, y, button.getTranslationKey(), display);
|
||||
}
|
||||
|
||||
private void drawTip(MatrixStack matrices, int x, int y, @NotNull String action, boolean display) {
|
||||
if (!display)
|
||||
return;
|
||||
var translatedAction = I18n.translate(action);
|
||||
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - this.client.textRenderer.fontHeight / 2) + 1;
|
||||
this.client.textRenderer.draw(matrices, translatedAction, (float) x, (float) (y + textY), 14737632);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.ControllerType;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
* Represents the midnightcontrols renderer.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class MidnightControlsRenderer {
|
||||
public static final int ICON_SIZE = 20;
|
||||
private static final int BUTTON_SIZE = 15;
|
||||
private static final int AXIS_SIZE = 18;
|
||||
|
||||
public static int getButtonSize(int button) {
|
||||
return switch (button) {
|
||||
case -1 -> 0;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200,
|
||||
GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200,
|
||||
GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200,
|
||||
GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200 -> AXIS_SIZE;
|
||||
default -> BUTTON_SIZE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the binding icon width.
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the width
|
||||
*/
|
||||
public static int getBindingIconWidth(@NotNull ButtonBinding binding) {
|
||||
return getBindingIconWidth(binding.getButton());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the binding icon width.
|
||||
*
|
||||
* @param buttons the buttons
|
||||
* @return the width
|
||||
*/
|
||||
public static int getBindingIconWidth(int[] buttons) {
|
||||
int width = 0;
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
width += ICON_SIZE;
|
||||
if (i + 1 < buttons.length) {
|
||||
width += 2;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
public static ButtonSize drawButton(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client) {
|
||||
return drawButton(matrices, x, y, button.getButton(), client);
|
||||
}
|
||||
|
||||
public static ButtonSize drawButton(MatrixStack matrices, int x, int y, int[] buttons, @NotNull MinecraftClient client) {
|
||||
int height = 0;
|
||||
int length = 0;
|
||||
int currentX = x;
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
int btn = buttons[i];
|
||||
int size = drawButton(matrices, currentX, y, btn, client);
|
||||
if (size > height)
|
||||
height = size;
|
||||
length += size;
|
||||
if (i + 1 < buttons.length) {
|
||||
length += 2;
|
||||
currentX = x + length;
|
||||
}
|
||||
}
|
||||
return new ButtonSize(length, height);
|
||||
}
|
||||
|
||||
public static int drawButton(MatrixStack matrices, int x, int y, int button, @NotNull MinecraftClient client) {
|
||||
boolean second = false;
|
||||
if (button == -1)
|
||||
return 0;
|
||||
else if (button >= 500) {
|
||||
button -= 1000;
|
||||
second = true;
|
||||
}
|
||||
|
||||
int controllerType = MidnightControlsConfig.controllerType == ControllerType.DEFAULT ? MidnightControlsConfig.matchControllerToType().getId() : MidnightControlsConfig.controllerType.getId();
|
||||
boolean axis = false;
|
||||
int buttonOffset = button * 15;
|
||||
switch (button) {
|
||||
case 15 -> buttonOffset = 0;
|
||||
case 16 -> buttonOffset = 18;
|
||||
case 17 -> buttonOffset = 36;
|
||||
case 18 -> buttonOffset = 54;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER -> buttonOffset = 7 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER -> buttonOffset = 8 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_BACK -> buttonOffset = 4 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_START -> buttonOffset = 6 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE -> buttonOffset = 5 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB -> buttonOffset = 15 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB -> buttonOffset = 16 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100 -> {
|
||||
buttonOffset = 0;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100 -> {
|
||||
buttonOffset = 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100 -> {
|
||||
buttonOffset = 2 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100 -> {
|
||||
buttonOffset = 3 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200 -> {
|
||||
buttonOffset = 4 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200 -> {
|
||||
buttonOffset = 5 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200 -> {
|
||||
buttonOffset = 6 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200 -> {
|
||||
buttonOffset = 7 * 18;
|
||||
axis = true;
|
||||
}
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 200 -> buttonOffset = 9 * 15;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200 -> buttonOffset = 10 * 15;
|
||||
}
|
||||
|
||||
RenderSystem.setShaderTexture(0, axis ? MidnightControlsClient.CONTROLLER_AXIS : button >= 15 && button <= 19 ? MidnightControlsClient.CONTROLLER_EXPANDED :MidnightControlsClient.CONTROLLER_BUTTONS);
|
||||
RenderSystem.disableDepthTest();
|
||||
|
||||
int assetSize = axis || (button >= 15 && button <= 18) ? AXIS_SIZE : BUTTON_SIZE;
|
||||
|
||||
RenderSystem.setShaderColor(1.f, second ? 0.f : 1.f, 1.f, 1.f);
|
||||
DrawableHelper.drawTexture(matrices, x + (ICON_SIZE / 2 - assetSize / 2), y + (ICON_SIZE / 2 - assetSize / 2),
|
||||
(float) buttonOffset, (float) (controllerType * assetSize),
|
||||
assetSize, assetSize,
|
||||
256, 256);
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
return ICON_SIZE;
|
||||
}
|
||||
|
||||
public static int drawButtonTip(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client) {
|
||||
return drawButtonTip(matrices, x, y, button.getButton(), button.getTranslationKey(), display, client);
|
||||
}
|
||||
|
||||
public static int drawButtonTip(MatrixStack matrices, int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client) {
|
||||
if (display) {
|
||||
int buttonWidth = drawButton(matrices, x, y, button, client).length();
|
||||
|
||||
var translatedAction = I18n.translate(action);
|
||||
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - client.textRenderer.fontHeight / 2) + 1;
|
||||
|
||||
return client.textRenderer.drawWithShadow(matrices, translatedAction, (float) (x + buttonWidth + 2), (float) (y + textY), 14737632);
|
||||
}
|
||||
|
||||
return -10;
|
||||
}
|
||||
|
||||
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) {
|
||||
return 15 + 5 + textRenderer.getWidth(action);
|
||||
}
|
||||
|
||||
public static void renderVirtualCursor(@NotNull MatrixStack matrices, @NotNull MinecraftClient client) {
|
||||
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|
||||
|| MidnightInput.isScreenInteractive(client.currentScreen)))
|
||||
return;
|
||||
|
||||
int mouseX = (int) (client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
|
||||
int mouseY = (int) (client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
|
||||
|
||||
boolean hoverSlot = false;
|
||||
|
||||
if (client.currentScreen instanceof HandledScreenAccessor inventoryScreen) {
|
||||
int guiLeft = inventoryScreen.getX();
|
||||
int guiTop = inventoryScreen.getY();
|
||||
|
||||
Slot slot = inventoryScreen.midnightcontrols$getSlotAt(mouseX, mouseY);
|
||||
|
||||
if (slot != null) {
|
||||
mouseX = guiLeft + slot.x;
|
||||
mouseY = guiTop + slot.y;
|
||||
hoverSlot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hoverSlot) {
|
||||
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, mouseX, mouseY);
|
||||
|
||||
if (slot != null) {
|
||||
mouseX = slot.x();
|
||||
mouseY = slot.y();
|
||||
hoverSlot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hoverSlot) {
|
||||
mouseX -= 8;
|
||||
mouseY -= 8;
|
||||
}
|
||||
|
||||
drawCursor(matrices, mouseX, mouseY, hoverSlot, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the virtual cursor.
|
||||
*
|
||||
* @param matrices the matrix stack
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param hoverSlot true if hovering a slot, else false
|
||||
* @param client the client instance
|
||||
*/
|
||||
public static void drawCursor(@NotNull MatrixStack matrices, int x, int y, boolean hoverSlot, @NotNull MinecraftClient client) {
|
||||
RenderSystem.disableDepthTest();
|
||||
|
||||
RenderSystem.setShaderColor(1.f, 1.f, 1.f, 1.f);
|
||||
RenderSystem.setShaderTexture(0, MidnightControlsClient.CURSOR_TEXTURE);
|
||||
DrawableHelper.drawTexture(matrices, x, y,
|
||||
hoverSlot ? 16.f : 0.f, MidnightControlsConfig.virtualMouseSkin.ordinal() * 16.f,
|
||||
16, 16, 32, 64);
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
public record ButtonSize(int length, int height) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dev.lambdaurora.spruceui.background.Background;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceWidget;
|
||||
import eu.midnightdust.lib.util.MidnightColorUtil;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.SpruceTexts;
|
||||
import dev.lambdaurora.spruceui.option.*;
|
||||
import dev.lambdaurora.spruceui.screen.SpruceScreen;
|
||||
import dev.lambdaurora.spruceui.widget.AbstractSpruceWidget;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceLabelWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceOptionListWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.tabbed.SpruceTabbedWidget;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.render.*;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.Matrix4f;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Represents the midnightcontrols settings screen.
|
||||
*/
|
||||
public class MidnightControlsSettingsScreen extends SpruceScreen {
|
||||
private static final Text SDL2_GAMEPAD_TOOL = Text.literal("SDL2 Gamepad Tool").formatted(Formatting.GREEN);
|
||||
public static final String GAMEPAD_TOOL_URL = "https://generalarcade.com/gamepadtool/";
|
||||
final MidnightControlsClient mod = MidnightControlsClient.get();
|
||||
private final Screen parent;
|
||||
// General options
|
||||
private final SpruceOption inputModeOption;
|
||||
private final SpruceOption autoSwitchModeOption;
|
||||
private final SpruceOption rotationSpeedOption;
|
||||
private final SpruceOption yAxisRotationSpeedOption;
|
||||
private final SpruceOption mouseSpeedOption;
|
||||
private final SpruceOption virtualMouseOption;
|
||||
private final SpruceOption resetOption;
|
||||
// Gameplay options
|
||||
private final SpruceOption analogMovementOption;
|
||||
private final SpruceOption doubleTapToSprintOption;
|
||||
private final SpruceOption autoJumpOption;
|
||||
private final SpruceOption fastBlockPlacingOption;
|
||||
private final SpruceOption frontBlockPlacingOption;
|
||||
private final SpruceOption verticalReacharoundOption;
|
||||
private final SpruceOption flyDriftingOption;
|
||||
private final SpruceOption flyVerticalDriftingOption;
|
||||
// Appearance options
|
||||
private final SpruceOption controllerTypeOption;
|
||||
private final SpruceOption virtualMouseSkinOption;
|
||||
private final SpruceOption hudEnableOption;
|
||||
private final SpruceOption hudSideOption;
|
||||
// Controller options
|
||||
private final SpruceOption controllerOption =
|
||||
new SpruceCyclingOption("midnightcontrols.menu.controller",
|
||||
amount -> {
|
||||
int id = MidnightControlsConfig.getController().id();
|
||||
id += amount;
|
||||
if (id > GLFW.GLFW_JOYSTICK_LAST)
|
||||
id = GLFW.GLFW_JOYSTICK_1;
|
||||
id = searchNextAvailableController(id, false);
|
||||
MidnightControlsConfig.setController(Controller.byId(id));
|
||||
},
|
||||
option -> {
|
||||
var controller = MidnightControlsConfig.getController();
|
||||
var controllerName = controller.getName();
|
||||
if (!controller.isConnected())
|
||||
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.RED));
|
||||
else if (!controller.isGamepad())
|
||||
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.GOLD));
|
||||
else
|
||||
return option.getDisplayText(Text.literal(controllerName));
|
||||
}, null);
|
||||
private final SpruceOption secondControllerOption = new SpruceCyclingOption("midnightcontrols.menu.controller2",
|
||||
amount -> {
|
||||
int id = MidnightControlsConfig.getSecondController().map(Controller::id).orElse(-1);
|
||||
id += amount;
|
||||
if (id > GLFW.GLFW_JOYSTICK_LAST)
|
||||
id = -1;
|
||||
id = searchNextAvailableController(id, true);
|
||||
MidnightControlsConfig.setSecondController(id == -1 ? null : Controller.byId(id));
|
||||
},
|
||||
option -> MidnightControlsConfig.getSecondController().map(controller -> {
|
||||
var controllerName = controller.getName();
|
||||
if (!controller.isConnected())
|
||||
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.RED));
|
||||
else if (!controller.isGamepad())
|
||||
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.GOLD));
|
||||
else
|
||||
return option.getDisplayText(Text.literal(controllerName));
|
||||
}).orElse(option.getDisplayText(SpruceTexts.OPTIONS_OFF.copyContentOnly().formatted(Formatting.RED))),
|
||||
Text.translatable("midnightcontrols.tooltip.controller2"));
|
||||
private final SpruceOption unfocusedInputOption;
|
||||
private final SpruceOption invertsRightXAxis;
|
||||
private final SpruceOption invertsRightYAxis;
|
||||
private final SpruceOption rightDeadZoneOption;
|
||||
private final SpruceOption leftDeadZoneOption;
|
||||
private final SpruceOption[] maxAnalogValueOptions = new SpruceOption[]{
|
||||
maxAnalogValueOption("midnightcontrols.menu.max_left_x_value", GLFW.GLFW_GAMEPAD_AXIS_LEFT_X),
|
||||
maxAnalogValueOption("midnightcontrols.menu.max_left_y_value", GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y),
|
||||
maxAnalogValueOption("midnightcontrols.menu.max_right_x_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X),
|
||||
maxAnalogValueOption("midnightcontrols.menu.max_right_y_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y)
|
||||
};
|
||||
|
||||
private static SpruceOption maxAnalogValueOption(String key, int axis) {
|
||||
return new SpruceDoubleOption(key, .25f, 1.f, 0.05f,
|
||||
() -> MidnightControlsConfig.getAxisMaxValue(axis),
|
||||
newValue -> MidnightControlsConfig.setAxisMaxValue(axis, newValue),
|
||||
option -> option.getDisplayText(Text.literal(String.format("%.2f", option.get()))),
|
||||
Text.translatable(key.replace("menu", "tooltip"))
|
||||
);
|
||||
}
|
||||
|
||||
private final MutableText controllerMappingsUrlText = Text.literal("(")
|
||||
.append(Text.literal(GAMEPAD_TOOL_URL).formatted(Formatting.GOLD))
|
||||
.append("),");
|
||||
|
||||
private static int searchNextAvailableController(int newId, boolean allowNone) {
|
||||
if ((allowNone && newId == -1) || newId == 0) return newId;
|
||||
|
||||
boolean connected = Controller.byId(newId).isConnected();
|
||||
if (!connected) {
|
||||
newId++;
|
||||
}
|
||||
|
||||
if (newId > GLFW.GLFW_JOYSTICK_LAST)
|
||||
newId = allowNone ? -1 : GLFW.GLFW_JOYSTICK_1;
|
||||
|
||||
return connected ? newId : searchNextAvailableController(newId, allowNone);
|
||||
}
|
||||
|
||||
public MidnightControlsSettingsScreen(Screen parent, boolean hideControls) {
|
||||
super(Text.translatable("midnightcontrols.title.settings"));
|
||||
MidnightControlsConfig.isEditing = true;
|
||||
this.parent = parent;
|
||||
// General options
|
||||
this.inputModeOption = new SpruceCyclingOption("midnightcontrols.menu.controls_mode",
|
||||
amount -> {
|
||||
var next = MidnightControlsConfig.controlsMode.next();
|
||||
MidnightControlsConfig.controlsMode = next;
|
||||
MidnightControlsConfig.save();
|
||||
|
||||
if (this.client.player != null) {
|
||||
ClientPlayNetworking.getSender().sendPacket(MidnightControls.CONTROLS_MODE_CHANNEL, this.mod.makeControlsModeBuffer(next));
|
||||
}
|
||||
}, option -> option.getDisplayText(Text.translatable(MidnightControlsConfig.controlsMode.getTranslationKey())),
|
||||
Text.translatable("midnightcontrols.tooltip.controls_mode"));
|
||||
this.autoSwitchModeOption = new SpruceToggleBooleanOption("midnightcontrols.menu.auto_switch_mode", () -> MidnightControlsConfig.autoSwitchMode,
|
||||
value -> MidnightControlsConfig.autoSwitchMode = value, Text.translatable("midnightcontrols.tooltip.auto_switch_mode"));
|
||||
this.rotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.rotation_speed", 0.0, 100.0, .5f,
|
||||
() -> MidnightControlsConfig.rotationSpeed,
|
||||
value -> MidnightControlsConfig.rotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||
Text.translatable("midnightcontrols.tooltip.rotation_speed"));
|
||||
this.yAxisRotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.y_axis_rotation_speed", 0.0, 100.0, .5f,
|
||||
() -> MidnightControlsConfig.yAxisRotationSpeed,
|
||||
value -> MidnightControlsConfig.yAxisRotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||
Text.translatable("midnightcontrols.tooltip.y_axis_rotation_speed"));
|
||||
this.mouseSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.mouse_speed", 0.0, 150.0, .5f,
|
||||
() -> MidnightControlsConfig.mouseSpeed,
|
||||
value -> MidnightControlsConfig.mouseSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||
Text.translatable("midnightcontrols.tooltip.mouse_speed"));
|
||||
this.resetOption = SpruceSimpleActionOption.reset(btn -> {
|
||||
// TODO
|
||||
MidnightControlsConfig.reset();
|
||||
var client = MinecraftClient.getInstance();
|
||||
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
|
||||
});
|
||||
// Gameplay options
|
||||
this.analogMovementOption = new SpruceToggleBooleanOption("midnightcontrols.menu.analog_movement",
|
||||
() -> MidnightControlsConfig.analogMovement, value -> MidnightControlsConfig.analogMovement = value,
|
||||
Text.translatable("midnightcontrols.tooltip.analog_movement"));
|
||||
this.doubleTapToSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.double_tap_to_sprint",
|
||||
() -> MidnightControlsConfig.doubleTapToSprint, value -> MidnightControlsConfig.doubleTapToSprint = value,
|
||||
Text.translatable("midnightcontrols.tooltip.double_tap_to_sprint"));
|
||||
this.autoJumpOption = new SpruceToggleBooleanOption("options.autoJump",
|
||||
() -> this.client.options.getAutoJump().getValue(),
|
||||
newValue -> this.client.options.getAutoJump().setValue(newValue),
|
||||
null);
|
||||
this.fastBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fast_block_placing", () -> MidnightControlsConfig.fastBlockPlacing,
|
||||
value -> MidnightControlsConfig.fastBlockPlacing = value, Text.translatable("midnightcontrols.tooltip.fast_block_placing"));
|
||||
this.frontBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.horizontal", () -> MidnightControlsConfig.horizontalReacharound,
|
||||
value -> MidnightControlsConfig.horizontalReacharound = value, Text.translatable("midnightcontrols.tooltip.reacharound.horizontal"));
|
||||
this.verticalReacharoundOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.vertical", () -> MidnightControlsConfig.verticalReacharound,
|
||||
value -> MidnightControlsConfig.verticalReacharound = value, Text.translatable("midnightcontrols.tooltip.reacharound.vertical"));
|
||||
this.flyDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting", () -> MidnightControlsConfig.flyDrifting,
|
||||
value -> MidnightControlsConfig.flyDrifting = value, Text.translatable("midnightcontrols.tooltip.fly_drifting"));
|
||||
this.flyVerticalDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting_vertical", () -> MidnightControlsConfig.verticalFlyDrifting,
|
||||
value -> MidnightControlsConfig.verticalFlyDrifting = value, Text.translatable("midnightcontrols.tooltip.fly_drifting_vertical"));
|
||||
// Appearance options
|
||||
this.controllerTypeOption = new SpruceCyclingOption("midnightcontrols.menu.controller_type",
|
||||
amount -> MidnightControlsConfig.controllerType = MidnightControlsConfig.controllerType.next(),
|
||||
option -> option.getDisplayText(MidnightControlsConfig.controllerType.getTranslatedText()),
|
||||
Text.translatable("midnightcontrols.tooltip.controller_type"));
|
||||
this.virtualMouseSkinOption = new SpruceCyclingOption("midnightcontrols.menu.virtual_mouse.skin",
|
||||
amount -> MidnightControlsConfig.virtualMouseSkin = MidnightControlsConfig.virtualMouseSkin.next(),
|
||||
option -> option.getDisplayText(MidnightControlsConfig.virtualMouseSkin.getTranslatedText()),
|
||||
null);
|
||||
this.hudEnableOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hud_enable", () -> MidnightControlsConfig.hudEnable,
|
||||
this.mod::setHudEnabled, Text.translatable("midnightcontrols.tooltip.hud_enable"));
|
||||
this.hudSideOption = new SpruceCyclingOption("midnightcontrols.menu.hud_side",
|
||||
amount -> MidnightControlsConfig.hudSide = MidnightControlsConfig.hudSide.next(),
|
||||
option -> option.getDisplayText(MidnightControlsConfig.hudSide.getTranslatedText()),
|
||||
Text.translatable("midnightcontrols.tooltip.hud_side"));
|
||||
// Controller options
|
||||
this.rightDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.right_dead_zone", 0.05, 1.0, .05f,
|
||||
() -> MidnightControlsConfig.rightDeadZone,
|
||||
value -> MidnightControlsConfig.rightDeadZone = value, option -> {
|
||||
var value = String.valueOf(option.get());
|
||||
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
|
||||
}, Text.translatable("midnightcontrols.tooltip.right_dead_zone"));
|
||||
this.leftDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.left_dead_zone", 0.05, 1.0, .05f,
|
||||
() -> MidnightControlsConfig.leftDeadZone,
|
||||
value -> MidnightControlsConfig.leftDeadZone = value, option -> {
|
||||
var value = String.valueOf(option.get());
|
||||
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
|
||||
}, Text.translatable("midnightcontrols.tooltip.left_dead_zone"));
|
||||
this.invertsRightXAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_x_axis", () -> MidnightControlsConfig.invertRightXAxis,
|
||||
value -> MidnightControlsConfig.invertRightXAxis = value, null);
|
||||
this.invertsRightYAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_y_axis", () -> MidnightControlsConfig.invertRightYAxis,
|
||||
value -> MidnightControlsConfig.invertRightYAxis = value, null);
|
||||
this.unfocusedInputOption = new SpruceToggleBooleanOption("midnightcontrols.menu.unfocused_input", () -> MidnightControlsConfig.unfocusedInput,
|
||||
value -> MidnightControlsConfig.unfocusedInput = value, Text.translatable("midnightcontrols.tooltip.unfocused_input"));
|
||||
this.virtualMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.virtual_mouse", () -> MidnightControlsConfig.virtualMouse,
|
||||
value -> MidnightControlsConfig.virtualMouse = value, Text.translatable("midnightcontrols.tooltip.virtual_mouse"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
MidnightControlsConfig.isEditing = false;
|
||||
MidnightControlsConfig.save();
|
||||
super.removed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
MidnightControlsConfig.isEditing = false;
|
||||
MidnightControlsConfig.save();
|
||||
super.close();
|
||||
}
|
||||
|
||||
private int getTextHeight() {
|
||||
return (5 + this.textRenderer.fontHeight) * 3 + 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
|
||||
this.buildTabs();
|
||||
|
||||
this.addDrawableChild(this.resetOption.createWidget(Position.of(this.width / 2 - 155, this.height - 29), 150));
|
||||
this.addDrawableChild(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, 20, SpruceTexts.GUI_DONE,
|
||||
btn -> this.client.setScreen(this.parent)));
|
||||
}
|
||||
|
||||
public void buildTabs() {
|
||||
var tabs = new SpruceTabbedWidget(Position.of(0, 24), this.width, this.height - 32 - 24,
|
||||
null,
|
||||
Math.max(116, this.width / 8), 0);
|
||||
tabs.getList().setBackground(new MidnightControlsBackground());
|
||||
this.addDrawableChild(tabs);
|
||||
|
||||
tabs.addSeparatorEntry(Text.translatable("midnightcontrols.menu.separator.general"));
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.general"), null,
|
||||
this::buildGeneralTab);
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.gameplay"), null,
|
||||
this::buildGameplayTab);
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.visual"), null,
|
||||
this::buildVisualTab);
|
||||
|
||||
tabs.addSeparatorEntry(Text.translatable("options.controls"));
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.controller_controls"), null,
|
||||
this::buildControllerControlsTab);
|
||||
|
||||
tabs.addSeparatorEntry(Text.translatable("midnightcontrols.menu.separator.controller"));
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.controller"), null,
|
||||
this::buildControllerTab);
|
||||
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.mappings.string"), null,
|
||||
this::buildMappingsStringEditorTab);
|
||||
}
|
||||
|
||||
public SpruceOptionListWidget buildGeneralTab(int width, int height) {
|
||||
var list = new SpruceOptionListWidget(Position.origin(), width, height);
|
||||
list.setBackground(new MidnightControlsBackground(130));
|
||||
list.addSingleOptionEntry(this.inputModeOption);
|
||||
list.addSingleOptionEntry(this.autoSwitchModeOption);
|
||||
list.addSingleOptionEntry(this.rotationSpeedOption);
|
||||
list.addSingleOptionEntry(this.yAxisRotationSpeedOption);
|
||||
list.addSingleOptionEntry(this.mouseSpeedOption);
|
||||
list.addSingleOptionEntry(this.virtualMouseOption);
|
||||
return list;
|
||||
}
|
||||
|
||||
public SpruceOptionListWidget buildGameplayTab(int width, int height) {
|
||||
var list = new SpruceOptionListWidget(Position.origin(), width, height);
|
||||
list.setBackground(new MidnightControlsBackground(130));
|
||||
list.addSingleOptionEntry(this.analogMovementOption);
|
||||
list.addSingleOptionEntry(this.doubleTapToSprintOption);
|
||||
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.fastBlockPlacingOption);
|
||||
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.frontBlockPlacingOption);
|
||||
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.verticalReacharoundOption);
|
||||
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.flyDriftingOption);
|
||||
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.flyVerticalDriftingOption);
|
||||
list.addSingleOptionEntry(this.autoJumpOption);
|
||||
return list;
|
||||
}
|
||||
|
||||
public SpruceOptionListWidget buildVisualTab(int width, int height) {
|
||||
var list = new SpruceOptionListWidget(Position.origin(), width, height);
|
||||
list.setBackground(new MidnightControlsBackground(130));
|
||||
list.addSingleOptionEntry(this.controllerTypeOption);
|
||||
list.addSingleOptionEntry(this.virtualMouseSkinOption);
|
||||
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
|
||||
list.addSingleOptionEntry(this.hudEnableOption);
|
||||
list.addSingleOptionEntry(this.hudSideOption);
|
||||
return list;
|
||||
}
|
||||
|
||||
public ControllerControlsWidget buildControllerControlsTab(int width, int height) {
|
||||
return new ControllerControlsWidget(Position.origin(), width, height);
|
||||
}
|
||||
|
||||
public AbstractSpruceWidget buildControllerTab(int width, int height) {
|
||||
var root = new SpruceContainerWidget(Position.origin(), width, height);
|
||||
|
||||
var aboutMappings1 = new SpruceLabelWidget(Position.of(0, 2),
|
||||
Text.translatable("midnightcontrols.controller.mappings.1", SDL2_GAMEPAD_TOOL),
|
||||
width, true);
|
||||
|
||||
var gamepadToolUrlLabel = new SpruceLabelWidget(Position.of(0, aboutMappings1.getHeight() + 4),
|
||||
this.controllerMappingsUrlText, width,
|
||||
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
|
||||
gamepadToolUrlLabel.setTooltip(Text.translatable("chat.link.open"));
|
||||
|
||||
var aboutMappings3 = new SpruceLabelWidget(Position.of(0,
|
||||
aboutMappings1.getHeight() + gamepadToolUrlLabel.getHeight() + 6),
|
||||
Text.translatable("midnightcontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()),
|
||||
width, true);
|
||||
|
||||
int listHeight = height - 8 - aboutMappings1.getHeight() - aboutMappings3.getHeight() - gamepadToolUrlLabel.getHeight();
|
||||
var labels = new SpruceContainerWidget(Position.of(0,
|
||||
listHeight),
|
||||
width, height - listHeight);
|
||||
labels.addChild(aboutMappings1);
|
||||
labels.addChild(gamepadToolUrlLabel);
|
||||
labels.addChild(aboutMappings3);
|
||||
|
||||
var list = new SpruceOptionListWidget(Position.origin(), width, listHeight);
|
||||
list.setBackground(new MidnightControlsBackground(130));
|
||||
list.addSingleOptionEntry(this.controllerOption);
|
||||
list.addSingleOptionEntry(this.secondControllerOption);
|
||||
list.addSingleOptionEntry(this.unfocusedInputOption);
|
||||
list.addOptionEntry(this.invertsRightXAxis, this.invertsRightYAxis);
|
||||
list.addSingleOptionEntry(this.rightDeadZoneOption);
|
||||
list.addSingleOptionEntry(this.leftDeadZoneOption);
|
||||
for (var option : this.maxAnalogValueOptions) {
|
||||
list.addSingleOptionEntry(option);
|
||||
}
|
||||
|
||||
root.addChild(list);
|
||||
root.addChild(labels);
|
||||
return root;
|
||||
}
|
||||
|
||||
public SpruceContainerWidget buildMappingsStringEditorTab(int width, int height) {
|
||||
return new MappingsStringInputWidget(Position.origin(), width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTitle(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
drawCenteredText(matrices, this.textRenderer, I18n.translate("midnightcontrols.menu.title"), this.width / 2, 8, 16777215);
|
||||
}
|
||||
|
||||
public static class MidnightControlsBackground implements Background {
|
||||
private int transparency = 160;
|
||||
public MidnightControlsBackground() {}
|
||||
public MidnightControlsBackground(int transparency) {
|
||||
this.transparency = transparency;
|
||||
}
|
||||
@Override
|
||||
public void render(MatrixStack matrixStack, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) {
|
||||
fill(matrixStack, widget.getX(), widget.getY(), widget.getX() + widget.getWidth(), widget.getY() + widget.getHeight(), MidnightColorUtil.hex2Rgb("#000000"));
|
||||
}
|
||||
private void fill(MatrixStack matrixStack, int x2, int y2, int x1, int y1, Color color) {
|
||||
matrixStack.push();
|
||||
|
||||
Matrix4f matrix = matrixStack.peek().getPositionMatrix();
|
||||
float r = (float)(color.getRed()) / 255.0F;
|
||||
float g = (float)(color.getGreen()) / 255.0F;
|
||||
float b = (float)(color.getBlue()) / 255.0F;
|
||||
float t = (float)(transparency) / 255.0F;
|
||||
BufferBuilder bufferBuilder = Tessellator.getInstance().getBuffer();
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.disableTexture();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.setShader(GameRenderer::getPositionColorShader);
|
||||
bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR);
|
||||
bufferBuilder.vertex(matrix, (float)x1, (float)y2, 0.0F).color(r, g, b, t).next();
|
||||
bufferBuilder.vertex(matrix, (float)x2, (float)y2, 0.0F).color(r, g, b, t).next();
|
||||
bufferBuilder.vertex(matrix, (float)x2, (float)y1, 0.0F).color(r, g, b, t).next();
|
||||
bufferBuilder.vertex(matrix, (float)x1, (float)y1, 0.0F).color(r, g, b, t).next();
|
||||
BufferRenderer.drawWithShader(bufferBuilder.end());
|
||||
RenderSystem.enableTexture();
|
||||
RenderSystem.disableBlend();
|
||||
matrixStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||
import dev.lambdaurora.spruceui.option.SpruceSimpleActionOption;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.text.Text;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents the option to reload the controller mappings.
|
||||
*/
|
||||
public class ReloadControllerMappingsOption {
|
||||
private static final String KEY = "midnightcontrols.menu.reload_controller_mappings";
|
||||
|
||||
public static SpruceSimpleActionOption newOption(@Nullable Consumer<SpruceButtonWidget> before) {
|
||||
return SpruceSimpleActionOption.of(KEY, btn -> {
|
||||
var client = MinecraftClient.getInstance();
|
||||
if (before != null)
|
||||
before.accept(btn);
|
||||
Controller.updateMappings();
|
||||
if (client.currentScreen instanceof MidnightControlsSettingsScreen)
|
||||
client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
|
||||
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.TUTORIAL_HINT,
|
||||
Text.translatable("midnightcontrols.controller.mappings.updated"), Text.empty()));
|
||||
}, Text.translatable("midnightcontrols.tooltip.reload_controller_mappings"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
/**
|
||||
* Represents the controls ring screen.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.4.3
|
||||
* @since 1.4.3
|
||||
*/
|
||||
public class RingScreen extends Screen {
|
||||
protected final MidnightControlsClient mod;
|
||||
|
||||
public RingScreen() {
|
||||
super(Text.literal("midnightcontrols.menu.title.ring"));
|
||||
this.mod = MidnightControlsClient.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
|
||||
RingPage page = this.mod.ring.getCurrentPage();
|
||||
|
||||
page.render(matrices, this.textRenderer, this.width, this.height, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
if (mod.ring.getCurrentPage().onClick(client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight(), (int) mouseX, (int) mouseY)) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui;
|
||||
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
|
||||
import eu.midnightdust.midnightcontrols.client.HudSide;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.widget.SilentTexturedButtonWidget;
|
||||
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||
import net.minecraft.client.gui.screen.ChatScreen;
|
||||
import net.minecraft.client.gui.screen.GameMenuScreen;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
|
||||
import net.minecraft.client.gui.widget.TexturedButtonWidget;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Arm;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
|
||||
|
||||
/**
|
||||
* Represents the touchscreen overlay
|
||||
*/
|
||||
public class TouchscreenOverlay extends Screen {
|
||||
public static final Identifier WIDGETS_LOCATION = new Identifier("midnightcontrols", "textures/gui/widgets.png");
|
||||
private MidnightControlsClient mod;
|
||||
private SilentTexturedButtonWidget jumpButton;
|
||||
private SilentTexturedButtonWidget flyButton;
|
||||
private SilentTexturedButtonWidget flyUpButton;
|
||||
private SilentTexturedButtonWidget flyDownButton;
|
||||
private int flyButtonEnableTicks = 0;
|
||||
private int forwardButtonTick = 0;
|
||||
private SilentTexturedButtonWidget forwardLeftButton;
|
||||
private SilentTexturedButtonWidget forwardRightButton;
|
||||
private SilentTexturedButtonWidget startSneakButton;
|
||||
private SilentTexturedButtonWidget endSneakButton;
|
||||
|
||||
public TouchscreenOverlay(@NotNull MidnightControlsClient mod) {
|
||||
super(Text.literal("Touchscreen overlay"));
|
||||
this.mod = mod;
|
||||
this.passEvents = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
super.keyPressed(keyCode,scanCode,modifiers);
|
||||
//return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void pauseGame(boolean bl) {
|
||||
if (this.client == null)
|
||||
return;
|
||||
boolean bl2 = this.client.isIntegratedServerRunning() && !this.client.getServer().isRemote();
|
||||
if (bl2) {
|
||||
this.client.setScreen(new GameMenuScreen(!bl));
|
||||
this.client.getSoundManager().pauseAll();
|
||||
} else {
|
||||
this.client.setScreen(new GameMenuScreen(true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the forward button ticks cooldown.
|
||||
*
|
||||
* @param state The button state.
|
||||
*
|
||||
*/
|
||||
private void updateForwardButtonsState(boolean state) {
|
||||
if (state)
|
||||
this.forwardButtonTick = -1;
|
||||
else
|
||||
this.forwardButtonTick = 20;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the jump buttons.
|
||||
*/
|
||||
private void updateJumpButtons() {
|
||||
if (this.client == null)
|
||||
return;
|
||||
if (!this.client.interactionManager.isFlyingLocked()) {
|
||||
boolean oldStateFly = this.flyButton.isVisible();
|
||||
this.jumpButton.setVisible(false);
|
||||
this.flyButton.setVisible(true);
|
||||
this.flyUpButton.setVisible(true);
|
||||
this.flyDownButton.setVisible(true);
|
||||
if (oldStateFly != this.flyButton.isVisible()) {
|
||||
this.flyButtonEnableTicks = 5;
|
||||
this.setJump(false);
|
||||
} else if (this.flyButtonEnableTicks > 0)
|
||||
this.flyButtonEnableTicks--;
|
||||
} else {
|
||||
this.jumpButton.setVisible(true);
|
||||
this.flyButton.setVisible(false);
|
||||
this.flyUpButton.setVisible(false);
|
||||
this.flyDownButton.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the jump button.
|
||||
*
|
||||
* @param btn The pressed button.
|
||||
*/
|
||||
private void handleJump(SpruceButtonWidget btn) {
|
||||
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
}
|
||||
/**
|
||||
* Handles the jump button.
|
||||
*
|
||||
* @param state The state.
|
||||
*/
|
||||
private void setJump(boolean state) {
|
||||
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.forwardButtonTick > 0) {
|
||||
this.forwardButtonTick--;
|
||||
} else if (this.forwardButtonTick == 0) {
|
||||
if (this.forwardLeftButton.isVisible())
|
||||
this.forwardLeftButton.setVisible(false);
|
||||
if (this.forwardRightButton.isVisible())
|
||||
this.forwardRightButton.setVisible(false);
|
||||
}
|
||||
this.updateJumpButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
int scaledWidth = this.client.getWindow().getScaledWidth();
|
||||
int scaledHeight = this.client.getWindow().getScaledHeight();
|
||||
this.addDrawableChild(new TexturedButtonWidget(scaledWidth / 2 - 20, 0, 20, 20, 0, 106, 20, new Identifier("textures/gui/widgets.png"), 256, 256,
|
||||
btn -> this.client.setScreen(new ChatScreen("")), Text.empty()));
|
||||
this.addDrawableChild(new TexturedButtonWidget(scaledWidth / 2, 0, 20, 20, 0, 0, 20, WIDGETS_LOCATION, 256, 256,
|
||||
btn -> this.pauseGame(false)));
|
||||
// Inventory buttons.
|
||||
int inventoryButtonX = scaledWidth / 2;
|
||||
int inventoryButtonY = scaledHeight - 16 - 5;
|
||||
if (this.client.options.getMainArm().getValue() == Arm.LEFT) {
|
||||
inventoryButtonX = inventoryButtonX - 91 - 24;
|
||||
} else {
|
||||
inventoryButtonX = inventoryButtonX + 91 + 4;
|
||||
}
|
||||
this.addDrawableChild(new TexturedButtonWidget(inventoryButtonX, inventoryButtonY, 20, 20, 20, 0, 20, WIDGETS_LOCATION, 256, 256,
|
||||
btn -> {
|
||||
if (this.client.interactionManager.hasRidingInventory()) {
|
||||
this.client.player.openRidingInventory();
|
||||
} else {
|
||||
this.client.getTutorialManager().onInventoryOpened();
|
||||
this.client.setScreen(new InventoryScreen(this.client.player));
|
||||
}
|
||||
}));
|
||||
int jumpButtonX, swapHandsX, sneakButtonX;
|
||||
int sneakButtonY = scaledHeight - 10 - 40 - 5;
|
||||
if (MidnightControlsConfig.hudSide == HudSide.LEFT) {
|
||||
jumpButtonX = scaledWidth - 20 - 20;
|
||||
swapHandsX = jumpButtonX - 5 - 40;
|
||||
sneakButtonX = 10 + 20 + 5;
|
||||
} else {
|
||||
jumpButtonX = 20;
|
||||
swapHandsX = jumpButtonX + 5 + 40;
|
||||
sneakButtonX = scaledWidth - 10 - 40 - 5;
|
||||
}
|
||||
// Swap items hand.
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY), 20, 20, Text.empty(),
|
||||
button -> {
|
||||
if (button.isActive()) {
|
||||
if (!this.client.player.isSpectator()) {
|
||||
this.client.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN));
|
||||
}
|
||||
}
|
||||
},0, 160, 20, WIDGETS_LOCATION));
|
||||
// Drop
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY + 5 + 20), 20, 20, Text.empty(), btn ->
|
||||
((KeyBindingAccessor) this.client.options.dropKey).midnightcontrols$handlePressState(btn.isActive()), 0, 160, 20, WIDGETS_LOCATION));
|
||||
// Jump keys
|
||||
this.addDrawableChild(this.jumpButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY), 20, 20, Text.empty(), this::handleJump, 0, 40, 20, WIDGETS_LOCATION));
|
||||
this.addDrawableChild(this.flyButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY), 20, 20, Text.empty(),btn -> {
|
||||
if (this.flyButtonEnableTicks == 0) this.client.player.getAbilities().flying = false;
|
||||
}, 20, 40, 20, WIDGETS_LOCATION)
|
||||
);
|
||||
this.addDrawableChild(this.flyUpButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY - 5 - 20), 20, 20,Text.empty(),
|
||||
this::handleJump, 40, 40, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.addDrawableChild(this.flyDownButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
|
||||
btn -> ((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(btn.isActive()), 60, 40, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.updateJumpButtons();
|
||||
// Movements keys
|
||||
this.addDrawableChild((this.startSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
|
||||
if (btn.isActive()) {
|
||||
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(true);
|
||||
this.startSneakButton.setVisible(false);
|
||||
this.endSneakButton.setVisible(true);
|
||||
}
|
||||
}, 0, 120, 20, WIDGETS_LOCATION))
|
||||
);
|
||||
this.addDrawableChild((this.endSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
|
||||
if (btn.isActive()) {
|
||||
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(false);
|
||||
this.endSneakButton.setVisible(false);
|
||||
this.startSneakButton.setVisible(true);
|
||||
}
|
||||
}, 20, 120, 20, WIDGETS_LOCATION)));
|
||||
this.endSneakButton.setVisible(false);
|
||||
this.addDrawableChild(this.forwardLeftButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
|
||||
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
this.updateForwardButtonsState(btn.isActive());
|
||||
}, 80, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.forwardLeftButton.setVisible(false);
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
|
||||
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
this.updateForwardButtonsState(btn.isActive());
|
||||
this.forwardLeftButton.setVisible(true);
|
||||
this.forwardRightButton.setVisible(true);
|
||||
}, 0, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.addDrawableChild(this.forwardRightButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
|
||||
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive());
|
||||
this.updateForwardButtonsState(btn.isActive());
|
||||
}, 100, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.forwardRightButton.setVisible(true);
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY), 20, 20, Text.empty(),
|
||||
btn -> ((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive()), 20, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
|
||||
btn -> ((KeyBindingAccessor) this.client.options.backKey).midnightcontrols$handlePressState(btn.isActive()), 40, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY), 20, 20, Text.empty(),
|
||||
btn -> ((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive()), 60, 80, 20, WIDGETS_LOCATION
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (mouseY >= (double) (this.height - 22) && this.client != null && this.client.player != null) {
|
||||
int centerX = this.width / 2;
|
||||
if (mouseX >= (double) (centerX - 90) && mouseX <= (double) (centerX + 90)) {
|
||||
for (int slot = 0; slot < 9; ++slot) {
|
||||
int slotX = centerX - 90 + slot * 20 + 2;
|
||||
if (mouseX >= (double) slotX && mouseX <= (double) (slotX + 20)) {
|
||||
this.client.player.getInventory().selectedSlot = slot;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
if (button == GLFW.GLFW_MOUSE_BUTTON_1 && this.client != null) {
|
||||
if (deltaY > 0.01)
|
||||
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(deltaY / 5.0), 2);
|
||||
else if (deltaY < 0.01)
|
||||
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(deltaY / 5.0), 1);
|
||||
|
||||
if (deltaX > 0.01)
|
||||
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(deltaX / 5.0), 2);
|
||||
else if (deltaX < 0.01)
|
||||
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(deltaX / 5.0), 1);
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui.widget;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.SpruceTexts;
|
||||
import dev.lambdaurora.spruceui.widget.AbstractSpruceIconButtonWidget;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a controller button widget.
|
||||
*/
|
||||
public class ControllerButtonWidget extends AbstractSpruceIconButtonWidget {
|
||||
private ButtonBinding binding;
|
||||
private int iconWidth;
|
||||
|
||||
public ControllerButtonWidget(Position position, int width, @NotNull ButtonBinding binding, @NotNull PressAction action) {
|
||||
super(position, width, 20, ButtonBinding.getLocalizedButtonName(binding.getButton()[0]), action);
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
int length = binding.getButton().length;
|
||||
this.setMessage(this.binding.isNotBound() ? SpruceTexts.NOT_BOUND.copy() :
|
||||
(length > 0 ? ButtonBinding.getLocalizedButtonName(binding.getButton()[0]) : Text.literal("<>")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getMessage() {
|
||||
if (this.binding.getButton().length > 1)
|
||||
return Text.empty();
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int renderIcon(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
int x = this.getX();
|
||||
if (this.binding.getButton().length > 1) {
|
||||
x += (this.width / 2 - this.iconWidth / 2) - 4;
|
||||
}
|
||||
var size = MidnightControlsRenderer.drawButton(matrices, x, this.getY(), this.binding, MinecraftClient.getInstance());
|
||||
this.iconWidth = size.length();
|
||||
return size.height();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui.widget;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.SpruceTexts;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
|
||||
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.function.Predicates;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents the controls screen.
|
||||
*/
|
||||
public class ControllerControlsWidget extends SpruceContainerWidget {
|
||||
final MidnightControlsClient mod;
|
||||
private ControlsListWidget bindingsListWidget;
|
||||
private SpruceButtonWidget resetButton;
|
||||
public ButtonBinding focusedBinding;
|
||||
public boolean waiting = false;
|
||||
public List<Integer> currentButtons = new ArrayList<>();
|
||||
|
||||
public ControllerControlsWidget(Position position, int width, int height) {
|
||||
super(position, width, height);
|
||||
this.mod = MidnightControlsClient.get();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
this.addChild(new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, 18), 310, 20,
|
||||
Text.translatable("midnightcontrols.menu.keyboard_controls"),
|
||||
btn -> this.client.setScreen(new ControlsOptionsScreen(null, this.client.options))));
|
||||
this.bindingsListWidget = new ControlsListWidget(Position.of(this, 0, 43), this.width, this.height - 43 - 35, this);
|
||||
this.bindingsListWidget.setBackground(new MidnightControlsSettingsScreen.MidnightControlsBackground(130));
|
||||
this.addChild(this.bindingsListWidget);
|
||||
this.addChild(this.resetButton = new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150, 20,
|
||||
SpruceTexts.CONTROLS_RESET_ALL,
|
||||
btn -> InputManager.streamBindings().collect(Collectors.toSet()).forEach(binding -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWidget(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
drawCenteredText(matrices, this.client.textRenderer, Text.translatable("midnightcontrols.menu.title.controller_controls"),
|
||||
this.getX() + this.width / 2, this.getY() + 4, 16777215);
|
||||
this.resetButton.setActive(InputManager.streamBindings().anyMatch(Predicates.not(ButtonBinding::isDefault)));
|
||||
super.renderWidget(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
public void finishBindingEdit(int... buttons) {
|
||||
if (this.focusedBinding == null) return;
|
||||
MidnightControlsConfig.setButtonBinding(this.focusedBinding, buttons);
|
||||
this.focusedBinding = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.gui.widget;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.SpruceTexts;
|
||||
import dev.lambdaurora.spruceui.navigation.NavigationDirection;
|
||||
import dev.lambdaurora.spruceui.navigation.NavigationUtils;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceButtonWidget;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceSeparatorWidget;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceEntryListWidget;
|
||||
import dev.lambdaurora.spruceui.widget.container.SpruceParentWidget;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a control list widget.
|
||||
*/
|
||||
public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget.Entry> {
|
||||
private static final int[] UNBOUND = new int[]{-1};
|
||||
private final ControllerControlsWidget gui;
|
||||
protected int lastIndex = 0;
|
||||
private final int maxTextLength;
|
||||
|
||||
public ControlsListWidget(Position position, int width, int height, ControllerControlsWidget gui) {
|
||||
super(position, width, height, 4, ControlsListWidget.Entry.class);
|
||||
this.gui = gui;
|
||||
this.maxTextLength = InputManager.streamBindings().mapToInt(binding -> this.client.textRenderer.getWidth(binding.getText())).max().orElse(0);
|
||||
|
||||
InputManager.streamCategories()
|
||||
.sorted(Comparator.comparingInt(ButtonCategory::getPriority))
|
||||
.forEach(category -> {
|
||||
this.addEntry(new CategoryEntry(this, category));
|
||||
|
||||
category.getBindings().forEach(binding -> {
|
||||
this.addEntry(new ControlsListWidget.ButtonBindingEntry(this, binding));
|
||||
});
|
||||
});
|
||||
|
||||
this.setAllowOutsideHorizontalNavigation(true);
|
||||
}
|
||||
|
||||
private int getRowWidth() {
|
||||
return this.getWidth() - 6 - this.getRowLeft() * 2;
|
||||
}
|
||||
|
||||
public int getRowLeft() {
|
||||
int baseWidth = 220 + 32;
|
||||
return this.getWidth() / 2 - baseWidth / 2 + 72 - this.maxTextLength;
|
||||
}
|
||||
|
||||
public class ButtonBindingEntry extends Entry implements SpruceParentWidget<SpruceWidget> {
|
||||
private final List<SpruceWidget> children = new ArrayList<>();
|
||||
private @Nullable SpruceWidget focused;
|
||||
private final ButtonBinding binding;
|
||||
private final String bindingName;
|
||||
private final ControllerButtonWidget editButton;
|
||||
private final SpruceButtonWidget resetButton;
|
||||
private final SpruceButtonWidget unbindButton;
|
||||
|
||||
ButtonBindingEntry(@NotNull ControlsListWidget parent, @NotNull ButtonBinding binding) {
|
||||
super(parent);
|
||||
this.binding = binding;
|
||||
this.bindingName = I18n.translate(this.binding.getTranslationKey());
|
||||
this.editButton = new ControllerButtonWidget(Position.of(this, parent.getWidth() / 2 - 8, 0), 110, this.binding, btn -> {
|
||||
gui.focusedBinding = binding;
|
||||
MidnightControlsClient.get().input.beginControlsInput(gui);
|
||||
}) {
|
||||
protected Text getNarrationMessage() {
|
||||
return binding.isNotBound() ? Text.translatable("narrator.controls.unbound", bindingName)
|
||||
: Text.translatable("narrator.controls.bound", bindingName, super.getNarrationMessage());
|
||||
}
|
||||
};
|
||||
this.children.add(editButton);
|
||||
this.resetButton = new SpruceButtonWidget(Position.of(this,
|
||||
this.editButton.getPosition().getRelativeX() + this.editButton.getWidth() + 2, 0),
|
||||
44, 20, Text.translatable("controls.reset"),
|
||||
btn -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton())) {
|
||||
protected Text getNarrationMessage() {
|
||||
return Text.translatable("narrator.controls.reset", bindingName);
|
||||
}
|
||||
};
|
||||
this.children.add(this.resetButton);
|
||||
this.unbindButton = new SpruceButtonWidget(Position.of(this,
|
||||
this.editButton.getPosition().getRelativeX() + this.editButton.getWidth() + 2, 0),
|
||||
this.resetButton.getWidth(), this.resetButton.getHeight(), SpruceTexts.GUI_UNBIND,
|
||||
btn -> {
|
||||
MidnightControlsConfig.setButtonBinding(binding, UNBOUND);
|
||||
gui.focusedBinding = null;
|
||||
MidnightControlsClient.get().input.beginControlsInput(null);
|
||||
}) {
|
||||
protected Text getNarrationMessage() {
|
||||
return Text.translatable("midnightcontrols.narrator.unbound", bindingName);
|
||||
}
|
||||
};
|
||||
this.children.add(this.unbindButton);
|
||||
|
||||
this.position.setRelativeX(4);
|
||||
this.width -= 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SpruceWidget> children() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SpruceWidget getFocused() {
|
||||
return this.focused;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocused(@Nullable SpruceWidget focused) {
|
||||
if (this.focused == focused)
|
||||
return;
|
||||
if (this.focused != null)
|
||||
this.focused.setFocused(false);
|
||||
this.focused = focused;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.children.stream().mapToInt(SpruceWidget::getHeight).reduce(Integer::max).orElse(0) + 4;
|
||||
}
|
||||
|
||||
/* Input */
|
||||
|
||||
@Override
|
||||
protected boolean onMouseClick(double mouseX, double mouseY, int button) {
|
||||
var it = this.children().iterator();
|
||||
|
||||
SpruceWidget element;
|
||||
do {
|
||||
if (!it.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element = it.next();
|
||||
} while (!element.mouseClicked(mouseX, mouseY, button));
|
||||
|
||||
this.setFocused(element);
|
||||
if (button == GLFW.GLFW_MOUSE_BUTTON_1)
|
||||
this.dragging = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMouseRelease(double mouseX, double mouseY, int button) {
|
||||
this.dragging = false;
|
||||
return this.hoveredElement(mouseX, mouseY).filter(element -> element.mouseReleased(mouseX, mouseY, button)).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMouseDrag(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
return this.getFocused() != null && this.dragging && button == GLFW.GLFW_MOUSE_BUTTON_1
|
||||
&& this.getFocused().mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onKeyPress(int keyCode, int scanCode, int modifiers) {
|
||||
return this.focused != null && this.focused.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
|
||||
@Override
|
||||
public void setFocused(boolean focused) {
|
||||
super.setFocused(focused);
|
||||
if (!focused) {
|
||||
this.setFocused(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
|
||||
if (this.requiresCursor()) return false;
|
||||
if (!tab && direction.isVertical()) {
|
||||
if (this.isFocused()) {
|
||||
this.setFocused(null);
|
||||
return false;
|
||||
}
|
||||
int lastIndex = this.parent.lastIndex;
|
||||
if (lastIndex >= this.children.size())
|
||||
lastIndex = this.children.size() - 1;
|
||||
if (!this.children.get(lastIndex).onNavigation(direction, tab))
|
||||
return false;
|
||||
this.setFocused(this.children.get(lastIndex));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean result = NavigationUtils.tryNavigate(direction, tab, this.children, this.focused, this::setFocused, true);
|
||||
if (result) {
|
||||
this.setFocused(true);
|
||||
if (direction.isHorizontal() && this.getFocused() != null) {
|
||||
this.parent.lastIndex = this.children.indexOf(this.getFocused());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Rendering */
|
||||
|
||||
@Override
|
||||
protected void renderWidget(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
boolean focused = gui.focusedBinding == this.binding;
|
||||
|
||||
var textRenderer = ControlsListWidget.this.client.textRenderer;
|
||||
int height = this.getHeight();
|
||||
//float textX = (float) (this.getX() + 70 - ControlsListWidget.this.maxTextLength);
|
||||
int textY = this.getY() + height / 2;
|
||||
textRenderer.draw(matrices, this.bindingName, this.getX(), (float) (textY - 9 / 2), 16777215);
|
||||
|
||||
this.resetButton.setVisible(!focused);
|
||||
this.unbindButton.setVisible(focused);
|
||||
this.resetButton.setActive(!this.binding.isDefault());
|
||||
|
||||
this.editButton.update();
|
||||
if (focused) {
|
||||
var text = Text.literal("> ").formatted(Formatting.WHITE);
|
||||
text.append(this.editButton.getMessage().copy().formatted(Formatting.YELLOW));
|
||||
this.editButton.setMessage(text.append(Text.literal(" <").formatted(Formatting.WHITE)));
|
||||
} else if (!this.binding.isNotBound() && InputManager.hasDuplicatedBindings(this.binding)) {
|
||||
var text = this.editButton.getMessage().copy();
|
||||
this.editButton.setMessage(text.formatted(Formatting.RED));
|
||||
} else if (this.binding.isNotBound()) {
|
||||
var text = this.editButton.getMessage().copy();
|
||||
this.editButton.setMessage(text.formatted(Formatting.GOLD));
|
||||
}
|
||||
|
||||
this.children.forEach(widget -> widget.render(matrices, mouseX, mouseY, delta));
|
||||
}
|
||||
}
|
||||
|
||||
public static class CategoryEntry extends Entry {
|
||||
private final SpruceSeparatorWidget separatorWidget;
|
||||
|
||||
protected CategoryEntry(ControlsListWidget parent, ButtonCategory category) {
|
||||
super(parent);
|
||||
this.separatorWidget = new SpruceSeparatorWidget(Position.of(this, 2, 0), this.getWidth() - 4,
|
||||
Text.literal(category.getTranslatedName())) {
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return CategoryEntry.this.getWidth() - 4;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public SpruceSeparatorWidget getSeparatorWidget() {
|
||||
return this.separatorWidget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.separatorWidget.getHeight() + 4;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
|
||||
@Override
|
||||
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
|
||||
return this.separatorWidget.onNavigation(direction, tab);
|
||||
}
|
||||
|
||||
/* Rendering */
|
||||
|
||||
@Override
|
||||
protected void renderWidget(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
this.separatorWidget.render(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SpruceTabbedWidget$SeparatorEntry{" +
|
||||
"position=" + this.getPosition() +
|
||||
", width=" + this.getWidth() +
|
||||
", height=" + this.getHeight() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public abstract static class Entry extends SpruceEntryListWidget.Entry {
|
||||
protected final ControlsListWidget parent;
|
||||
|
||||
protected Entry(ControlsListWidget parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return this.parent.getInnerWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.midnightdust.midnightcontrols.client.gui.widget;
|
||||
|
||||
import dev.lambdaurora.spruceui.Position;
|
||||
import dev.lambdaurora.spruceui.widget.SpruceTexturedButtonWidget;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SilentTexturedButtonWidget extends SpruceTexturedButtonWidget {
|
||||
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) {
|
||||
super(position, width, height, message, action, u, v, hoveredVOffset, texture);
|
||||
}
|
||||
|
||||
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) {
|
||||
super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture);
|
||||
}
|
||||
|
||||
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) {
|
||||
super(position, width, height, message, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight);
|
||||
}
|
||||
|
||||
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) {
|
||||
super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight);
|
||||
}
|
||||
@Override
|
||||
public void playDownSound() {}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.advancement.Advancement;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||
import net.minecraft.client.network.ClientAdvancementManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents an accessor of {@link AdvancementsScreen}.
|
||||
*/
|
||||
@Mixin(AdvancementsScreen.class)
|
||||
public interface AdvancementsScreenAccessor {
|
||||
@Accessor("advancementHandler")
|
||||
ClientAdvancementManager getAdvancementManager();
|
||||
|
||||
@Accessor("tabs")
|
||||
Map<Advancement, AdvancementTab> getTabs();
|
||||
|
||||
@Accessor("selectedTab")
|
||||
AdvancementTab getSelectedTab();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ClickableWidget.class)
|
||||
public interface ClickableWidgetAccessor {
|
||||
@Accessor("height")
|
||||
int getHeight();
|
||||
@Accessor("focused")
|
||||
void setFocused(boolean value);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.MovementHandler;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.input.Input;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.MovementType;
|
||||
import net.minecraft.network.encryption.PlayerPublicKey;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Injects the anti fly drifting feature.
|
||||
*/
|
||||
@Mixin(ClientPlayerEntity.class)
|
||||
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity {
|
||||
private boolean midnightcontrols$driftingPrevented = false;
|
||||
|
||||
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile, @Nullable PlayerPublicKey publicKey) {
|
||||
super(world, profile, publicKey);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean hasMovementInput();
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
protected MinecraftClient client;
|
||||
|
||||
@Shadow
|
||||
public Input input;
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean isCamera();
|
||||
|
||||
@Shadow protected int ticksLeftToDoubleTapSprint;
|
||||
|
||||
|
||||
@Inject(method = "move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V"))
|
||||
public void onMove(MovementType type, Vec3d movement, CallbackInfo ci) {
|
||||
if (!MidnightControlsConfig.doubleTapToSprint) ticksLeftToDoubleTapSprint = 0;
|
||||
if (!MidnightControls.isExtrasLoaded) return;
|
||||
if (type == MovementType.SELF) {
|
||||
if (this.getAbilities().flying && (!MidnightControlsConfig.flyDrifting || !MidnightControlsConfig.verticalFlyDrifting)) {
|
||||
if (!this.hasMovementInput()) {
|
||||
if (!this.midnightcontrols$driftingPrevented) {
|
||||
if (!MidnightControlsConfig.flyDrifting)
|
||||
this.setVelocity(this.getVelocity().multiply(0, 1.0, 0));
|
||||
}
|
||||
this.midnightcontrols$driftingPrevented = true;
|
||||
} else
|
||||
this.midnightcontrols$driftingPrevented = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(ZF)V", shift = At.Shift.AFTER))
|
||||
public void onInputUpdate(CallbackInfo ci) {
|
||||
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
|
||||
public void onTickMovement(CallbackInfo ci) {
|
||||
if (this.getAbilities().flying && this.isCamera()) {
|
||||
if (MidnightControlsConfig.verticalFlyDrifting || !MidnightControls.isExtrasLoaded)
|
||||
return;
|
||||
int moving = 0;
|
||||
if (this.input.sneaking) {
|
||||
--moving;
|
||||
}
|
||||
|
||||
if (this.input.jumping) {
|
||||
++moving;
|
||||
}
|
||||
|
||||
if (moving == 0) {
|
||||
this.setVelocity(this.getVelocity().multiply(1.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.lib.util.screen.TexturedOverlayButtonWidget;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
|
||||
import net.minecraft.client.gui.screen.option.GameOptionsScreen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Injects the new controls settings button.
|
||||
*/
|
||||
@Mixin(ControlsOptionsScreen.class)
|
||||
public abstract class ControlsOptionsScreenMixin extends GameOptionsScreen {
|
||||
@Unique private final boolean showAlternativeButton = FabricLoader.getInstance().isModLoaded("crawl");
|
||||
public ControlsOptionsScreenMixin(Screen parent, GameOptions gameOptions, Text text) {
|
||||
super(parent, gameOptions, text);
|
||||
}
|
||||
@Inject(method = "init", at = @At(value = "INVOKE", ordinal = 1, shift = At.Shift.AFTER, target = "Lnet/minecraft/client/gui/screen/option/ControlsOptionsScreen;addDrawableChild(Lnet/minecraft/client/gui/Element;)Lnet/minecraft/client/gui/Element;"))
|
||||
private void addAlternativeControllerButton(CallbackInfo ci) {
|
||||
if (showAlternativeButton) {
|
||||
this.addDrawableChild(new TexturedOverlayButtonWidget(this.width / 2 + 158, this.height / 6 - 12, 20, 20,0,0,20, new Identifier("midnightcontrols", "textures/gui/midnightcontrols_button.png"), 32, 64, (button) -> {
|
||||
this.client.setScreen(new MidnightControlsSettingsScreen(this, false));
|
||||
}, Text.translatable("midnightcontrols.menu.title.controller")));
|
||||
}
|
||||
}
|
||||
@Inject(method = "init", at = @At(value = "INVOKE", ordinal = 4, shift = At.Shift.AFTER, target = "Lnet/minecraft/client/gui/screen/option/ControlsOptionsScreen;addDrawableChild(Lnet/minecraft/client/gui/Element;)Lnet/minecraft/client/gui/Element;"))
|
||||
private void addControllerButton(CallbackInfo ci) {
|
||||
if (!showAlternativeButton) {
|
||||
int i = this.width / 2 - 155;
|
||||
int j = i + 160;
|
||||
int k = this.height / 6 - 12 + 48;
|
||||
this.addDrawableChild(new ButtonWidget(j, k, 150, 20, Text.translatable("midnightcontrols.menu.title.controller").append("..."), (button) -> {
|
||||
this.client.setScreen(new MidnightControlsSettingsScreen(this, false));
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
/**
|
||||
* Represents an accessor to CreativeInventoryScreen.
|
||||
*/
|
||||
@Mixin(CreativeInventoryScreen.class)
|
||||
public interface CreativeInventoryScreenAccessor {
|
||||
/**
|
||||
* Gets the selected tab.
|
||||
*
|
||||
* @return the selected tab index
|
||||
*/
|
||||
@Accessor("selectedTab")
|
||||
static int getSelectedTab() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected tab.
|
||||
*
|
||||
* @param group the tab's item group
|
||||
*/
|
||||
@Invoker("setSelectedTab")
|
||||
void midnightcontrols$setSelectedTab(@NotNull ItemGroup group);
|
||||
|
||||
/**
|
||||
* Returns whether the slot belongs to the creative inventory or not.
|
||||
*
|
||||
* @param slot the slot to check
|
||||
* @return true if the slot is from the creative inventory, else false
|
||||
*/
|
||||
@Invoker("isCreativeInventorySlot")
|
||||
boolean midnightcontrols$isCreativeInventorySlot(@Nullable Slot slot);
|
||||
|
||||
/**
|
||||
* Returns whether the current tab has a scrollbar or not.
|
||||
*
|
||||
* @return true if the current tab has a scrollbar, else false
|
||||
*/
|
||||
@Invoker("hasScrollbar")
|
||||
boolean midnightcontrols$hasScrollbar();
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(EntryListWidget.class)
|
||||
public interface EntryListWidgetAccessor
|
||||
{
|
||||
public interface EntryListWidgetAccessor {
|
||||
@Invoker("moveSelection")
|
||||
void move_selection(int amount);
|
||||
void midnightcontrols$moveSelection(EntryListWidget.MoveDirection direction);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.minecraft.client.option.SimpleOption;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Represents a mixin to GameOptions.
|
||||
* <p>
|
||||
* Sets the default of the Auto-Jump option to false.
|
||||
*/
|
||||
@Mixin(GameOptions.class)
|
||||
public abstract class GameOptionsMixin {
|
||||
|
||||
@Shadow public abstract SimpleOption<Boolean> getAutoJump();
|
||||
|
||||
@Inject(method = "load", at = @At("HEAD"))
|
||||
public void onInit(CallbackInfo ci) {
|
||||
// Set default value of the Auto-Jump option to false.
|
||||
getAutoJump().setValue(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getX()D"))
|
||||
private void onRender(float tickDelta, long startTime, boolean fullRender, CallbackInfo ci) {
|
||||
if (this.client.currentScreen != null && MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER)
|
||||
MidnightControlsClient.get().input.onPreRenderScreen(this.client, this.client.currentScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
|
||||
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
import net.minecraft.text.Text;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Represents the mixin for the class ContainerScreen.
|
||||
*/
|
||||
@Mixin(HandledScreen.class)
|
||||
public abstract class HandledScreenMixin implements HandledScreenAccessor {
|
||||
@Unique private static float scale = 1f;
|
||||
|
||||
@Accessor("x")
|
||||
public abstract int getX();
|
||||
|
||||
@Accessor("y")
|
||||
public abstract int getY();
|
||||
|
||||
@Invoker("getSlotAt")
|
||||
public abstract Slot midnightcontrols$getSlotAt(double posX, double posY);
|
||||
|
||||
@Invoker("isClickOutsideBounds")
|
||||
public abstract boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
|
||||
|
||||
|
||||
@Invoker("onMouseClick")
|
||||
public abstract void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType);
|
||||
|
||||
@Inject(method = "render", at = @At("RETURN"))
|
||||
public void onRender(MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
|
||||
var client = MinecraftClient.getInstance();
|
||||
if (client.options.getGuiScale().getValue() >= 4) {
|
||||
scale = 0.75f;
|
||||
} else scale = 1f;
|
||||
matrices.push();
|
||||
if (scale != 1f) matrices.scale(scale,scale,scale);
|
||||
int x = 2, y = (int) (client.getWindow().getScaledHeight() * (1 / scale) - 2 - MidnightControlsRenderer.ICON_SIZE);
|
||||
if (MidnightControlsCompat.isEMIPresent()) {
|
||||
x += 40 * (1 / scale);
|
||||
}
|
||||
x = MidnightControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, "midnightcontrols.action.pickup_all", true, client) + 2;
|
||||
x = MidnightControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, "midnightcontrols.action.exit", true, client) + 2;
|
||||
if (MidnightControlsCompat.isReiPresent()) {
|
||||
x = 2;
|
||||
y -= 24;
|
||||
}
|
||||
if (MidnightControlsCompat.isEMIPresent()) {
|
||||
x = (int) (client.getWindow().getScaledWidth() * (1 / scale) - 55 - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.pickup"))
|
||||
* (1 / scale) - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.quick_move"))
|
||||
- MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.TAKE) - MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.QUICK_MOVE));
|
||||
}
|
||||
x = MidnightControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, "midnightcontrols.action.pickup", true, client);
|
||||
MidnightControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, "midnightcontrols.action.quick_move", true, client);
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(KeyBinding.class)
|
||||
public class KeyBindingMixin implements KeyBindingAccessor {
|
||||
@Shadow
|
||||
private int timesPressed;
|
||||
|
||||
@Shadow
|
||||
private boolean pressed;
|
||||
|
||||
@Override
|
||||
public boolean midnightcontrols$press() {
|
||||
boolean oldPressed = this.pressed;
|
||||
if (!this.pressed)
|
||||
this.pressed = true;
|
||||
++this.timesPressed;
|
||||
return !oldPressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean midnightcontrols$unpress() {
|
||||
if (this.pressed) {
|
||||
this.pressed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(value = KeyBindingRegistryImpl.class, remap = false)
|
||||
public interface KeyBindingRegistryImplAccessor {
|
||||
@Accessor @Final
|
||||
static List<KeyBinding> getModdedKeyBindings() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public abstract class MinecraftClientMixin {
|
||||
@Shadow
|
||||
@Nullable
|
||||
public HitResult crosshairTarget;
|
||||
|
||||
@Shadow
|
||||
@Nullable
|
||||
public ClientPlayerEntity player;
|
||||
|
||||
@Shadow
|
||||
@Nullable
|
||||
public ClientPlayerInteractionManager interactionManager;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
public GameRenderer gameRenderer;
|
||||
|
||||
@Shadow
|
||||
private int itemUseCooldown;
|
||||
|
||||
private BlockPos midnightcontrols$lastTargetPos;
|
||||
private Vec3d midnightcontrols$lastPos;
|
||||
private Direction midnightcontrols$lastTargetSide;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onInit(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onMcInit((MinecraftClient) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void onStartTick(CallbackInfo ci) {
|
||||
if (this.player == null)
|
||||
return;
|
||||
|
||||
if (!MidnightControlsFeature.FAST_BLOCK_PLACING.isAvailable())
|
||||
return;
|
||||
if (this.midnightcontrols$lastPos == null)
|
||||
this.midnightcontrols$lastPos = this.player.getPos();
|
||||
|
||||
int cooldown = this.itemUseCooldown;
|
||||
BlockHitResult hitResult;
|
||||
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.BLOCK && this.player.getAbilities().flying) {
|
||||
hitResult = (BlockHitResult) this.crosshairTarget;
|
||||
var targetPos = hitResult.getBlockPos();
|
||||
var side = hitResult.getSide();
|
||||
|
||||
boolean sidewaysBlockPlacing = this.midnightcontrols$lastTargetPos == null || !targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide));
|
||||
boolean backwardsBlockPlacing = this.player.input.movementForward < 0.0f && (this.midnightcontrols$lastTargetPos == null || targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide)));
|
||||
|
||||
if (cooldown > 1
|
||||
&& !targetPos.equals(this.midnightcontrols$lastTargetPos)
|
||||
&& (sidewaysBlockPlacing || backwardsBlockPlacing)) {
|
||||
this.itemUseCooldown = 1;
|
||||
}
|
||||
|
||||
this.midnightcontrols$lastTargetPos = targetPos.toImmutable();
|
||||
this.midnightcontrols$lastTargetSide = side;
|
||||
}
|
||||
// Removed front placing sprinting as way too cheaty.
|
||||
else if (this.player.isSprinting()) {
|
||||
hitResult = MidnightControlsClient.get().reacharound.getLastReacharoundResult();
|
||||
if (hitResult != null) {
|
||||
if (cooldown > 0)
|
||||
this.itemUseCooldown = 0;
|
||||
}
|
||||
}
|
||||
this.midnightcontrols$lastPos = this.player.getPos();
|
||||
}
|
||||
// Applied multiple times for smooth camera turning even on low FPS
|
||||
@Inject(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;startTick()V", shift = At.Shift.BEFORE))
|
||||
private void onPrePreRender(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onRender((MinecraftClient) (Object) (this));
|
||||
}
|
||||
@Inject(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Recorder;startTick()V", shift = At.Shift.AFTER))
|
||||
private void onPreRender(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onRender((MinecraftClient) (Object) (this));
|
||||
}
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void onRender(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onRender((MinecraftClient) (Object) (this));
|
||||
}
|
||||
@Inject(method = "render", at = @At("TAIL"))
|
||||
private void onPostRender(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onRender((MinecraftClient) (Object) (this));
|
||||
}
|
||||
@Inject(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;endTick()V", shift = At.Shift.AFTER))
|
||||
private void onPostPostRender(CallbackInfo ci) {
|
||||
MidnightControlsClient.get().onRender((MinecraftClient) (Object) (this));
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;render(FJZ)V", shift = At.Shift.AFTER))
|
||||
private void renderVirtualCursor(boolean fullRender, CallbackInfo ci) {
|
||||
MidnightControlsRenderer.renderVirtualCursor(new MatrixStack(), (MinecraftClient) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "doItemUse()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
|
||||
private void onItemUse(CallbackInfo ci, Hand[] hands, int handCount, int handIndex, Hand hand, ItemStack stackInHand) {
|
||||
var mod = MidnightControlsClient.get();
|
||||
if (!stackInHand.isEmpty() && this.player.getPitch(0.f) > 35.0F && mod.reacharound.isReacharoundAvailable()) {
|
||||
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.MISS && this.player.isOnGround()) {
|
||||
if (!stackInHand.isEmpty() && stackInHand.getItem() instanceof BlockItem) {
|
||||
var hitResult = mod.reacharound.getLastReacharoundResult();
|
||||
|
||||
if (hitResult == null)
|
||||
return;
|
||||
|
||||
hitResult = mod.reacharound.withSideForReacharound(hitResult, stackInHand);
|
||||
|
||||
int previousStackCount = stackInHand.getCount();
|
||||
var result = this.interactionManager.interactBlock(this.player, hand, hitResult);
|
||||
if (result.isAccepted()) {
|
||||
if (result.shouldSwingHand()) {
|
||||
this.player.swingHand(hand);
|
||||
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || this.interactionManager.hasCreativeInventory())) {
|
||||
this.gameRenderer.firstPersonRenderer.resetEquipProgress(hand);
|
||||
}
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
if (result == ActionResult.FAIL) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import eu.midnightdust.midnightcontrols.client.util.MouseAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.Mouse;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Adds extra access to the mouse.
|
||||
*/
|
||||
@Mixin(Mouse.class)
|
||||
public abstract class MouseMixin implements MouseAccessor {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Accessor
|
||||
public abstract void setLeftButtonClicked(boolean value);
|
||||
|
||||
@Invoker("onCursorPos")
|
||||
public abstract void midnightcontrols$onCursorPos(long window, double x, double y);
|
||||
|
||||
@Inject(method = "onMouseButton", at = @At(value = "TAIL"))
|
||||
private void onMouseBackButton(long window, int button, int action, int mods, CallbackInfo ci) {
|
||||
if (action == 1 && button == GLFW.GLFW_MOUSE_BUTTON_4 && MinecraftClient.getInstance().currentScreen != null) {
|
||||
if (MidnightControlsClient.get().input.tryGoBack(MinecraftClient.getInstance().currentScreen)) {
|
||||
action = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "isCursorLocked", at = @At("HEAD"), cancellable = true)
|
||||
private void isCursorLocked(CallbackInfoReturnable<Boolean> ci) {
|
||||
if (this.client.currentScreen == null) {
|
||||
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.virtualMouse) {
|
||||
ci.setReturnValue(true);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "lockCursor", at = @At("HEAD"), cancellable = true)
|
||||
private void onCursorLocked(CallbackInfo ci) {
|
||||
if (/*config.getControlsMode() == ControlsMode.TOUCHSCREEN
|
||||
||*/ (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.virtualMouse))
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(RecipeBookWidget.class)
|
||||
public interface RecipeBookWidgetAccessor {
|
||||
@Accessor("tabButtons")
|
||||
List<RecipeGroupButtonWidget> getTabButtons();
|
||||
|
||||
@Accessor("currentTab")
|
||||
RecipeGroupButtonWidget getCurrentTab();
|
||||
|
||||
@Accessor("currentTab")
|
||||
void setCurrentTab(RecipeGroupButtonWidget currentTab);
|
||||
|
||||
@Invoker("refreshResults")
|
||||
void midnightcontrols$refreshResults(boolean resetCurrentPage);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import net.minecraft.client.gui.Selectable;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(Screen.class)
|
||||
public interface ScreenAccessor {
|
||||
@Accessor
|
||||
List<Selectable> getSelectables();
|
||||
@Accessor @Nullable
|
||||
Selectable getSelected();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.*;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.math.Matrix4f;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Represents a mixin to WorldRenderer.
|
||||
* <p>
|
||||
* Handles the rendering of the block outline of the reach-around features.
|
||||
*/
|
||||
@Mixin(WorldRenderer.class)
|
||||
public abstract class WorldRendererMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Shadow
|
||||
private ClientWorld world;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BufferBuilderStorage bufferBuilders;
|
||||
|
||||
@Shadow
|
||||
private static void drawShapeOutline(MatrixStack matrixStack, VertexConsumer vertexConsumer, VoxelShape voxelShape, double d, double e, double f, float g, float h, float i, float j) {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "FIELD",
|
||||
target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;",
|
||||
ordinal = 1,
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void onOutlineRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer,
|
||||
LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
|
||||
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !MidnightControlsConfig.shouldRenderReacharoundOutline)
|
||||
return;
|
||||
var result = MidnightControlsClient.get().reacharound.getLastReacharoundResult();
|
||||
if (result == null)
|
||||
return;
|
||||
var blockPos = result.getBlockPos();
|
||||
if (this.world.getWorldBorder().contains(blockPos)) {
|
||||
var stack = this.client.player.getStackInHand(Hand.MAIN_HAND);
|
||||
if (stack == null || !(stack.getItem() instanceof BlockItem))
|
||||
return;
|
||||
|
||||
var mod = MidnightControlsClient.get();
|
||||
|
||||
var block = ((BlockItem) stack.getItem()).getBlock();
|
||||
result = mod.reacharound.withSideForReacharound(result, block);
|
||||
var context = new ItemPlacementContext(new ItemUsageContext(this.client.player, Hand.MAIN_HAND, result));
|
||||
|
||||
var placementState = block.getPlacementState(context);
|
||||
if (placementState == null)
|
||||
return;
|
||||
var pos = camera.getPos();
|
||||
|
||||
var outlineShape = placementState.getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
|
||||
int[] color = MidnightControlsConfig.reacharoundOutlineColor;
|
||||
|
||||
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
|
||||
drawShapeOutline(matrices, vertexConsumer, outlineShape,
|
||||
(double) blockPos.getX() - pos.getX(), (double) blockPos.getY() - pos.getY(), (double) blockPos.getZ() - pos.getZ(),
|
||||
color[0] / 255.f, color[1] / 255.f, color[2] / 255.f, color[3] / 255.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DummyRingAction extends RingAction {
|
||||
public DummyRingAction(@NotNull Config config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAction(@NotNull RingButtonMode mode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(@NotNull MatrixStack matrices, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) {
|
||||
drawCenteredText(matrices, textRenderer, this.getName(), x + 25, y + 25 - textRenderer.fontHeight / 2, 0xffffff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class KeyBindingRingAction extends RingAction {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
public final KeyBinding binding;
|
||||
|
||||
public KeyBindingRingAction(@NotNull Config config, @NotNull KeyBinding binding) {
|
||||
super(config);
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.binding.getTranslationKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAction(@NotNull RingButtonMode mode) {
|
||||
KeyBindingAccessor accessor = (KeyBindingAccessor) this.binding;
|
||||
switch (mode) {
|
||||
case PRESS, HOLD -> accessor.midnightcontrols$handlePressState(this.activated);
|
||||
case TOGGLE -> {
|
||||
accessor.midnightcontrols$handlePressState(!this.binding.isPressed());
|
||||
this.activated = !this.binding.isPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(@NotNull MatrixStack matrices, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) {
|
||||
drawCenteredText(matrices, textRenderer, Text.translatable(this.getName()), x + 25, y + 25 - textRenderer.fontHeight / 2, 0xffffff);
|
||||
}
|
||||
|
||||
protected static class Factory implements RingAction.Factory {
|
||||
@Override
|
||||
public @NotNull Supplier<RingAction> newFromGui(@NotNull Screen screen) {
|
||||
return () -> null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RingAction parse(@NotNull Config config) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a key binding ring.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.7.0
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class MidnightRing {
|
||||
public static final int ELEMENT_SIZE = 50;
|
||||
|
||||
private final Map<String, RingAction.Factory> actionFactories = new Object2ObjectOpenHashMap<>();
|
||||
private final List<RingPage> pages = new ArrayList<>(Collections.singletonList(RingPage.DEFAULT));
|
||||
private final MidnightControlsClient mod;
|
||||
private int currentPage = 0;
|
||||
|
||||
public MidnightRing(@NotNull MidnightControlsClient mod) {
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
public void registerAction(@NotNull String name, @NotNull RingAction.Factory factory) {
|
||||
if (this.actionFactories.containsKey(name)) {
|
||||
this.mod.warn("Tried to register twice a ring action: \"" + name + "\".");
|
||||
return;
|
||||
}
|
||||
this.actionFactories.put(name, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the ring from configuration.
|
||||
*/
|
||||
public void load() {
|
||||
List<Config> configPages = null;
|
||||
if (configPages != null) {
|
||||
this.pages.clear();
|
||||
for (var configPage : configPages) {
|
||||
RingPage.parseRingPage(configPage).ifPresent(this.pages::add);
|
||||
}
|
||||
}
|
||||
if (this.pages.isEmpty()) {
|
||||
this.pages.add(RingPage.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull RingPage getCurrentPage() {
|
||||
if (this.currentPage >= this.pages.size())
|
||||
this.currentPage = this.pages.size() - 1;
|
||||
else if (this.currentPage < 0)
|
||||
this.currentPage = 0;
|
||||
return this.pages.get(this.currentPage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a ring action.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class RingAction extends DrawableHelper implements Nameable {
|
||||
protected Config config;
|
||||
protected boolean activated = false;
|
||||
|
||||
public RingAction(@NotNull Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text name of the ring action.
|
||||
*
|
||||
* @return the text name
|
||||
*/
|
||||
public Text getTextName() {
|
||||
return Text.translatable(this.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the action is activated or not.
|
||||
*
|
||||
* @return true if the action is activated, else false
|
||||
*/
|
||||
public boolean isActivated() {
|
||||
return this.activated;
|
||||
}
|
||||
|
||||
public void activate(@NotNull RingButtonMode mode) {
|
||||
this.activated = !this.activated;
|
||||
|
||||
this.onAction(mode);
|
||||
}
|
||||
|
||||
public abstract void onAction(@NotNull RingButtonMode mode);
|
||||
|
||||
public void render(@NotNull MatrixStack matrices, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) {
|
||||
fill(matrices, x, y, x + MidnightRing.ELEMENT_SIZE, y + MidnightRing.ELEMENT_SIZE, hovered ? 0xbb777777 : 0xbb000000);
|
||||
drawIcon(matrices, textRenderer, x, y, hovered);
|
||||
}
|
||||
|
||||
public abstract void drawIcon(@NotNull MatrixStack matrices, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered);
|
||||
|
||||
/**
|
||||
* Represents a factory for {@link RingAction}.
|
||||
*
|
||||
* @version 1.4.3
|
||||
* @since 1.4.3
|
||||
*/
|
||||
public interface Factory {
|
||||
@NotNull Supplier<RingAction> newFromGui(@NotNull Screen screen);
|
||||
|
||||
@Nullable RingAction parse(@NotNull Config config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents the mode of a ring button.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.4.0
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public enum RingButtonMode implements Nameable {
|
||||
PRESS("press"),
|
||||
HOLD("hold"),
|
||||
TOGGLE("toggle");
|
||||
|
||||
private final String name;
|
||||
private final Text text;
|
||||
|
||||
RingButtonMode(@NotNull String name) {
|
||||
this.name = name;
|
||||
this.text = Text.translatable(this.getTranslationKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next ring button mode available.
|
||||
*
|
||||
* @return the next ring button mode
|
||||
*/
|
||||
public @NotNull RingButtonMode next() {
|
||||
var v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation key of this ring button mode.
|
||||
*
|
||||
* @return the translation key of this ring button mode
|
||||
*/
|
||||
public @NotNull String getTranslationKey() {
|
||||
return "midnightcontrols.ring.button_mode." + this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this ring button mode.
|
||||
*
|
||||
* @return the translated name of this ring button mode
|
||||
*/
|
||||
public @NotNull Text getTranslatedText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.ring;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.gui.screen.ChatScreen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a ring page.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.5.0
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class RingPage extends DrawableHelper {
|
||||
public static final RingPage DEFAULT = new RingPage("Default");
|
||||
|
||||
public final String name;
|
||||
private RingAction[] actions = new RingAction[8];
|
||||
|
||||
public RingPage(@NotNull String name) {
|
||||
this.name = name;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
this.actions[i] = null;
|
||||
}
|
||||
this.actions[0] = new DummyRingAction(null);
|
||||
this.actions[1] = new KeyBindingRingAction(null, MidnightControlsClient.BINDING_LOOK_UP);
|
||||
this.actions[2] = new KeyBindingRingAction(null, MidnightControlsClient.BINDING_LOOK_LEFT);
|
||||
this.actions[3] = new KeyBindingRingAction(null, MidnightControlsClient.BINDING_LOOK_RIGHT);
|
||||
this.actions[4] = new KeyBindingRingAction(null, MidnightControlsClient.BINDING_LOOK_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the ring page.
|
||||
*
|
||||
* @param matrices the matrices
|
||||
* @param width the screen width
|
||||
* @param height the screen height
|
||||
* @param mouseX the mouse X-coordinate
|
||||
* @param mouseY the mouse Y-coordinate
|
||||
* @param tickDelta the tick delta
|
||||
*/
|
||||
public void render(@NotNull MatrixStack matrices, @NotNull TextRenderer textRenderer, int width, int height, int mouseX, int mouseY, float tickDelta) {
|
||||
int centerX = width / 2;
|
||||
int centerY = height / 2;
|
||||
|
||||
int offset = MidnightRing.ELEMENT_SIZE + (MidnightRing.ELEMENT_SIZE / 2) + 5;
|
||||
|
||||
int y = centerY - offset;
|
||||
int x = centerX - offset;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null)
|
||||
ringAction.render(matrices, textRenderer, x, y, isHovered(x, y, mouseX, mouseY));
|
||||
x += 55;
|
||||
}
|
||||
y += 55;
|
||||
x = centerX - offset;
|
||||
for (int i = 3; i < 5; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null)
|
||||
ringAction.render(matrices, textRenderer, x, y, isHovered(x, y, mouseX, mouseY));
|
||||
x += 55 * 2;
|
||||
}
|
||||
y += 55;
|
||||
x = centerX - offset;
|
||||
for (int i = 5; i < 8; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null)
|
||||
ringAction.render(matrices, textRenderer, x, y, isHovered(x, y, mouseX, mouseY));
|
||||
x += 55;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isHovered(int x, int y, int mouseX, int mouseY) {
|
||||
return mouseX >= x && mouseY >= y && mouseX <= x + MidnightRing.ELEMENT_SIZE && mouseY <= y + MidnightRing.ELEMENT_SIZE;
|
||||
}
|
||||
/**
|
||||
* Renders the ring page.
|
||||
*
|
||||
* @param width the screen width
|
||||
* @param height the screen height
|
||||
* @param mouseX the mouse X-coordinate
|
||||
* @param mouseY the mouse Y-coordinate
|
||||
*/
|
||||
public boolean onClick(int width, int height, int mouseX, int mouseY) {
|
||||
int centerX = width / 2;
|
||||
int centerY = height / 2;
|
||||
|
||||
int offset = MidnightRing.ELEMENT_SIZE + (MidnightRing.ELEMENT_SIZE / 2) + 5;
|
||||
|
||||
int y = centerY - offset;
|
||||
int x = centerX - offset;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
|
||||
ringAction.activate(RingButtonMode.PRESS);
|
||||
return true;
|
||||
}
|
||||
x += 55;
|
||||
}
|
||||
y += 55;
|
||||
x = centerX - offset;
|
||||
for (int i = 3; i < 5; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
|
||||
ringAction.activate(RingButtonMode.PRESS);
|
||||
return true;
|
||||
}
|
||||
x += 55 * 2;
|
||||
}
|
||||
y += 55;
|
||||
x = centerX - offset;
|
||||
for (int i = 5; i < 8; i++) {
|
||||
var ringAction = this.actions[i];
|
||||
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
|
||||
ringAction.activate(RingButtonMode.PRESS);
|
||||
return true;
|
||||
}
|
||||
x += 55;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a ring page configuration.
|
||||
*
|
||||
* @param config the configuration
|
||||
* @return an optional ring page
|
||||
*/
|
||||
public static @NotNull Optional<RingPage> parseRingPage(@NotNull Config config) {
|
||||
String name = config.get("name");
|
||||
if (name == null)
|
||||
return Optional.empty();
|
||||
|
||||
var page = new RingPage(name);
|
||||
|
||||
List<Config> actionConfigs = config.get("actions");
|
||||
|
||||
|
||||
return Optional.of(page);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.util;
|
||||
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an accessor to AbstractContainerScreen.
|
||||
*/
|
||||
public interface HandledScreenAccessor {
|
||||
/**
|
||||
* Gets the left coordinate of the GUI.
|
||||
*
|
||||
* @return the left coordinate of the GUI
|
||||
*/
|
||||
int getX();
|
||||
|
||||
/**
|
||||
* Gets the top coordinate of the GUI.
|
||||
*
|
||||
* @return the top coordinate of the GUI
|
||||
*/
|
||||
int getY();
|
||||
|
||||
/**
|
||||
* Gets the slot at position.
|
||||
*
|
||||
* @param posX the X position to check
|
||||
* @param posY the Y position to check
|
||||
* @return the slot at the specified position
|
||||
*/
|
||||
Slot midnightcontrols$getSlotAt(double posX, double posY);
|
||||
|
||||
boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
|
||||
|
||||
/**
|
||||
* Handles a mouse click on the specified slot.
|
||||
*
|
||||
* @param slot the slot instance
|
||||
* @param slotId the slot id
|
||||
* @param clickData the click data
|
||||
* @param actionType the action type
|
||||
*/
|
||||
void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.util;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft keybinding with extra access.
|
||||
*/
|
||||
public interface KeyBindingAccessor {
|
||||
boolean midnightcontrols$press();
|
||||
|
||||
boolean midnightcontrols$unpress();
|
||||
|
||||
default boolean midnightcontrols$handlePressState(boolean pressed) {
|
||||
if (pressed)
|
||||
return this.midnightcontrols$press();
|
||||
else
|
||||
return this.midnightcontrols$unpress();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.client.util;
|
||||
|
||||
/**
|
||||
* Represents mouse's extra access.
|
||||
*/
|
||||
public interface MouseAccessor {
|
||||
void midnightcontrols$onCursorPos(long window, double x, double y);
|
||||
void setLeftButtonClicked(boolean value);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of midnightcontrols.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package eu.midnightdust.midnightcontrols.event;
|
||||
|
||||
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an event callback which is fired when a player changes the controls mode.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.1.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PlayerChangeControlsModeCallback {
|
||||
Event<PlayerChangeControlsModeCallback> EVENT = EventFactory.createArrayBacked(PlayerChangeControlsModeCallback.class, listeners -> (player, controlsMode) -> {
|
||||
for (PlayerChangeControlsModeCallback event : listeners) {
|
||||
event.apply(player, controlsMode);
|
||||
}
|
||||
});
|
||||
|
||||
void apply(@NotNull PlayerEntity player, @NotNull ControlsMode controlsMode);
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.options.GameOptions;
|
||||
import net.minecraft.client.options.KeyBinding;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.ScreenshotUtils;
|
||||
import org.aperlambda.lambdacommon.Identifier;
|
||||
import org.aperlambda.lambdacommon.utils.Identifiable;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a button binding.
|
||||
*
|
||||
* @author LambdAurora
|
||||
*/
|
||||
public class ButtonBinding implements Nameable
|
||||
{
|
||||
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
|
||||
private static final List<Category> CATEGORIES = new ArrayList<>();
|
||||
public static final Category MOVEMENT_CATEGORY;
|
||||
public static final Category GAMEPLAY_CATEGORY;
|
||||
public static final Category INVENTORY_CATEGORY;
|
||||
public static final Category MULTIPLAYER_CATEGORY;
|
||||
public static final Category MISC_CATEGORY;
|
||||
public static final ButtonBinding ATTACK = new ButtonBinding("attack", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true));
|
||||
public static final ButtonBinding BACK = new ButtonBinding("back", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false));
|
||||
public static final ButtonBinding CHAT = new ButtonBinding("chat", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||
public static final ButtonBinding DROP_ITEM = new ButtonBinding("drop_item", GLFW.GLFW_GAMEPAD_BUTTON_B);
|
||||
public static final ButtonBinding FORWARD = new ButtonBinding("forward", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true));
|
||||
public static final ButtonBinding INVENTORY = new ButtonBinding("inventory", GLFW.GLFW_GAMEPAD_BUTTON_Y);
|
||||
public static final ButtonBinding JUMP = new ButtonBinding("jump", GLFW.GLFW_GAMEPAD_BUTTON_A);
|
||||
public static final ButtonBinding LEFT = new ButtonBinding("left", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, false));
|
||||
public static final ButtonBinding PAUSE_GAME = new ButtonBinding("pause_game", GLFW.GLFW_GAMEPAD_BUTTON_START);
|
||||
public static final ButtonBinding PICK_BLOCK = new ButtonBinding("pick_block", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
public static final ButtonBinding PLAYER_LIST = new ButtonBinding("player_list", GLFW.GLFW_GAMEPAD_BUTTON_BACK);
|
||||
public static final ButtonBinding RIGHT = new ButtonBinding("right", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, true));
|
||||
public static final ButtonBinding SCREENSHOT = new ButtonBinding("screenshot", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
|
||||
Collections.singletonList((client, action) -> {
|
||||
if (action == 0)
|
||||
ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(),
|
||||
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
|
||||
return true;
|
||||
}));
|
||||
public static final ButtonBinding SMOOTH_CAMERA = new ButtonBinding("toggle_smooth_camera", -1);
|
||||
public static final ButtonBinding SNEAK = new ButtonBinding("sneak", GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB);
|
||||
public static final ButtonBinding SPRINT = new ButtonBinding("sprint", GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB);
|
||||
public static final ButtonBinding SWAP_HANDS = new ButtonBinding("swap_hands", GLFW.GLFW_GAMEPAD_BUTTON_X);
|
||||
public static final ButtonBinding TOGGLE_PERSPECTIVE = new ButtonBinding("toggle_perspective", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP);
|
||||
public static final ButtonBinding USE = new ButtonBinding("use", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true));
|
||||
|
||||
private int button;
|
||||
private int default_button;
|
||||
private String key;
|
||||
private KeyBinding minecraft_key_binding = null;
|
||||
private List<PressAction> actions = new ArrayList<>(Collections.singletonList((client, action) -> {
|
||||
this.as_key_binding().ifPresent(key_binding -> ((KeyBindingAccessor) key_binding).handle_press_state(this.is_button_down()));
|
||||
return true;
|
||||
}));
|
||||
private boolean pressed = false;
|
||||
|
||||
protected ButtonBinding(@NotNull String key, int default_button, @NotNull List<PressAction> actions)
|
||||
{
|
||||
this.default_button = this.button = default_button;
|
||||
this.key = key;
|
||||
this.actions.addAll(actions);
|
||||
BINDINGS.add(this);
|
||||
}
|
||||
|
||||
protected ButtonBinding(@NotNull String key, int default_button)
|
||||
{
|
||||
this(key, default_button, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the button bound.
|
||||
*
|
||||
* @return The bound button.
|
||||
*/
|
||||
public int get_button()
|
||||
{
|
||||
return this.button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bound button.
|
||||
*
|
||||
* @param button The bound button.
|
||||
*/
|
||||
public void set_button(int button)
|
||||
{
|
||||
this.button = button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the bound button is the specified button or not.
|
||||
*
|
||||
* @param button The button to check.
|
||||
* @return True if the bound button is the specified button, else false.
|
||||
*/
|
||||
public boolean is_button(int button)
|
||||
{
|
||||
return this.button == button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this button is down or not.
|
||||
*
|
||||
* @return True if the button is down, else false.
|
||||
*/
|
||||
public boolean is_button_down()
|
||||
{
|
||||
return this.pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this button binding is bound or not.
|
||||
*
|
||||
* @return True if this button binding is bound, else false.
|
||||
*/
|
||||
public boolean is_not_bound()
|
||||
{
|
||||
return this.button == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default button assigned to this binding.
|
||||
*
|
||||
* @return The default button.
|
||||
*/
|
||||
public int get_default_button()
|
||||
{
|
||||
return this.default_button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the assigned button is the default button.
|
||||
*
|
||||
* @return True if the assigned button is the default button, else false.
|
||||
*/
|
||||
public boolean is_default()
|
||||
{
|
||||
return this.button == this.default_button;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String get_name()
|
||||
{
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation key of this button binding.
|
||||
*
|
||||
* @return The translation key.
|
||||
*/
|
||||
public @NotNull String get_translation_key()
|
||||
{
|
||||
return "lambdacontrols.action." + this.get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key binding equivalent of this button binding.
|
||||
*
|
||||
* @return The key binding equivalent.
|
||||
*/
|
||||
public @NotNull Optional<KeyBinding> as_key_binding()
|
||||
{
|
||||
return Optional.ofNullable(this.minecraft_key_binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified axis as a button.
|
||||
*
|
||||
* @param axis The axis.
|
||||
* @param positive True if the axis part is positive, else false.
|
||||
* @return The axis as a button.
|
||||
*/
|
||||
public static int axis_as_button(int axis, boolean positive)
|
||||
{
|
||||
return positive ? 100 + axis : 200 + axis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the second Joycon's specified button code.
|
||||
*
|
||||
* @param button The raw button code.
|
||||
* @return The second Joycon's button code.
|
||||
*/
|
||||
public static int joycon2_button(int button)
|
||||
{
|
||||
return 300 + button;
|
||||
}
|
||||
|
||||
public static void init(@NotNull GameOptions options)
|
||||
{
|
||||
ATTACK.minecraft_key_binding = options.keyAttack;
|
||||
BACK.minecraft_key_binding = options.keyBack;
|
||||
CHAT.minecraft_key_binding = options.keyChat;
|
||||
DROP_ITEM.minecraft_key_binding = options.keyDrop;
|
||||
FORWARD.minecraft_key_binding = options.keyForward;
|
||||
INVENTORY.minecraft_key_binding = options.keyInventory;
|
||||
JUMP.minecraft_key_binding = options.keyJump;
|
||||
LEFT.minecraft_key_binding = options.keyLeft;
|
||||
PICK_BLOCK.minecraft_key_binding = options.keyPickItem;
|
||||
PLAYER_LIST.minecraft_key_binding = options.keyPlayerList;
|
||||
RIGHT.minecraft_key_binding = options.keyRight;
|
||||
SCREENSHOT.minecraft_key_binding = options.keyScreenshot;
|
||||
SMOOTH_CAMERA.minecraft_key_binding = options.keySmoothCamera;
|
||||
SNEAK.minecraft_key_binding = options.keySneak;
|
||||
SPRINT.minecraft_key_binding = options.keySprint;
|
||||
SWAP_HANDS.minecraft_key_binding = options.keySwapHands;
|
||||
TOGGLE_PERSPECTIVE.minecraft_key_binding = options.keyTogglePerspective;
|
||||
USE.minecraft_key_binding = options.keyUse;
|
||||
}
|
||||
|
||||
public static void load_from_config(@NotNull LambdaControlsConfig config)
|
||||
{
|
||||
BINDINGS.forEach(config::load_button_binding);
|
||||
}
|
||||
|
||||
public static void set_button_state(int button, boolean state)
|
||||
{
|
||||
BINDINGS.parallelStream().filter(binding -> Objects.equals(binding.button, button))
|
||||
.forEach(binding -> binding.pressed = state);
|
||||
}
|
||||
|
||||
public static void handle_button(@NotNull MinecraftClient client, int button, int action)
|
||||
{
|
||||
BINDINGS.parallelStream().filter(binding -> binding.button == button)
|
||||
.forEach(binding -> {
|
||||
for (int i = binding.actions.size() - 1; i >= 0; i--) {
|
||||
if (binding.actions.get(i).press(client, action))
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the button has duplicated bindings.
|
||||
*
|
||||
* @param button The button to check.
|
||||
* @return True if the button has duplicated bindings, else false.
|
||||
*/
|
||||
public static boolean has_duplicates(int button)
|
||||
{
|
||||
return BINDINGS.parallelStream().filter(binding -> binding.button == button).count() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized name of the specified button.
|
||||
*
|
||||
* @param button The button.
|
||||
* @return The localized name of the button.
|
||||
*/
|
||||
public static @NotNull String get_localized_button_name(int button)
|
||||
{
|
||||
switch (button) {
|
||||
case -1:
|
||||
return I18n.translate("key.keyboard.unknown");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_A:
|
||||
return I18n.translate("lambdacontrols.button.a");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_B:
|
||||
return I18n.translate("lambdacontrols.button.b");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_X:
|
||||
return I18n.translate("lambdacontrols.button.x");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_Y:
|
||||
return I18n.translate("lambdacontrols.button.y");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
|
||||
return I18n.translate("lambdacontrols.button.left_bumper");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
|
||||
return I18n.translate("lambdacontrols.button.right_bumper");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
|
||||
return I18n.translate("lambdacontrols.button.back");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_START:
|
||||
return I18n.translate("lambdacontrols.button.start");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
|
||||
return I18n.translate("lambdacontrols.button.guide");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
|
||||
return I18n.translate("lambdacontrols.button.left_thumb");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
|
||||
return I18n.translate("lambdacontrols.button.right_thumb");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return I18n.translate("lambdacontrols.button.dpad_up");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return I18n.translate("lambdacontrols.button.dpad_right");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return I18n.translate("lambdacontrols.button.dpad_down");
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return I18n.translate("lambdacontrols.button.dpad_left");
|
||||
case 100:
|
||||
return I18n.translate("lambdacontrols.axis.left_x+");
|
||||
case 101:
|
||||
return I18n.translate("lambdacontrols.axis.left_y+");
|
||||
case 102:
|
||||
return I18n.translate("lambdacontrols.axis.right_x+");
|
||||
case 103:
|
||||
return I18n.translate("lambdacontrols.axis.right_y+");
|
||||
case 104:
|
||||
return I18n.translate("lambdacontrols.axis.left_trigger");
|
||||
case 105:
|
||||
return I18n.translate("lambdacontrols.axis.right_trigger");
|
||||
case 200:
|
||||
return I18n.translate("lambdacontrols.axis.left_x-");
|
||||
case 201:
|
||||
return I18n.translate("lambdacontrols.axis.left_y-");
|
||||
case 202:
|
||||
return I18n.translate("lambdacontrols.axis.right_x-");
|
||||
case 203:
|
||||
return I18n.translate("lambdacontrols.axis.right_y-");
|
||||
default:
|
||||
return I18n.translate("lambdacontrols.button.unknown", button);
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Stream<ButtonBinding> stream()
|
||||
{
|
||||
return BINDINGS.stream();
|
||||
}
|
||||
|
||||
public static @NotNull Stream<Category> stream_categories()
|
||||
{
|
||||
return CATEGORIES.stream();
|
||||
}
|
||||
|
||||
static {
|
||||
MOVEMENT_CATEGORY = register_default_category("key.categories.movement", category -> category.register_all_bindings(
|
||||
FORWARD,
|
||||
BACK,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
JUMP,
|
||||
SNEAK,
|
||||
SPRINT));
|
||||
GAMEPLAY_CATEGORY = register_default_category("key.categories.gameplay", category -> category.register_all_bindings(
|
||||
ATTACK,
|
||||
PICK_BLOCK,
|
||||
USE
|
||||
));
|
||||
INVENTORY_CATEGORY = register_default_category("key.categories.inventory", category -> category.register_all_bindings(
|
||||
DROP_ITEM,
|
||||
INVENTORY,
|
||||
SWAP_HANDS
|
||||
));
|
||||
MULTIPLAYER_CATEGORY = register_default_category("key.categories.multiplayer",
|
||||
category -> category.register_all_bindings(CHAT, PLAYER_LIST));
|
||||
MISC_CATEGORY = register_default_category("key.categories.misc", category -> category.register_all_bindings(
|
||||
SCREENSHOT,
|
||||
//SMOOTH_CAMERA,
|
||||
TOGGLE_PERSPECTIVE
|
||||
));
|
||||
}
|
||||
|
||||
public static ButtonBinding register(@NotNull Identifier binding_id, int default_button, @NotNull List<PressAction> actions)
|
||||
{
|
||||
return new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions);
|
||||
}
|
||||
|
||||
public static ButtonBinding register(@NotNull Identifier binding_id, int default_button)
|
||||
{
|
||||
return register(binding_id, default_button, Collections.emptyList());
|
||||
}
|
||||
|
||||
public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button, @NotNull List<PressAction> actions)
|
||||
{
|
||||
return register(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions);
|
||||
}
|
||||
|
||||
public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button)
|
||||
{
|
||||
return register(binding_id, default_button, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a category of button bindings.
|
||||
*
|
||||
* @param category The category to register.
|
||||
* @return The registered category.
|
||||
*/
|
||||
public static Category register_category(@NotNull Category category)
|
||||
{
|
||||
CATEGORIES.add(category);
|
||||
return category;
|
||||
}
|
||||
|
||||
public static Category register_category(@NotNull Identifier identifier, int priority)
|
||||
{
|
||||
return register_category(new Category(identifier, priority));
|
||||
}
|
||||
|
||||
public static Category register_category(@NotNull Identifier identifier)
|
||||
{
|
||||
return register_category(new Category(identifier));
|
||||
}
|
||||
|
||||
private static Category register_default_category(@NotNull String key, @NotNull Consumer<Category> key_adder)
|
||||
{
|
||||
Category category = register_category(new Identifier("minecraft", key), CATEGORIES.size());
|
||||
key_adder.accept(category);
|
||||
return category;
|
||||
}
|
||||
|
||||
public static class Category implements Identifiable
|
||||
{
|
||||
private final List<ButtonBinding> bindings = new ArrayList<>();
|
||||
private final Identifier id;
|
||||
private int priority;
|
||||
|
||||
public Category(@NotNull Identifier id, int priority)
|
||||
{
|
||||
this.id = id;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Category(@NotNull Identifier id)
|
||||
{
|
||||
this(id, 100);
|
||||
}
|
||||
|
||||
public void register_binding(@NotNull ButtonBinding binding)
|
||||
{
|
||||
if (this.bindings.contains(binding))
|
||||
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
|
||||
this.bindings.add(binding);
|
||||
}
|
||||
|
||||
public void register_all_bindings(@NotNull ButtonBinding... bindings)
|
||||
{
|
||||
this.register_all_bindings(Arrays.asList(bindings));
|
||||
}
|
||||
|
||||
public void register_all_bindings(@NotNull List<ButtonBinding> bindings)
|
||||
{
|
||||
bindings.forEach(this::register_binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bindings assigned to this category.
|
||||
*
|
||||
* @return The bindings assigned to this category.
|
||||
*/
|
||||
public @NotNull List<ButtonBinding> get_bindings()
|
||||
{
|
||||
return Collections.unmodifiableList(this.bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this category.
|
||||
* <p>
|
||||
* The translation key should be `modid.identifier_name`.
|
||||
*
|
||||
* @return The translated name.
|
||||
*/
|
||||
public @NotNull String get_translated_name()
|
||||
{
|
||||
if (this.id.get_namespace().equals("minecraft"))
|
||||
return I18n.translate(this.id.get_name());
|
||||
else
|
||||
return I18n.translate(this.id.get_namespace() + "." + this.id.get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the priority display of this category.
|
||||
* It will defines in which order the categories will display on the controls screen.
|
||||
*
|
||||
* @return The priority of this category.
|
||||
*/
|
||||
public int get_priority()
|
||||
{
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Identifier get_identifier()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface PressAction
|
||||
{
|
||||
/**
|
||||
* Handles when there is a press action on the button.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @param action The action done.
|
||||
*/
|
||||
boolean press(@NotNull MinecraftClient client, int action);
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWGamepadState;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.lwjgl.BufferUtils.createByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents a controller.
|
||||
*/
|
||||
public class Controller implements Nameable
|
||||
{
|
||||
private static final Map<Integer, Controller> CONTROLLERS = new HashMap<>();
|
||||
private final int id;
|
||||
|
||||
public Controller(int id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier of this controller.
|
||||
*
|
||||
* @return The identifier of this controller.
|
||||
*/
|
||||
public int get_id()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's globally unique identifier.
|
||||
*
|
||||
* @return The controller's GUID.
|
||||
*/
|
||||
public String get_guid()
|
||||
{
|
||||
String guid = GLFW.glfwGetJoystickGUID(this.id);
|
||||
return guid == null ? "" : guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this controller is connected or not.
|
||||
*
|
||||
* @return True if this controller is connected, else false.
|
||||
*/
|
||||
public boolean is_connected()
|
||||
{
|
||||
return GLFW.glfwJoystickPresent(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this controller is a gamepad or not.
|
||||
*
|
||||
* @return True if this controller is a gamepad, else false.
|
||||
*/
|
||||
public boolean is_gamepad()
|
||||
{
|
||||
return this.is_connected() && GLFW.glfwJoystickIsGamepad(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the controller.
|
||||
*
|
||||
* @return The controller's name.
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String get_name()
|
||||
{
|
||||
String name = this.is_gamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
|
||||
return name == null ? String.valueOf(this.get_id()) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the controller.
|
||||
*
|
||||
* @return The state of the controller input.
|
||||
*/
|
||||
public GLFWGamepadState get_state()
|
||||
{
|
||||
GLFWGamepadState state = GLFWGamepadState.create();
|
||||
if (this.is_gamepad())
|
||||
GLFW.glfwGetGamepadState(this.id, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
public static @NotNull Controller by_id(int id)
|
||||
{
|
||||
if (id > GLFW.GLFW_JOYSTICK_LAST) {
|
||||
LambdaControls.get().log("Controller '" + id + "' doesn't exist.");
|
||||
id = GLFW.GLFW_JOYSTICK_LAST;
|
||||
}
|
||||
Controller controller;
|
||||
if (CONTROLLERS.containsKey(id))
|
||||
return CONTROLLERS.get(id);
|
||||
else {
|
||||
controller = new Controller(id);
|
||||
CONTROLLERS.put(id, controller);
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Optional<Controller> by_guid(@NotNull String guid)
|
||||
{
|
||||
return CONTROLLERS.values().stream().filter(Controller::is_connected)
|
||||
.filter(controller -> controller.get_guid().equals(guid))
|
||||
.max(Comparator.comparingInt(Controller::get_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified resource and returns the raw data as a ByteBuffer.
|
||||
*
|
||||
* @param resource The resource to read.
|
||||
* @param buffer_size The initial buffer size.
|
||||
* @return The resource data.
|
||||
* @throws IOException If an IO error occurs.
|
||||
*/
|
||||
private static ByteBuffer io_resource_to_buffer(String resource, int buffer_size) throws IOException
|
||||
{
|
||||
ByteBuffer buffer = null;
|
||||
|
||||
Path path = Paths.get(resource);
|
||||
if (Files.isReadable(path)) {
|
||||
try (SeekableByteChannel fc = Files.newByteChannel(path)) {
|
||||
buffer = createByteBuffer((int) fc.size() + 2);
|
||||
while (fc.read(buffer) != -1) ;
|
||||
buffer.put((byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
((Buffer) buffer).flip(); // Force Java 8 >.<
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the controller mappings.
|
||||
*/
|
||||
public static void update_mappings()
|
||||
{
|
||||
try {
|
||||
File mappings_file = new File("config/gamecontrollerdb.txt");
|
||||
if (!mappings_file.exists())
|
||||
return;
|
||||
LambdaControls.get().log("Updating controller mappings...");
|
||||
ByteBuffer buffer = io_resource_to_buffer(mappings_file.getPath(), 1024);
|
||||
GLFW.glfwUpdateGamepadMappings(buffer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a controller type.
|
||||
*/
|
||||
public enum ControllerType implements Nameable
|
||||
{
|
||||
DEFAULT(0),
|
||||
DUALSHOCK(1),
|
||||
SWITCH(2),
|
||||
XBOX(3),
|
||||
STEAM(4),
|
||||
OUYA(5);
|
||||
|
||||
private final int id;
|
||||
|
||||
ControllerType(int id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the controller type's identifier.
|
||||
*
|
||||
* @return The controller type's identifier.
|
||||
*/
|
||||
public int get_id()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next controller type available.
|
||||
*
|
||||
* @return The next available controller type.
|
||||
*/
|
||||
public ControllerType next()
|
||||
{
|
||||
ControllerType[] v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this controller type.
|
||||
*
|
||||
* @return The translated name of this controller type.
|
||||
*/
|
||||
public String get_translated_name()
|
||||
{
|
||||
return I18n.translate("lambdacontrols.controller_type." + this.get_name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String get_name()
|
||||
{
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller type from its identifier.
|
||||
*
|
||||
* @param id The identifier of the controller type.
|
||||
* @return The controller type if found, else empty.
|
||||
*/
|
||||
public static Optional<ControllerType> by_id(@NotNull String id)
|
||||
{
|
||||
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the controls mode.
|
||||
*/
|
||||
public enum ControlsMode implements Nameable
|
||||
{
|
||||
DEFAULT,
|
||||
CONTROLLER,
|
||||
TOUCHSCREEN;
|
||||
|
||||
/**
|
||||
* Returns the next controls mode available.
|
||||
*
|
||||
* @return The next available controls mode.
|
||||
*/
|
||||
public ControlsMode next()
|
||||
{
|
||||
ControlsMode[] v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this controls mode.
|
||||
*
|
||||
* @return The translated name of this controls mode.
|
||||
*/
|
||||
public String get_translated_name()
|
||||
{
|
||||
return I18n.translate("lambdacontrols.controls_mode." + this.get_name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String get_name()
|
||||
{
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controls mode from its identifier.
|
||||
*
|
||||
* @param id The identifier of the controls mode.
|
||||
* @return The controls mode if found, else empty.
|
||||
*/
|
||||
public static Optional<ControlsMode> by_id(@NotNull String id)
|
||||
{
|
||||
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the hud side which is the side where the movements buttons are.
|
||||
*/
|
||||
public enum HudSide implements Nameable
|
||||
{
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
/**
|
||||
* Returns the next side available.
|
||||
*
|
||||
* @return The next available side.
|
||||
*/
|
||||
public HudSide next()
|
||||
{
|
||||
HudSide[] v = values();
|
||||
if (v.length == this.ordinal() + 1)
|
||||
return v[0];
|
||||
return v[this.ordinal() + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translated name of this hud side.
|
||||
*
|
||||
* @return The translated name of this hud side.
|
||||
*/
|
||||
public String get_translated_name()
|
||||
{
|
||||
return I18n.translate("lambdacontrols.hud_side." + this.get_name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String get_name()
|
||||
{
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hud side from its identifier.
|
||||
*
|
||||
* @param id The identifier of the hud side.
|
||||
* @return The hud side if found, else empty.
|
||||
*/
|
||||
public static Optional<HudSide> by_id(@NotNull String id)
|
||||
{
|
||||
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
|
||||
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
* Represents the LambdaControls mod.
|
||||
*/
|
||||
public class LambdaControls implements ClientModInitializer
|
||||
{
|
||||
private static LambdaControls INSTANCE;
|
||||
public static final FabricKeyBinding BINDING_LOOK_UP = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_up"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement").build();
|
||||
public static final FabricKeyBinding BINDING_LOOK_RIGHT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_right"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement").build();
|
||||
public static final FabricKeyBinding BINDING_LOOK_DOWN = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_down"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement").build();
|
||||
public static final FabricKeyBinding BINDING_LOOK_LEFT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_left"),
|
||||
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement").build();
|
||||
public static final Identifier CONTROLLER_BUTTONS = new Identifier("lambdacontrols", "textures/gui/controller_buttons.png");
|
||||
public static final Identifier CONTROLLER_AXIS = new Identifier("lambdacontrols", "textures/gui/controller_axis.png");
|
||||
public final Logger logger = LogManager.getLogger("LambdaControls");
|
||||
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
|
||||
public final LambdaInput input = new LambdaInput(this);
|
||||
private ControlsMode previous_controls_mode;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient()
|
||||
{
|
||||
INSTANCE = this;
|
||||
this.log("Initializing LambdaControls...");
|
||||
this.config.load();
|
||||
|
||||
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_UP);
|
||||
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_RIGHT);
|
||||
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_DOWN);
|
||||
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Minecraft is initializing.
|
||||
*/
|
||||
public void on_mc_init(@NotNull MinecraftClient client)
|
||||
{
|
||||
Controller.update_mappings();
|
||||
ButtonBinding.init(client.options);
|
||||
GLFW.glfwSetJoystickCallback((jid, event) -> {
|
||||
if (event == GLFW.GLFW_CONNECTED) {
|
||||
Controller controller = Controller.by_id(jid);
|
||||
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.connected", jid),
|
||||
new LiteralText(controller.get_name())));
|
||||
} else if (event == GLFW.GLFW_DISCONNECTED) {
|
||||
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.disconnected", jid),
|
||||
null));
|
||||
}
|
||||
|
||||
this.switch_controls_mode();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick.
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
public void on_tick(@NotNull MinecraftClient client)
|
||||
{
|
||||
this.input.on_tick(client);
|
||||
if (this.config.get_controls_mode() == ControlsMode.CONTROLLER)
|
||||
this.input.on_controller_tick(client);
|
||||
}
|
||||
|
||||
public void on_render(MinecraftClient client)
|
||||
{
|
||||
this.input.on_render(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the controls mode if the auto switch is enabled.
|
||||
*/
|
||||
public void switch_controls_mode()
|
||||
{
|
||||
if (this.config.has_auto_switch_mode()) {
|
||||
if (this.config.get_controller().is_gamepad()) {
|
||||
this.previous_controls_mode = this.config.get_controls_mode();
|
||||
this.config.set_controls_mode(ControlsMode.CONTROLLER);
|
||||
} else {
|
||||
if (this.previous_controls_mode == null) {
|
||||
this.previous_controls_mode = ControlsMode.DEFAULT;
|
||||
}
|
||||
|
||||
this.config.set_controls_mode(this.previous_controls_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a message to the terminal.
|
||||
*
|
||||
* @param info The message to print.
|
||||
*/
|
||||
public void log(String info)
|
||||
{
|
||||
this.logger.info("[LambdaControls] " + info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LambdaControls instance.
|
||||
*
|
||||
* @return The LambdaControls instance.
|
||||
*/
|
||||
public static LambdaControls get()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static int draw_button(int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client)
|
||||
{
|
||||
return draw_button(x, y, button.get_button(), client);
|
||||
}
|
||||
|
||||
public static int draw_button(int x, int y, int button, @NotNull MinecraftClient client)
|
||||
{
|
||||
if (button == -1)
|
||||
return 0;
|
||||
|
||||
int controller_type = get().config.get_controller_type().get_id();
|
||||
boolean axis = false;
|
||||
int button_offset = button * 15;
|
||||
switch (button) {
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
|
||||
button_offset = 7 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
|
||||
button_offset = 8 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
|
||||
button_offset = 4 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_START:
|
||||
button_offset = 6 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
|
||||
button_offset = 5 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
|
||||
button_offset = 15 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
|
||||
button_offset = 16 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100:
|
||||
button_offset = 0;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100:
|
||||
button_offset = 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100:
|
||||
button_offset = 2 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100:
|
||||
button_offset = 3 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200:
|
||||
button_offset = 4 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200:
|
||||
button_offset = 5 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200:
|
||||
button_offset = 6 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200:
|
||||
button_offset = 7 * 18;
|
||||
axis = true;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 100:
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 200:
|
||||
button_offset = 9 * 15;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100:
|
||||
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200:
|
||||
button_offset = 10 * 15;
|
||||
break;
|
||||
}
|
||||
|
||||
client.getTextureManager().bindTexture(axis ? LambdaControls.CONTROLLER_AXIS : LambdaControls.CONTROLLER_BUTTONS);
|
||||
GlStateManager.disableDepthTest();
|
||||
|
||||
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
DrawableHelper.blit(x, y, (float) button_offset, (float) (controller_type * (axis ? 18 : 15)), axis ? 18 : 15, axis ? 18 : 15, 256, 256);
|
||||
GlStateManager.enableDepthTest();
|
||||
|
||||
return axis ? 18 : 15;
|
||||
}
|
||||
|
||||
public static int draw_button_tip(int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client)
|
||||
{
|
||||
return draw_button_tip(x, y, button.get_button(), button.get_translation_key(), display, client);
|
||||
}
|
||||
|
||||
public static int draw_button_tip(int x, int y, int button, @NotNull String action, boolean display, @NotNull MinecraftClient client)
|
||||
{
|
||||
String translated_action = I18n.translate(action);
|
||||
|
||||
if (display) {
|
||||
int button_width = draw_button(x, y, button, client);
|
||||
|
||||
int text_y = (15 - client.textRenderer.fontHeight) / 2;
|
||||
client.textRenderer.drawWithShadow(translated_action, (float) (x + button_width + 5), (float) (y + text_y), 14737632);
|
||||
}
|
||||
|
||||
return display ? get_button_tip_width(translated_action, client.textRenderer) : -10;
|
||||
}
|
||||
|
||||
private static int get_button_tip_width(@NotNull String action, @NotNull TextRenderer text_renderer)
|
||||
{
|
||||
return 15 + 5 + text_renderer.getStringWidth(action);
|
||||
}
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import net.minecraft.client.options.KeyBinding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents LambdaControls configuration.
|
||||
*/
|
||||
public class LambdaControlsConfig
|
||||
{
|
||||
private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build();
|
||||
private final Map<String, KeyBinding> keybinding_mappings = new HashMap<>();
|
||||
private final LambdaControls mod;
|
||||
private ControlsMode controls_mode;
|
||||
private ControllerType controller_type;
|
||||
// HUD settings.
|
||||
private boolean hud_enable;
|
||||
private HudSide hud_side;
|
||||
// Controller settings
|
||||
private double dead_zone;
|
||||
private double rotation_speed;
|
||||
private double mouse_speed;
|
||||
|
||||
public LambdaControlsConfig(@NotNull LambdaControls mod)
|
||||
{
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
this.keybinding_mappings.clear();
|
||||
this.config.load();
|
||||
this.mod.log("Configuration loaded.");
|
||||
this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", "default")).orElse(ControlsMode.DEFAULT);
|
||||
// HUD settings.
|
||||
this.hud_enable = this.config.getOrElse("hud.enable", true);
|
||||
this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", "left")).orElse(HudSide.LEFT);
|
||||
// Controller settings.
|
||||
this.controller_type = ControllerType.by_id(this.config.getOrElse("controller.type", "default")).orElse(ControllerType.DEFAULT);
|
||||
this.dead_zone = this.config.getOrElse("controller.dead_zone", 0.25);
|
||||
this.rotation_speed = this.config.getOrElse("controller.rotation_speed", 40.0);
|
||||
this.mouse_speed = this.config.getOrElse("controller.mouse_speed", 25.0);
|
||||
// Controller controls.
|
||||
ButtonBinding.load_from_config(this);
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
this.config.set("controller.dead_zone", this.dead_zone);
|
||||
this.config.set("controller.rotation_speed", this.rotation_speed);
|
||||
this.config.set("controller.mouse_speed", this.mouse_speed);
|
||||
this.config.save();
|
||||
this.mod.log("Configuration saved.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controls mode from the configuration.
|
||||
*
|
||||
* @return The controls mode.
|
||||
*/
|
||||
public @NotNull ControlsMode get_controls_mode()
|
||||
{
|
||||
return this.controls_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controls mode in the configuration.
|
||||
*
|
||||
* @param controls_mode The controls mode.
|
||||
*/
|
||||
public void set_controls_mode(@NotNull ControlsMode controls_mode)
|
||||
{
|
||||
this.controls_mode = controls_mode;
|
||||
this.config.set("controls", controls_mode.get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the auto switch mode is enabled or not.
|
||||
*
|
||||
* @return True if the auto switch mode is enabled, else false.
|
||||
*/
|
||||
public boolean has_auto_switch_mode()
|
||||
{
|
||||
return this.config.getOrElse("auto_switch_mode", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the auto switch mode is enabled or not.
|
||||
*
|
||||
* @param auto_switch_mode True if the auto switch mode is enabled, else false.
|
||||
*/
|
||||
public void set_auto_switch_mode(boolean auto_switch_mode)
|
||||
{
|
||||
this.config.set("auto_switch_mode", auto_switch_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the HUD is enabled.
|
||||
*
|
||||
* @return True if the HUD is enabled, else false.
|
||||
*/
|
||||
public boolean is_hud_enabled()
|
||||
{
|
||||
return this.hud_enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the HUD is enabled.
|
||||
*
|
||||
* @param enable True if the HUD is enabled, else false.
|
||||
*/
|
||||
public void set_hud_enabled(boolean enable)
|
||||
{
|
||||
this.hud_enable = enable;
|
||||
this.config.set("hud.enable", this.hud_enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HUD side from the configuration.
|
||||
*
|
||||
* @return The HUD side.
|
||||
*/
|
||||
public @NotNull HudSide get_hud_side()
|
||||
{
|
||||
return this.hud_side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the HUD side in the configuration.
|
||||
*
|
||||
* @param hud_side The HUD side.
|
||||
*/
|
||||
public void set_hud_side(@NotNull HudSide hud_side)
|
||||
{
|
||||
this.hud_side = hud_side;
|
||||
this.config.set("hud.side", hud_side.get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the used controller.
|
||||
*
|
||||
* @return The used controller.
|
||||
*/
|
||||
public @NotNull Controller get_controller()
|
||||
{
|
||||
Object raw = this.config.getRaw("controller.id");
|
||||
if (raw instanceof Number) {
|
||||
return Controller.by_id((Integer) raw);
|
||||
} else if (raw instanceof String) {
|
||||
return Controller.by_guid((String) raw).orElse(Controller.by_id(GLFW.GLFW_JOYSTICK_1));
|
||||
}
|
||||
return Controller.by_id(GLFW.GLFW_JOYSTICK_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the used controller.
|
||||
*
|
||||
* @param controller The used controller.
|
||||
*/
|
||||
public void set_controller(@NotNull Controller controller)
|
||||
{
|
||||
this.config.set("controller.id", controller.get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's type.
|
||||
*
|
||||
* @return The controller's type.
|
||||
*/
|
||||
public @NotNull ControllerType get_controller_type()
|
||||
{
|
||||
return this.controller_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's type.
|
||||
*
|
||||
* @param controller_type The controller's type.
|
||||
*/
|
||||
public void set_controller_type(@NotNull ControllerType controller_type)
|
||||
{
|
||||
this.controller_type = controller_type;
|
||||
this.config.set("controller.type", controller_type.get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's dead zone from the configuration.
|
||||
*
|
||||
* @return The controller's dead zone value.
|
||||
*/
|
||||
public double get_dead_zone()
|
||||
{
|
||||
return this.dead_zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's dead zone in the configuration.
|
||||
*
|
||||
* @param dead_zone The new controller's dead zone value.
|
||||
*/
|
||||
public void set_dead_zone(double dead_zone)
|
||||
{
|
||||
this.dead_zone = dead_zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's rotation speed.
|
||||
*
|
||||
* @return The rotation speed.
|
||||
*/
|
||||
public double get_rotation_speed()
|
||||
{
|
||||
return this.rotation_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's rotation speed.
|
||||
*
|
||||
* @param rotation_speed The rotation speed.
|
||||
*/
|
||||
public void set_rotation_speed(double rotation_speed)
|
||||
{
|
||||
this.rotation_speed = rotation_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's mouse speed.
|
||||
*
|
||||
* @return The mouse speed.
|
||||
*/
|
||||
public double get_mouse_speed()
|
||||
{
|
||||
return this.mouse_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's mouse speed.
|
||||
*
|
||||
* @param mouse_speed The mouse speed.
|
||||
*/
|
||||
public void set_mouse_speed(double mouse_speed)
|
||||
{
|
||||
this.mouse_speed = mouse_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the right X axis is inverted or not.
|
||||
*
|
||||
* @return True if the right X axis is inverted, else false.
|
||||
*/
|
||||
public boolean does_invert_right_x_axis()
|
||||
{
|
||||
return this.config.getOrElse("controller.invert_right_x_axis", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the right X axis is inverted or not.
|
||||
*
|
||||
* @param invert True if the right X axis is inverted, else false.
|
||||
*/
|
||||
public void set_invert_right_x_axis(boolean invert)
|
||||
{
|
||||
this.config.set("controller.invert_right_x_axis", invert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the right Y axis is inverted or not.
|
||||
*
|
||||
* @return True if the right Y axis is inverted, else false.
|
||||
*/
|
||||
public boolean does_invert_right_y_axis()
|
||||
{
|
||||
return this.config.getOrElse("controller.invert_right_y_axis", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the right Y axis is inverted or not.
|
||||
*
|
||||
* @param invert True if the right Y axis is inverted, else false.
|
||||
*/
|
||||
public void set_invert_right_y_axis(boolean invert)
|
||||
{
|
||||
this.config.set("controller.invert_right_y_axis", invert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right X axis sign.
|
||||
*
|
||||
* @return The right X axis sign.
|
||||
*/
|
||||
public double get_right_x_axis_sign()
|
||||
{
|
||||
return this.does_invert_right_x_axis() ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right Y axis sign.
|
||||
*
|
||||
* @return The right Y axis sign.
|
||||
*/
|
||||
public double get_right_y_axis_sign()
|
||||
{
|
||||
return this.does_invert_right_y_axis() ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keybindings.
|
||||
*
|
||||
* @return The keybindings.
|
||||
*/
|
||||
public @NotNull Map<String, KeyBinding> get_keybindings()
|
||||
{
|
||||
return this.keybinding_mappings;
|
||||
}
|
||||
|
||||
public Optional<KeyBinding> get_keybind(@NotNull String id)
|
||||
{
|
||||
return Optional.ofNullable(this.keybinding_mappings.get(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the button binding from configuration.
|
||||
*
|
||||
* @param button The button binding.
|
||||
*/
|
||||
public void load_button_binding(@NotNull ButtonBinding button)
|
||||
{
|
||||
button.set_button(this.config.getOrElse("controller.controls." + button.get_name(), button.get_button()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the button binding in configuration.
|
||||
*
|
||||
* @param binding The button binding.
|
||||
* @param button The button.
|
||||
*/
|
||||
public void set_button_binding(@NotNull ButtonBinding binding, int button)
|
||||
{
|
||||
binding.set_button(button);
|
||||
this.config.set("controller.controls." + binding.get_name(), button);
|
||||
}
|
||||
|
||||
public boolean is_back_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(btn, state == 1));
|
||||
}
|
||||
|
||||
public boolean is_forward_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(btn, state == 1));
|
||||
}
|
||||
|
||||
public boolean is_left_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(btn, state == 1));
|
||||
}
|
||||
|
||||
public boolean is_right_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(btn, state == 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified axis is an axis used for movements.
|
||||
*
|
||||
* @param i The axis index.
|
||||
* @return True if the axis is used for movements, else false.
|
||||
*/
|
||||
public boolean is_movement_axis(int i)
|
||||
{
|
||||
return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, false))
|
||||
|| ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, false))
|
||||
|| ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, false))
|
||||
|| ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, false));
|
||||
}
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.gui.LabelWidget;
|
||||
import me.lambdaurora.lambdacontrols.gui.LambdaControlsControlsScreen;
|
||||
import me.lambdaurora.lambdacontrols.gui.TouchscreenOverlay;
|
||||
import me.lambdaurora.lambdacontrols.mixin.EntryListWidgetAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.AbstractContainerScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.MouseAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.ParentElement;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
|
||||
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
|
||||
import net.minecraft.client.gui.screen.world.WorldListWidget;
|
||||
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
|
||||
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget;
|
||||
import net.minecraft.client.gui.widget.SliderWidget;
|
||||
import net.minecraft.container.Slot;
|
||||
import net.minecraft.container.SlotActionType;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.aperlambda.lambdacommon.utils.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWGamepadState;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static me.lambdaurora.lambdacontrols.ButtonBinding.*;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
|
||||
|
||||
/**
|
||||
* Represents the LambdaControls' input handler.
|
||||
*/
|
||||
public class LambdaInput
|
||||
{
|
||||
private static final Map<Integer, Boolean> BUTTON_STATES = new HashMap<>();
|
||||
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
|
||||
private static final Map<Integer, Boolean> AXIS_STATES = new HashMap<>();
|
||||
private static final Map<Integer, Integer> AXIS_COOLDOWNS = new HashMap<>();
|
||||
private final LambdaControlsConfig config;
|
||||
private int action_gui_cooldown = 0;
|
||||
private int ignore_next_a = 0;
|
||||
private int last_sneak = 0;
|
||||
private double prev_target_yaw = 0.0;
|
||||
private double prev_target_pitch = 0.0;
|
||||
private double target_yaw = 0.0;
|
||||
private double target_pitch = 0.0;
|
||||
private float prev_x_axis = 0.F;
|
||||
private float prev_y_axis = 0.F;
|
||||
private int prev_target_mouse_x = 0;
|
||||
private int prev_target_mouse_y = 0;
|
||||
private int target_mouse_x = 0;
|
||||
private int target_mouse_y = 0;
|
||||
private float mouse_speed_x = 0.F;
|
||||
private float mouse_speed_y = 0.F;
|
||||
|
||||
public LambdaInput(@NotNull LambdaControls mod)
|
||||
{
|
||||
this.config = mod.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick.
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
public void on_tick(@NotNull MinecraftClient client)
|
||||
{
|
||||
this.prev_target_yaw = this.target_yaw;
|
||||
this.prev_target_pitch = this.target_pitch;
|
||||
|
||||
// Handles the key bindings.
|
||||
if (LambdaControls.BINDING_LOOK_UP.isPressed()) {
|
||||
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
|
||||
} else if (LambdaControls.BINDING_LOOK_DOWN.isPressed()) {
|
||||
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
|
||||
}
|
||||
if (LambdaControls.BINDING_LOOK_LEFT.isPressed()) {
|
||||
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
|
||||
} else if (LambdaControls.BINDING_LOOK_RIGHT.isPressed()) {
|
||||
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick for controller input update.
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
public void on_controller_tick(@NotNull MinecraftClient client)
|
||||
{
|
||||
BUTTON_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> BUTTON_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
|
||||
AXIS_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> AXIS_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
|
||||
// Decreases the last_sneak counter which allows to double press to sneak continuously.
|
||||
if (this.last_sneak > 0)
|
||||
--this.last_sneak;
|
||||
// Decreases the cooldown for GUI actions.
|
||||
if (this.action_gui_cooldown > 0)
|
||||
--this.action_gui_cooldown;
|
||||
this.prev_target_mouse_x = this.target_mouse_x;
|
||||
this.prev_target_mouse_y = this.target_mouse_y;
|
||||
|
||||
Controller controller = this.config.get_controller();
|
||||
if (controller.is_connected()) {
|
||||
GLFWGamepadState state = controller.get_state();
|
||||
this.fetch_button_input(client, state);
|
||||
this.fetch_axe_input(client, state);
|
||||
}
|
||||
|
||||
if (this.ignore_next_a > 0)
|
||||
this.ignore_next_a--;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before the screen is rendered.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @param screen The screen to render.
|
||||
*/
|
||||
public void on_pre_render_screen(@NotNull MinecraftClient client, @NotNull Screen screen)
|
||||
{
|
||||
if (!is_screen_interactive(screen)) {
|
||||
if (this.prev_target_mouse_x != this.target_mouse_x || this.prev_target_mouse_y != this.target_mouse_y) {
|
||||
double mouse_x = this.prev_target_mouse_x + (this.target_mouse_x - this.prev_target_mouse_x) * client.getTickDelta() + 0.5;
|
||||
double mouse_y = this.prev_target_mouse_y + (this.target_mouse_y - this.prev_target_mouse_y) * client.getTickDelta() + 0.5;
|
||||
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouse_x, mouse_y);
|
||||
((MouseAccessor) client.mouse).on_cursor_pos(client.getWindow().getHandle(), mouse_x, mouse_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Minecraft renders.
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
public void on_render(@NotNull MinecraftClient client)
|
||||
{
|
||||
if ((client.currentScreen == null || client.currentScreen instanceof TouchscreenOverlay) &&
|
||||
(this.prev_target_yaw != this.target_yaw || this.prev_target_pitch != this.target_pitch)) {
|
||||
float delta_yaw = (float) ((this.target_yaw - client.player.prevYaw) * client.getTickDelta());
|
||||
float delta_pitch = (float) ((this.target_pitch - client.player.prevPitch) * client.getTickDelta());
|
||||
float rotation_yaw = client.player.prevYaw + delta_yaw;
|
||||
float rotation_pitch = client.player.prevPitch + delta_pitch;
|
||||
client.player.yaw = rotation_yaw;
|
||||
client.player.pitch = MathHelper.clamp(rotation_pitch, -90.F, 90.F);
|
||||
if (client.player.isRiding()) {
|
||||
client.player.getVehicle().copyPositionAndRotation(client.player);
|
||||
}
|
||||
client.getTutorialManager().onUpdateMouse(delta_pitch, delta_yaw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a Screen is opened.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @param window_width The window width.
|
||||
* @param window_height The window height.
|
||||
*/
|
||||
public void on_screen_open(@NotNull MinecraftClient client, int window_width, int window_height)
|
||||
{
|
||||
if (client.currentScreen == null) {
|
||||
this.mouse_speed_x = this.mouse_speed_y = 0.0F;
|
||||
this.target_mouse_x = this.prev_target_mouse_x = (int) (window_width / 2.F);
|
||||
this.target_mouse_y = this.prev_target_mouse_y = (int) (window_height / 2.F);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetch_button_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state)
|
||||
{
|
||||
ByteBuffer buffer = gamepad_state.buttons();
|
||||
for (int btn = 0; btn < buffer.limit(); btn++) {
|
||||
boolean btn_state = buffer.get() == (byte) 1;
|
||||
boolean previous_state = BUTTON_STATES.getOrDefault(btn, false);
|
||||
|
||||
ButtonBinding.set_button_state(btn, btn_state);
|
||||
|
||||
if (btn_state != previous_state) {
|
||||
this.handle_button(client, btn, btn_state ? 0 : 1, btn_state);
|
||||
if (btn_state)
|
||||
BUTTON_COOLDOWNS.put(btn, 5);
|
||||
} else if (btn_state) {
|
||||
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
|
||||
BUTTON_COOLDOWNS.put(btn, 5);
|
||||
this.handle_button(client, btn, 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
BUTTON_STATES.put(btn, btn_state);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetch_axe_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state)
|
||||
{
|
||||
FloatBuffer buffer = gamepad_state.axes();
|
||||
for (int i = 0; i < buffer.limit(); i++) {
|
||||
float value = buffer.get();
|
||||
float abs_value = Math.abs(value);
|
||||
|
||||
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
|
||||
value *= -1.0F;
|
||||
|
||||
ButtonBinding.set_button_state(axis_as_button(i, true), value > 0.5F);
|
||||
ButtonBinding.set_button_state(axis_as_button(i, false), value < -0.5F);
|
||||
|
||||
int state = value > this.config.get_dead_zone() ? 1 : (value < -this.config.get_dead_zone() ? 2 : 0);
|
||||
this.handle_axe(client, i, value, abs_value, state);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state)
|
||||
{
|
||||
if (client.currentScreen instanceof LambdaControlsControlsScreen && action == 0) {
|
||||
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
|
||||
if (controls_screen.focused_binding != null) {
|
||||
this.config.set_button_binding(controls_screen.focused_binding, button);
|
||||
controls_screen.focused_binding = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == 0 || action == 2) {
|
||||
// Handles RB and LB buttons.
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER || button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) {
|
||||
this.handle_rb_lb(client, button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.currentScreen != null && is_screen_interactive(client.currentScreen)
|
||||
&& (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN
|
||||
|| button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) {
|
||||
if (this.action_gui_cooldown == 0) {
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) {
|
||||
this.change_focus(client.currentScreen, false);
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) {
|
||||
this.change_focus(client.currentScreen, true);
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) {
|
||||
this.handle_left_right(client.currentScreen, false);
|
||||
} else {
|
||||
this.handle_left_right(client.currentScreen, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == 0) {
|
||||
// Handles when the player presses the Start button.
|
||||
if (PAUSE_GAME.is_button(button)) {
|
||||
// If in game, then pause the game.
|
||||
if (client.currentScreen == null)
|
||||
client.openPauseMenu(false);
|
||||
else if (client.currentScreen instanceof AbstractContainerScreen) // If the current screen is a container then close it.
|
||||
client.player.closeContainer();
|
||||
else// Else just close the current screen.
|
||||
client.currentScreen.onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) {
|
||||
if (this.action_gui_cooldown == 0) {
|
||||
Element focused = client.currentScreen.getFocused();
|
||||
if (focused != null && is_screen_interactive(client.currentScreen)) {
|
||||
if (this.handle_a_button(client.currentScreen, focused)) {
|
||||
this.action_gui_cooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client.currentScreen instanceof AbstractContainerScreen && client.interactionManager != null && client.player != null) {
|
||||
double pos_x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
|
||||
double pos_y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
|
||||
Slot slot = ((AbstractContainerScreenAccessor) client.currentScreen).get_slot_at(pos_x, pos_y);
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && slot != null) {
|
||||
client.interactionManager.clickSlot(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_1, SlotActionType.PICKUP, client.player);
|
||||
this.action_gui_cooldown = 5;
|
||||
return;
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
|
||||
client.player.closeContainer();
|
||||
return;
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_X && slot != null) {
|
||||
client.interactionManager.clickSlot(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_2, SlotActionType.PICKUP, client.player);
|
||||
return;
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_Y && slot != null) {
|
||||
client.interactionManager.clickSlot(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_1, SlotActionType.QUICK_MOVE, client.player);
|
||||
return;
|
||||
}
|
||||
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
|
||||
if (client.currentScreen != null) {
|
||||
client.currentScreen.onClose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles sneak button.
|
||||
if (SNEAK.is_button(button) && client.player != null) {
|
||||
this.toggle_sneaking(client);
|
||||
}
|
||||
}
|
||||
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !is_screen_interactive(client.currentScreen) && this.action_gui_cooldown == 0 && this.ignore_next_a == 0) {
|
||||
double mouse_x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
|
||||
double mouse_y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
|
||||
if (action == 0) {
|
||||
client.currentScreen.mouseClicked(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1);
|
||||
} else if (action == 1) {
|
||||
client.currentScreen.mouseReleased(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1);
|
||||
}
|
||||
this.action_gui_cooldown = 5;
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.currentScreen == null && action != 2) {
|
||||
ButtonBinding.handle_button(client, button, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_axe(@NotNull MinecraftClient client, int axis, float value, float abs_value, int state)
|
||||
{
|
||||
int as_button_state = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0);
|
||||
|
||||
if (client.currentScreen instanceof LambdaControlsControlsScreen && as_button_state != 0
|
||||
&& !(as_button_state == 2 && (axis == GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER))) {
|
||||
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
|
||||
if (controls_screen.focused_binding != null) {
|
||||
this.config.set_button_binding(controls_screen.focused_binding, axis_as_button(axis, as_button_state == 1));
|
||||
controls_screen.focused_binding = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double dead_zone = this.config.get_dead_zone();
|
||||
if (client.currentScreen == null) {
|
||||
{
|
||||
int axis_minus = axis + 100;
|
||||
boolean current_plus_state = as_button_state == 1;
|
||||
boolean current_minus_state = as_button_state == 2;
|
||||
boolean previous_plus_state = AXIS_STATES.getOrDefault(axis, false);
|
||||
boolean previous_minus_state = AXIS_STATES.getOrDefault(axis_minus, false);
|
||||
|
||||
if (current_plus_state != previous_plus_state) {
|
||||
ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, true), 0);
|
||||
if (current_plus_state)
|
||||
AXIS_COOLDOWNS.put(axis, 5);
|
||||
} else if (current_plus_state) {
|
||||
if (AXIS_COOLDOWNS.getOrDefault(axis, 0) == 0) {
|
||||
AXIS_COOLDOWNS.put(axis, 5);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_minus_state != previous_minus_state) {
|
||||
ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, false), 0);
|
||||
if (current_minus_state)
|
||||
AXIS_COOLDOWNS.put(axis_minus, 5);
|
||||
} else if (current_minus_state) {
|
||||
if (AXIS_COOLDOWNS.getOrDefault(axis_minus, 0) == 0) {
|
||||
AXIS_COOLDOWNS.put(axis_minus, 5);
|
||||
}
|
||||
}
|
||||
|
||||
AXIS_STATES.put(axis, current_plus_state);
|
||||
AXIS_STATES.put(axis_minus, current_minus_state);
|
||||
}
|
||||
|
||||
// Handles the look direction.
|
||||
this.handle_look(client, axis, abs_value, state);
|
||||
} else {
|
||||
boolean allow_mouse_control = true;
|
||||
|
||||
if (this.action_gui_cooldown == 0 && this.config.is_movement_axis(axis) && is_screen_interactive(client.currentScreen)) {
|
||||
if (this.config.is_forward_button(axis, false, as_button_state)) {
|
||||
allow_mouse_control = this.change_focus(client.currentScreen, false);
|
||||
} else if (this.config.is_back_button(axis, false, as_button_state)) {
|
||||
allow_mouse_control = this.change_focus(client.currentScreen, true);
|
||||
} else if (this.config.is_left_button(axis, false, as_button_state)) {
|
||||
allow_mouse_control = this.handle_left_right(client.currentScreen, false);
|
||||
} else if (this.config.is_right_button(axis, false, as_button_state)) {
|
||||
allow_mouse_control = this.handle_left_right(client.currentScreen, true);
|
||||
}
|
||||
}
|
||||
|
||||
float movement_x = 0.0F;
|
||||
float movement_y = 0.0F;
|
||||
|
||||
if (this.config.is_back_button(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movement_y = abs_value;
|
||||
} else if (this.config.is_forward_button(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movement_y = -abs_value;
|
||||
} else if (this.config.is_left_button(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movement_x = -abs_value;
|
||||
} else if (this.config.is_right_button(axis, false, (value > 0 ? 1 : 2))) {
|
||||
movement_x = abs_value;
|
||||
}
|
||||
|
||||
if (client.currentScreen != null && allow_mouse_control) {
|
||||
boolean moving = Math.abs(movement_y) >= dead_zone || Math.abs(movement_x) >= dead_zone;
|
||||
if (moving) {
|
||||
/*
|
||||
Updates the target mouse position when the initial movement stick movement is detected.
|
||||
It prevents the cursor to jump to the old target mouse position if the user moves the cursor with the mouse.
|
||||
*/
|
||||
if (Math.abs(prev_x_axis) < dead_zone && Math.abs(prev_y_axis) < dead_zone) {
|
||||
double mouse_x = client.mouse.getX();
|
||||
double mouse_y = client.mouse.getY();
|
||||
prev_target_mouse_x = target_mouse_x = (int) mouse_x;
|
||||
prev_target_mouse_y = target_mouse_y = (int) mouse_y;
|
||||
}
|
||||
|
||||
if (Math.abs(movement_x) >= dead_zone)
|
||||
this.mouse_speed_x = movement_x;
|
||||
else
|
||||
this.mouse_speed_x = 0.F;
|
||||
|
||||
if (Math.abs(movement_y) >= dead_zone)
|
||||
this.mouse_speed_y = movement_y;
|
||||
else
|
||||
this.mouse_speed_y = 0.F;
|
||||
} else {
|
||||
this.mouse_speed_x = 0.F;
|
||||
this.mouse_speed_y = 0.F;
|
||||
}
|
||||
|
||||
if (Math.abs(this.mouse_speed_x) > .05F || Math.abs(this.mouse_speed_y) > .05F) {
|
||||
this.target_mouse_x += this.mouse_speed_x * this.config.get_mouse_speed();
|
||||
this.target_mouse_x = MathHelper.clamp(this.target_mouse_x, 0, client.getWindow().getWidth());
|
||||
this.target_mouse_y += this.mouse_speed_y * this.config.get_mouse_speed();
|
||||
this.target_mouse_y = MathHelper.clamp(this.target_mouse_y, 0, client.getWindow().getHeight());
|
||||
}
|
||||
|
||||
this.move_mouse_to_closest_slot(client, client.currentScreen);
|
||||
}
|
||||
|
||||
this.prev_x_axis = movement_x;
|
||||
this.prev_y_axis = movement_y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the press on RB on LB.
|
||||
*
|
||||
* @param client The client's instance.
|
||||
* @param right True if RB is pressed, else false.
|
||||
*/
|
||||
private void handle_rb_lb(@NotNull MinecraftClient client, boolean right)
|
||||
{
|
||||
// When ingame
|
||||
if (client.currentScreen == null && client.player != null) {
|
||||
if (right)
|
||||
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1;
|
||||
else
|
||||
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1;
|
||||
} else if (client.currentScreen instanceof CreativeInventoryScreen) {
|
||||
CreativeInventoryScreenAccessor creative_inventory = (CreativeInventoryScreenAccessor) client.currentScreen;
|
||||
int current_selected_tab = creative_inventory.get_selected_tab();
|
||||
int next_tab = current_selected_tab + (right ? 1 : -1);
|
||||
if (next_tab < 0)
|
||||
next_tab = ItemGroup.GROUPS.length - 1;
|
||||
else if (next_tab >= ItemGroup.GROUPS.length)
|
||||
next_tab = 0;
|
||||
creative_inventory.set_selected_tab(ItemGroup.GROUPS[next_tab]);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handle_a_button(@NotNull Screen screen, @NotNull Element focused)
|
||||
{
|
||||
if (focused instanceof AbstractPressableButtonWidget) {
|
||||
AbstractPressableButtonWidget button_widget = (AbstractPressableButtonWidget) focused;
|
||||
button_widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
|
||||
button_widget.onPress();
|
||||
return true;
|
||||
} else if (focused instanceof LabelWidget) {
|
||||
((LabelWidget) focused).on_press();
|
||||
return true;
|
||||
} else if (focused instanceof WorldListWidget) {
|
||||
WorldListWidget list = (WorldListWidget) focused;
|
||||
list.method_20159().ifPresent(WorldListWidget.Entry::play);
|
||||
return true;
|
||||
} else if (focused instanceof MultiplayerServerListWidget) {
|
||||
MultiplayerServerListWidget list = (MultiplayerServerListWidget) focused;
|
||||
MultiplayerServerListWidget.Entry entry = list.getSelected();
|
||||
if (entry instanceof MultiplayerServerListWidget.LanServerEntry || entry instanceof MultiplayerServerListWidget.ServerEntry) {
|
||||
((MultiplayerScreen) screen).select(entry);
|
||||
((MultiplayerScreen) screen).connect();
|
||||
}
|
||||
} else if (focused instanceof ParentElement) {
|
||||
Element child_focused = ((ParentElement) focused).getFocused();
|
||||
if (child_focused != null)
|
||||
return this.handle_a_button(screen, child_focused);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the left and right buttons.
|
||||
*
|
||||
* @param screen The current screen.
|
||||
* @param right True if the right button is pressed, else false.
|
||||
*/
|
||||
private boolean handle_left_right(@NotNull Screen screen, boolean right)
|
||||
{
|
||||
Element focused = screen.getFocused();
|
||||
if (focused != null)
|
||||
if (this.handle_right_left_element(focused, right))
|
||||
return this.change_focus(screen, right);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handle_right_left_element(@NotNull Element element, boolean right)
|
||||
{
|
||||
if (element instanceof SliderWidget) {
|
||||
SliderWidget slider = (SliderWidget) element;
|
||||
slider.keyPressed(right ? 262 : 263, 0, 0);
|
||||
this.action_gui_cooldown = 2; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
|
||||
return false;
|
||||
} else if (element instanceof AlwaysSelectedEntryListWidget) {
|
||||
((EntryListWidgetAccessor) element).move_selection(right ? 1 : -1);
|
||||
return false;
|
||||
} else if (element instanceof ParentElement) {
|
||||
ParentElement entry_list = (ParentElement) element;
|
||||
Element focused = entry_list.getFocused();
|
||||
if (focused == null)
|
||||
return true;
|
||||
return this.handle_right_left_element(focused, right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles whether the player is sneaking.
|
||||
*
|
||||
* @param client The client's instance.
|
||||
*/
|
||||
private void toggle_sneaking(@NotNull MinecraftClient client)
|
||||
{
|
||||
((KeyBindingAccessor) client.options.keySneak).handle_press_state(!client.options.keySneak.isPressed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the look direction input.
|
||||
*
|
||||
* @param client The client isntance.
|
||||
* @param axis The axis to change.
|
||||
* @param value The value of the look.
|
||||
* @param state The state.
|
||||
*/
|
||||
public void handle_look(@NotNull MinecraftClient client, int axis, float value, int state)
|
||||
{
|
||||
// Handles the look direction.
|
||||
if (client.player != null) {
|
||||
double pow_value = Math.pow(value, 2.0);
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||
if (state == 2) {
|
||||
this.target_pitch = client.player.pitch - this.config.get_right_y_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
|
||||
this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D);
|
||||
} else if (state == 1) {
|
||||
this.target_pitch = client.player.pitch + this.config.get_right_y_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
|
||||
this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D);
|
||||
}
|
||||
}
|
||||
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
|
||||
if (state == 2) {
|
||||
this.target_yaw = client.player.yaw - this.config.get_right_x_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
|
||||
} else if (state == 1) {
|
||||
this.target_yaw = client.player.yaw + this.config.get_right_x_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean change_focus(@NotNull Screen screen, boolean down)
|
||||
{
|
||||
if (!screen.changeFocus(down)) {
|
||||
if (screen.changeFocus(down)) {
|
||||
this.action_gui_cooldown = 5;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
this.action_gui_cooldown = 5;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean is_screen_interactive(@NotNull Screen screen)
|
||||
{
|
||||
return !(screen instanceof AdvancementsScreen || screen instanceof AbstractContainerScreen);
|
||||
}
|
||||
|
||||
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
|
||||
private void move_mouse_to_closest_slot(@NotNull MinecraftClient client, @Nullable Screen screen)
|
||||
{
|
||||
// Makes the mouse attracted to slots. This helps with selecting items when using a controller.
|
||||
if (screen instanceof AbstractContainerScreen) {
|
||||
AbstractContainerScreen inventory_screen = (AbstractContainerScreen) screen;
|
||||
AbstractContainerScreenAccessor accessor = (AbstractContainerScreenAccessor) inventory_screen;
|
||||
int gui_left = accessor.get_x();
|
||||
int gui_top = accessor.get_y();
|
||||
int mouse_x = (int) (target_mouse_x * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
|
||||
int mouse_y = (int) (target_mouse_y * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
|
||||
|
||||
// Finds the closest slot in the GUI within 14 pixels.
|
||||
Optional<Pair<Slot, Double>> closest_slot = inventory_screen.getContainer().slotList.parallelStream()
|
||||
.map(slot -> {
|
||||
int pos_x = gui_left + slot.xPosition + 8;
|
||||
int pos_y = gui_top + slot.yPosition + 8;
|
||||
|
||||
// Distance between the slot and the cursor.
|
||||
double distance = Math.sqrt(Math.pow(pos_x - mouse_x, 2) + Math.pow(pos_y - mouse_y, 2));
|
||||
return Pair.of(slot, distance);
|
||||
}).filter(entry -> entry.get_value() <= 14.0)
|
||||
.min(Comparator.comparingDouble(Pair::get_value));
|
||||
|
||||
if (closest_slot.isPresent()) {
|
||||
Slot slot = closest_slot.get().get_key();
|
||||
if (slot.hasStack() || !client.player.inventory.getMainHandStack().isEmpty()) {
|
||||
int slot_center_x_scaled = gui_left + slot.xPosition + 8;
|
||||
int slot_center_y_scaled = gui_top + slot.yPosition + 8;
|
||||
int slot_center_x = (int) (slot_center_x_scaled / ((double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));
|
||||
int slot_center_y = (int) (slot_center_y_scaled / ((double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()));
|
||||
double delta_x = slot_center_x - target_mouse_x;
|
||||
double delta_y = slot_center_y - target_mouse_y;
|
||||
|
||||
if (mouse_x != slot_center_x_scaled || mouse_y != slot_center_y_scaled) {
|
||||
this.target_mouse_x += delta_x * 0.75;
|
||||
this.target_mouse_y += delta_y * 0.75;
|
||||
} else {
|
||||
this.mouse_speed_x *= 0.3F;
|
||||
this.mouse_speed_y *= 0.3F;
|
||||
}
|
||||
this.mouse_speed_x *= .75F;
|
||||
this.mouse_speed_y *= .75F;
|
||||
} else {
|
||||
this.mouse_speed_x *= .1F;
|
||||
this.mouse_speed_y *= .1F;
|
||||
}
|
||||
} else {
|
||||
this.mouse_speed_x *= .3F;
|
||||
this.mouse_speed_y *= .3F;
|
||||
}
|
||||
} else {
|
||||
this.mouse_speed_x = 0.F;
|
||||
this.mouse_speed_y = 0.F;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a button with an icon instead of text.
|
||||
*/
|
||||
public abstract class AbstractIconButtonWidget extends ButtonWidget
|
||||
{
|
||||
private int icon_size = 0;
|
||||
|
||||
public AbstractIconButtonWidget(int x, int y, int width, int height, @NotNull String message, @NotNull PressAction on_press)
|
||||
{
|
||||
super(x, y, width, height, message, on_press);
|
||||
}
|
||||
|
||||
protected abstract int render_icon(int mouse_x, int mouse_y, float delta, int x, int y);
|
||||
|
||||
@Override
|
||||
public void renderButton(int mouse_x, int mouse_y, float delta)
|
||||
{
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
client.getTextureManager().bindTexture(WIDGETS_LOCATION);
|
||||
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
|
||||
int i = this.getYImage(this.isHovered());
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
|
||||
this.blit(this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
|
||||
this.blit(this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
|
||||
this.renderBg(client, mouse_x, mouse_y);
|
||||
|
||||
this.icon_size = this.render_icon(mouse_x, mouse_y, delta, this.x + 4, this.y + (this.height / 2 - this.icon_size / 2));
|
||||
|
||||
if (!this.getMessage().isEmpty()) {
|
||||
int j = this.active ? 16777215 : 10526880;
|
||||
this.drawCenteredString(client.textRenderer, this.getMessage(), this.x + 8 + this.icon_size + (this.width - 8 - this.icon_size - 6) / 2,
|
||||
this.y + (this.height - 8) / 2, j | MathHelper.ceil(this.alpha * 255.0F) << 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.ButtonBinding;
|
||||
import me.lambdaurora.lambdacontrols.LambdaControls;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a controller button widget.
|
||||
*/
|
||||
public class ControllerButtonWidget extends AbstractIconButtonWidget
|
||||
{
|
||||
private ButtonBinding binding;
|
||||
|
||||
public ControllerButtonWidget(int x, int y, int width, @NotNull ButtonBinding button_binding, @NotNull PressAction on_press)
|
||||
{
|
||||
super(x, y, width, 20, ButtonBinding.get_localized_button_name(button_binding.get_button()), on_press);
|
||||
this.binding = button_binding;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
this.setMessage(ButtonBinding.get_localized_button_name(binding.get_button()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int render_icon(int mouse_x, int mouse_y, float delta, int x, int y)
|
||||
{
|
||||
return LambdaControls.draw_button(x, y, this.binding, MinecraftClient.getInstance());
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.ButtonBinding;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.ElementListWidget;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a control list widget.
|
||||
*/
|
||||
public class ControlsListWidget extends ElementListWidget<ControlsListWidget.Entry>
|
||||
{
|
||||
private final LambdaControlsControlsScreen gui;
|
||||
private int field_2733;
|
||||
|
||||
public ControlsListWidget(@NotNull LambdaControlsControlsScreen gui, @NotNull MinecraftClient client)
|
||||
{
|
||||
super(client, gui.width + 45, gui.height, 43, gui.height - 32, 24);
|
||||
this.gui = gui;
|
||||
|
||||
ButtonBinding.stream_categories()
|
||||
.sorted(Comparator.comparingInt(ButtonBinding.Category::get_priority))
|
||||
.forEach(category -> {
|
||||
this.addEntry(new CategoryEntry(category));
|
||||
|
||||
category.get_bindings().forEach(binding -> {
|
||||
int i = client.textRenderer.getStringWidth(I18n.translate(binding.get_translation_key()));
|
||||
if (i > this.field_2733) {
|
||||
this.field_2733 = i;
|
||||
}
|
||||
|
||||
this.addEntry(new ControlsListWidget.ButtonBindingEntry(binding));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getScrollbarPosition()
|
||||
{
|
||||
return super.getScrollbarPosition() + 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowWidth()
|
||||
{
|
||||
return super.getRowWidth() + 32;
|
||||
}
|
||||
|
||||
public class ButtonBindingEntry extends Entry
|
||||
{
|
||||
private final ButtonBinding binding;
|
||||
private final String binding_name;
|
||||
private final ControllerButtonWidget edit_button;
|
||||
private final ButtonWidget reset_button;
|
||||
|
||||
ButtonBindingEntry(@NotNull ButtonBinding binding)
|
||||
{
|
||||
this.binding = binding;
|
||||
this.binding_name = I18n.translate(this.binding.get_translation_key());
|
||||
this.edit_button = new ControllerButtonWidget(0, 0, 90, this.binding, btn -> gui.focused_binding = binding)
|
||||
{
|
||||
protected String getNarrationMessage()
|
||||
{
|
||||
return binding.is_not_bound() ? I18n.translate("narrator.controls.unbound", binding_name) : I18n.translate("narrator.controls.bound", binding_name, super.getNarrationMessage());
|
||||
}
|
||||
};
|
||||
this.reset_button = new ButtonWidget(0, 0, 50, 20, I18n.translate("controls.reset"),
|
||||
btn -> gui.mod.config.set_button_binding(binding, binding.get_default_button()))
|
||||
{
|
||||
protected String getNarrationMessage()
|
||||
{
|
||||
return I18n.translate("narrator.controls.reset", binding_name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Element> children()
|
||||
{
|
||||
return Collections.unmodifiableList(Arrays.asList(this.edit_button, this.reset_button));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int index, int y, int x, int width, int height, int mouse_x, int mouse_y, boolean hovering, float delta)
|
||||
{
|
||||
boolean focused = gui.focused_binding == this.binding;
|
||||
TextRenderer text_renderer = ControlsListWidget.this.minecraft.textRenderer;
|
||||
String binding_name = this.binding_name;
|
||||
float var10002 = (float) (x + 70 - ControlsListWidget.this.field_2733);
|
||||
int var10003 = y + height / 2;
|
||||
text_renderer.draw(binding_name, var10002, (float) (var10003 - 9 / 2), 16777215);
|
||||
this.reset_button.x = x + 190;
|
||||
this.reset_button.y = y;
|
||||
this.reset_button.active = !this.binding.is_default();
|
||||
this.reset_button.render(mouse_x, mouse_y, delta);
|
||||
this.edit_button.x = x + 85;
|
||||
this.edit_button.y = y;
|
||||
this.edit_button.update();
|
||||
|
||||
if (focused) {
|
||||
this.edit_button.setMessage(Formatting.WHITE + "> " + Formatting.YELLOW + this.edit_button.getMessage() + Formatting.WHITE + " <");
|
||||
} else if (!this.binding.is_not_bound() && ButtonBinding.has_duplicates(this.binding.get_button())) {
|
||||
this.edit_button.setMessage(Formatting.RED + this.edit_button.getMessage());
|
||||
} else if (this.binding.is_not_bound()) {
|
||||
this.edit_button.setMessage(Formatting.GOLD + edit_button.getMessage());
|
||||
}
|
||||
|
||||
this.edit_button.render(mouse_x, mouse_y, delta);
|
||||
}
|
||||
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button)
|
||||
{
|
||||
if (this.edit_button.mouseClicked(mouseX, mouseY, button))
|
||||
return true;
|
||||
else
|
||||
return this.reset_button.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button)
|
||||
{
|
||||
return this.edit_button.mouseReleased(mouseX, mouseY, button) || this.reset_button.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
}
|
||||
|
||||
public class CategoryEntry extends Entry
|
||||
{
|
||||
private final String name;
|
||||
private final int name_width;
|
||||
|
||||
public CategoryEntry(@NotNull ButtonBinding.Category category)
|
||||
{
|
||||
this.name = category.get_translated_name();
|
||||
this.name_width = ControlsListWidget.this.minecraft.textRenderer.getStringWidth(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int index, int y, int x, int width, int height, int mouse_x, int mouse_y, boolean hovering, float delta)
|
||||
{
|
||||
ControlsListWidget.this.minecraft.textRenderer.draw(this.name, (float) (ControlsListWidget.this.minecraft.currentScreen.width / 2 - this.name_width / 2),
|
||||
(float) ((y + height) - 9 - 1), 16777215);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeFocus(boolean bl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Element> children()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public abstract static class Entry extends ElementListWidget.Entry<Entry>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.Drawable;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.render.Tessellator;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.util.math.Matrix4f;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents a label widget.
|
||||
*/
|
||||
// @TODO move this to a GUI library.
|
||||
public class LabelWidget extends DrawableHelper implements Element, Drawable
|
||||
{
|
||||
public static final Consumer<LabelWidget> DEFAULT_ACTION = label -> {
|
||||
};
|
||||
|
||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||
private final Consumer<LabelWidget> press_action;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int max_width;
|
||||
//private final int max_height;
|
||||
private String text;
|
||||
private String tooltip_text;
|
||||
public boolean visible;
|
||||
private int width;
|
||||
private int height;
|
||||
private boolean centered;
|
||||
protected boolean hovered;
|
||||
protected boolean focused;
|
||||
|
||||
public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer<LabelWidget> press_action, boolean centered)
|
||||
{
|
||||
this.visible = true;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.max_width = max_width;
|
||||
this.press_action = press_action;
|
||||
this.centered = centered;
|
||||
this.set_text(text);
|
||||
}
|
||||
|
||||
public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer<LabelWidget> press_action)
|
||||
{
|
||||
this(x, y, text, max_width, press_action, false);
|
||||
}
|
||||
|
||||
public LabelWidget(int x, int y, @NotNull String text, int max_width, boolean centered)
|
||||
{
|
||||
this(x, y, text, max_width, DEFAULT_ACTION, centered);
|
||||
}
|
||||
|
||||
public LabelWidget(int x, int y, @NotNull String text, int max_width)
|
||||
{
|
||||
this(x, y, text, max_width, DEFAULT_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of this label.
|
||||
*
|
||||
* @param text The text to set.
|
||||
*/
|
||||
public void set_text(@NotNull String text)
|
||||
{
|
||||
int width = this.client.textRenderer.getStringWidth(text);
|
||||
while (width > this.max_width) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
width = this.client.textRenderer.getStringWidth(text);
|
||||
}
|
||||
|
||||
this.text = text;
|
||||
this.width = width;
|
||||
this.height = this.client.textRenderer.fontHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip text of this label.
|
||||
*
|
||||
* @param text The tooltip text.
|
||||
*/
|
||||
public void set_tooltip_text(String text)
|
||||
{
|
||||
this.tooltip_text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of this label widget.
|
||||
*
|
||||
* @return The width of this label widget.
|
||||
*/
|
||||
public int get_width()
|
||||
{
|
||||
return this.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of this label widget.
|
||||
*
|
||||
* @return The height of this label widget.
|
||||
*/
|
||||
public int get_height()
|
||||
{
|
||||
return this.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the press event on this label widget.
|
||||
*/
|
||||
public void on_press()
|
||||
{
|
||||
this.press_action.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouse_x, int mouse_y, float delta)
|
||||
{
|
||||
if (this.visible) {
|
||||
int x = this.centered ? this.x - this.client.textRenderer.getStringWidth(this.text) / 2 : this.x;
|
||||
this.hovered = mouse_x >= x && mouse_y >= this.y && mouse_x < x + this.width && mouse_y < this.y + this.height;
|
||||
this.drawString(this.client.textRenderer, this.text, x, this.y, 10526880);
|
||||
|
||||
if (this.tooltip_text != null && !this.tooltip_text.isEmpty()) {
|
||||
List<String> wrapped_tooltip_text = this.client.textRenderer.wrapStringToWidthAsList(this.tooltip_text, Math.max(this.width / 2, 200));
|
||||
if (this.hovered)
|
||||
this.render_tooltip(wrapped_tooltip_text, mouse_x, mouse_y);
|
||||
else if (this.focused)
|
||||
this.render_tooltip(wrapped_tooltip_text, this.x - 12, this.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouse_x, double mouse_y, int button)
|
||||
{
|
||||
if (this.visible && button == GLFW.GLFW_MOUSE_BUTTON_1) {
|
||||
if (this.hovered) {
|
||||
this.on_press();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeFocus(boolean down)
|
||||
{
|
||||
if (this.visible) {
|
||||
this.focused = !this.focused;
|
||||
return this.focused;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void render_tooltip(List<String> text, int x, int y)
|
||||
{
|
||||
if (!text.isEmpty()) {
|
||||
RenderSystem.disableRescaleNormal();
|
||||
RenderSystem.disableDepthTest();
|
||||
int i = 0;
|
||||
|
||||
for (String string : text) {
|
||||
int j = this.client.textRenderer.getStringWidth(string);
|
||||
if (j > i) {
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
int k = x + 12;
|
||||
int l = y - 12;
|
||||
int n = 8;
|
||||
if (text.size() > 1) {
|
||||
n += 2 + (text.size() - 1) * 10;
|
||||
}
|
||||
|
||||
if (k + i > this.client.getWindow().getScaledWidth()) {
|
||||
k -= 28 + i;
|
||||
}
|
||||
|
||||
if (l + n + 6 > this.client.getWindow().getScaledHeight()) {
|
||||
l = this.client.getWindow().getScaledHeight() - n - 6;
|
||||
}
|
||||
|
||||
this.setBlitOffset(300);
|
||||
this.client.getItemRenderer().zOffset = 300.0F;
|
||||
this.fillGradient(k - 3, l - 4, k + i + 3, l - 3, -267386864, -267386864);
|
||||
this.fillGradient(k - 3, l + n + 3, k + i + 3, l + n + 4, -267386864, -267386864);
|
||||
this.fillGradient(k - 3, l - 3, k + i + 3, l + n + 3, -267386864, -267386864);
|
||||
this.fillGradient(k - 4, l - 3, k - 3, l + n + 3, -267386864, -267386864);
|
||||
this.fillGradient(k + i + 3, l - 3, k + i + 4, l + n + 3, -267386864, -267386864);
|
||||
this.fillGradient(k - 3, l - 3 + 1, k - 3 + 1, l + n + 3 - 1, 1347420415, 1344798847);
|
||||
this.fillGradient(k + i + 2, l - 3 + 1, k + i + 3, l + n + 3 - 1, 1347420415, 1344798847);
|
||||
this.fillGradient(k - 3, l - 3, k + i + 3, l - 3 + 1, 1347420415, 1347420415);
|
||||
this.fillGradient(k - 3, l + n + 2, k + i + 3, l + n + 3, 1344798847, 1344798847);
|
||||
MatrixStack matrix_stack = new MatrixStack();
|
||||
VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
|
||||
matrix_stack.translate(0.0D, 0.0D, this.client.getItemRenderer().zOffset);
|
||||
Matrix4f matrix4f = matrix_stack.peek().getModel();
|
||||
|
||||
for (int r = 0; r < text.size(); ++r) {
|
||||
String string2 = text.get(r);
|
||||
if (string2 != null) {
|
||||
this.client.textRenderer.draw(string2, (float) k, (float) l, -1, true, matrix4f, immediate, false, 0, 15728880);
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
l += 2;
|
||||
}
|
||||
|
||||
l += 10;
|
||||
}
|
||||
|
||||
immediate.draw();
|
||||
this.setBlitOffset(0);
|
||||
this.client.getItemRenderer().zOffset = 0.0F;
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.enableRescaleNormal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.ButtonBinding;
|
||||
import me.lambdaurora.lambdacontrols.LambdaControls;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.options.BooleanOption;
|
||||
import net.minecraft.client.options.Option;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Represents the controls screen.
|
||||
*/
|
||||
public class LambdaControlsControlsScreen extends Screen
|
||||
{
|
||||
private final LambdaControlsSettingsScreen parent;
|
||||
final LambdaControls mod;
|
||||
private final Option inverts_right_x_axis;
|
||||
private final Option inverts_right_y_axis;
|
||||
private ControlsListWidget bindings_list_widget;
|
||||
private ButtonWidget reset_button;
|
||||
public ButtonBinding focused_binding;
|
||||
|
||||
protected LambdaControlsControlsScreen(@NotNull LambdaControlsSettingsScreen parent)
|
||||
{
|
||||
super(new TranslatableText("lambdacontrols.menu.title.controller_controls"));
|
||||
this.parent = parent;
|
||||
this.mod = parent.mod;
|
||||
this.inverts_right_x_axis = new BooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(),
|
||||
(game_options, new_value) -> {
|
||||
synchronized (this.mod.config) {
|
||||
this.mod.config.set_invert_right_x_axis(new_value);
|
||||
}
|
||||
});
|
||||
this.inverts_right_y_axis = new BooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(),
|
||||
(game_options, new_value) -> {
|
||||
synchronized (this.mod.config) {
|
||||
this.mod.config.set_invert_right_y_axis(new_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed()
|
||||
{
|
||||
this.mod.config.save();
|
||||
super.removed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init()
|
||||
{
|
||||
this.addButton(this.inverts_right_x_axis.createButton(this.minecraft.options, this.width / 2 - 155, 18, 150));
|
||||
this.addButton(this.inverts_right_y_axis.createButton(this.minecraft.options, this.width / 2 - 155 + 160, 18, 150));
|
||||
this.bindings_list_widget = new ControlsListWidget(this, this.minecraft);
|
||||
this.children.add(this.bindings_list_widget);
|
||||
this.reset_button = this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 150, 20, I18n.translate("controls.resetAll"),
|
||||
btn -> ButtonBinding.stream().forEach(binding -> this.mod.config.set_button_binding(binding, binding.get_default_button()))));
|
||||
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, 20, I18n.translate("gui.done"),
|
||||
btn -> this.minecraft.openScreen(this.parent)));
|
||||
}
|
||||
|
||||
// Replacement for Predicate#not as it is Java 11.
|
||||
private <T> Predicate<T> not(Predicate<T> target)
|
||||
{
|
||||
Objects.requireNonNull(target);
|
||||
return target.negate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouse_x, int mouse_y, float delta)
|
||||
{
|
||||
this.renderBackground();
|
||||
this.bindings_list_widget.render(mouse_x, mouse_y, delta);
|
||||
this.drawCenteredString(this.font, this.title.asFormattedString(), this.width / 2, 8, 16777215);
|
||||
this.reset_button.active = ButtonBinding.stream().anyMatch(this.not(ButtonBinding::is_default));
|
||||
super.render(mouse_x, mouse_y, delta);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user