mirror of
https://github.com/TeamMidnightDust/MidnightControls.git
synced 2025-12-13 23:25:10 +01:00
Compare commits
254 Commits
1.4.0
...
architectu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf60595c12 | ||
|
|
b96547dafb | ||
|
|
ab869f4f76 | ||
|
|
84df412162 | ||
|
|
afb80fd89c | ||
|
|
b835a6c4ca | ||
|
|
3eec0dda4a | ||
|
|
a23e8b8975 | ||
|
|
1aa449b5bd | ||
|
|
35ab81f696 | ||
|
|
e08547a641 | ||
|
|
c93cc729f4 | ||
|
|
1595fffc2c | ||
|
|
953df7a8c4 | ||
|
|
a3b8d7cbb5 | ||
|
|
929639c081 | ||
|
|
abcfd4c98c | ||
|
|
24d169b4c2 | ||
|
|
c0af00f2a3 | ||
|
|
333fc7f300 | ||
|
|
dbb6e926e6 | ||
|
|
78ada44d73 | ||
|
|
a55e506af0 | ||
|
|
540920009f | ||
|
|
044bbfe9ed | ||
|
|
f782e17349 | ||
|
|
abcbf20eaf | ||
|
|
86c96c8a3e | ||
|
|
310af56162 | ||
|
|
cd416cf022 | ||
|
|
78df229dd2 | ||
|
|
9bcc63fcef | ||
|
|
202ff888be | ||
|
|
da467619cb | ||
|
|
0d2bd6e4cb | ||
|
|
241228dcde | ||
|
|
45149af859 | ||
|
|
bdb3c36518 | ||
|
|
f82ee2e24a | ||
|
|
27221b62cd | ||
|
|
9e3b2ae060 | ||
|
|
838fab2c63 | ||
|
|
2cbe02ebb2 | ||
|
|
ba74d98109 | ||
|
|
d3a787090e | ||
|
|
00780e13fd | ||
|
|
a5554ee21d | ||
|
|
728a2d4603 | ||
|
|
eb54245c21 | ||
|
|
e6fcd1469c | ||
|
|
8a30b5ee73 | ||
|
|
43ffd89a61 | ||
|
|
8a9866f92f | ||
|
|
b12413cbcd | ||
|
|
9f62538197 | ||
|
|
ba5143403d | ||
|
|
454afa92ec | ||
|
|
8e05b658c2 | ||
|
|
cba8a4e1d4 | ||
|
|
dd0795bc56 | ||
|
|
566c26ac17 | ||
|
|
e203e26c3f | ||
|
|
797d1463a6 | ||
|
|
3e665a14fd | ||
|
|
2b143c6bf1 | ||
|
|
ab317f3d31 | ||
|
|
2c11a23914 | ||
|
|
6df3e4454b | ||
|
|
afee0b407d | ||
|
|
d32aefccac | ||
|
|
98797b03ea | ||
|
|
e1d53ee463 | ||
|
|
233ae36343 | ||
|
|
eb4f30913a | ||
|
|
1186c9c241 | ||
|
|
de7cf8a307 | ||
|
|
15510b5997 | ||
|
|
cb56632ec4 | ||
|
|
9d11d08807 | ||
|
|
54f731c7f0 | ||
|
|
3e3ab8743d | ||
|
|
ae3d6cc846 | ||
|
|
00c68004e8 | ||
|
|
f0f6a443bf | ||
|
|
d8ff51b3d3 | ||
|
|
0315784a47 | ||
|
|
3a4d23fb91 | ||
|
|
8bce9170a2 | ||
|
|
ec0c334d4a | ||
|
|
b4209785e0 | ||
|
|
fa3d56eb25 | ||
|
|
60bd283c4c | ||
|
|
889a5d8d58 | ||
|
|
febf171b88 | ||
|
|
d133fed2f6 | ||
|
|
9a61a13fb3 | ||
|
|
e97223e316 | ||
|
|
f16f7416ea | ||
|
|
c086ecfcef | ||
|
|
66cdca30fe | ||
|
|
6e49667ada | ||
|
|
d4c3208cee | ||
|
|
35a4fbee1b | ||
|
|
2aac2ad4e7 | ||
|
|
4b2919bfca | ||
|
|
1c999137ba | ||
|
|
d48952fec9 | ||
|
|
9deab7228d | ||
|
|
b4d82a7230 | ||
|
|
36e241e438 | ||
|
|
bdbee99fe2 | ||
|
|
439cd0858e | ||
|
|
617648cf57 | ||
|
|
89914a1b6e | ||
|
|
c272fb457f | ||
|
|
308b41094f | ||
|
|
bf7209083a | ||
|
|
187844db2b | ||
|
|
24ced76d06 | ||
|
|
aa75a58a4d | ||
|
|
1f675622f0 | ||
|
|
2d902bbf3c | ||
|
|
9cc5393703 | ||
|
|
8d08fdedf4 | ||
|
|
cdafde01f3 | ||
|
|
c07c4200f7 | ||
|
|
c70da21df6 | ||
|
|
f5d5d93c19 | ||
|
|
429b4ca607 | ||
|
|
dd173836ff | ||
|
|
203fd6cdb6 | ||
|
|
003f79d405 | ||
|
|
f48527402a | ||
|
|
8c4b705e65 | ||
|
|
1def693ae8 | ||
|
|
cfd05bb7b6 | ||
|
|
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 |
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: 'Bug: TITLE HERE'
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
@@ -21,15 +21,17 @@ Steps to reproduce the behavior:
|
|||||||
A clear and concise description of what you expected to happen.
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots or videos to help explain your problem.
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. Linux]
|
- OS: [e.g. Windows / Linux / MacOS]
|
||||||
- Minecraft [e.g. 1.14.4]
|
- Minecraft [e.g. 1.20.4]
|
||||||
- Fabric [e.g. fabric 0.7.2+build.174]
|
- Modloader [e.g. Fabric Loader 0.15.6]
|
||||||
- Mods [e.g. aurora_keystrokes v1.0.0, modmenu v1.7.15]
|
- Fabric/Quilt Libraries [e.g. Fabric Api 0.96.1+1.20.4]
|
||||||
|
- Mods [e.g. Puzzle v1.6.1, LilTaterReloaded v1.1.15]
|
||||||
- Version [e.g. 1.0.0]
|
- Version [e.g. 1.0.0]
|
||||||
- Branch [e.g. dev]
|
- Remove this line if you actually completed it
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
In case of a crash, please provide the crash log.
|
||||||
|
|||||||
40
.github/ISSUE_TEMPLATE/controller_support.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/controller_support.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: Controller support
|
||||||
|
about: Report a problem related to a specific controller
|
||||||
|
title: 'Controller Issues: CONTROLLER NAME HERE'
|
||||||
|
labels: controller
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the current state**
|
||||||
|
A clear and concise description of current state of support for the controller and the issues.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
If needed, specify steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots or videos to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- Have you checked #263 for your controller? [e.g. Yes/No]
|
||||||
|
- Connection method [e.g. Wired / Bluetooth]
|
||||||
|
|
||||||
|
- OS: [e.g. Windows / Linux / MacOS]
|
||||||
|
- Minecraft [e.g. 1.20.4]
|
||||||
|
- Modloader [e.g. Fabric Loader 0.15.6]
|
||||||
|
- Fabric/Quilt Libraries [e.g. Fabric Api 0.96.1+1.20.4]
|
||||||
|
- Mods [e.g. Puzzle v1.6.1, LilTaterReloaded v1.1.15]
|
||||||
|
- Version [e.g. 1.0.0]
|
||||||
|
- Remove this line if you actually completed it
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
|
In case of a crash, please provide the crash log.
|
||||||
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: 'Feature: TITLE HERE'
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
@@ -17,4 +17,4 @@ A clear and concise description of what you want to happen.
|
|||||||
A clear and concise description of any alternative solutions or features you've considered.
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
Add any other context or screenshots/videos about the feature request here.
|
||||||
|
|||||||
20
.github/ISSUE_TEMPLATE/mod_support.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/mod_support.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Mod Support
|
||||||
|
about: Compatibility improvements with a specific mod (not for crashes)
|
||||||
|
title: 'Mod Support: MOD NAME HERE'
|
||||||
|
labels: mod compatibility
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your compatibility request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I've always wanted to [...]
|
||||||
|
|
||||||
|
**Describe the way of compatibility you'd imagine**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative mods or workarounds you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots/videos about the compatibility request here.
|
||||||
25
.github/workflows/build.yml
vendored
Normal file
25
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Build with Gradle
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Validate Gradle Wrapper
|
||||||
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 21
|
||||||
|
check-latest: true
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build
|
||||||
|
- name: Upload artifacts to GitHub
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: MidnightControls-Artifacts
|
||||||
|
path: /build/libs/
|
||||||
38
.github/workflows/publish.yml
vendored
Normal file
38
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Publish to Modrinth
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Validate Gradle Wrapper
|
||||||
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 21
|
||||||
|
check-latest: true
|
||||||
|
# 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 }}
|
||||||
|
- name: Publish to Modrinth
|
||||||
|
env:
|
||||||
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
|
run: ./gradlew publishModrinth
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# LambdAurora's ignore file
|
# LambdAurora's ignore file
|
||||||
#
|
#
|
||||||
# v0.13
|
# v0.15
|
||||||
|
|
||||||
# JetBrains
|
# JetBrains
|
||||||
.idea/
|
.idea/
|
||||||
@@ -53,6 +53,8 @@ __pycache__/
|
|||||||
# OS
|
# OS
|
||||||
## Windows
|
## Windows
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
# MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# File types
|
# File types
|
||||||
*.dll
|
*.dll
|
||||||
@@ -61,10 +63,13 @@ desktop.ini
|
|||||||
*.lib
|
*.lib
|
||||||
lib*.a
|
lib*.a
|
||||||
*.png~
|
*.png~
|
||||||
|
*.tar.?z
|
||||||
|
|
||||||
# Common
|
# Common
|
||||||
bin/
|
bin/
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
lib/
|
||||||
|
obj/
|
||||||
run/
|
run/
|
||||||
target/
|
target/
|
||||||
|
|||||||
78
CHANGELOG.md
78
CHANGELOG.md
@@ -1,6 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v1.0.0
|
# 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:
|
:tada: First release! :tada:
|
||||||
|
|
||||||
@@ -12,11 +28,11 @@
|
|||||||
- Added key bindings for look around.
|
- Added key bindings for look around.
|
||||||
- And more!
|
- And more!
|
||||||
|
|
||||||
### v1.0.1
|
### 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))
|
- Fixed tutorial toast to look around not affected by camera movement done with a controller. ([#2](https://github.com/LambdAurora/LambdaControls/issues/2))
|
||||||
|
|
||||||
### v1.0.2 (Unofficial)
|
### 1.0.2 (Unofficial)
|
||||||
|
|
||||||
This update was never pushed but was aiming to fix [#4](https://github.com/LambdAurora/LambdaControls/issues/4).
|
This update was never pushed but was aiming to fix [#4](https://github.com/LambdAurora/LambdaControls/issues/4).
|
||||||
|
|
||||||
@@ -24,7 +40,7 @@ This update was never pushed but was aiming to fix [#4](https://github.com/Lambd
|
|||||||
- Fixed broken chat arrow keys.
|
- Fixed broken chat arrow keys.
|
||||||
- Optimized a little bit the button indicator. (need more work)
|
- Optimized a little bit the button indicator. (need more work)
|
||||||
|
|
||||||
## v1.1.0 - Chording update
|
## 1.1.0 - Chording update
|
||||||
|
|
||||||
This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAurora/LambdaControls/issues/9)).
|
This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAurora/LambdaControls/issues/9)).
|
||||||
|
|
||||||
@@ -46,9 +62,9 @@ This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAur
|
|||||||
- HUD side affects button indicators now.
|
- HUD side affects button indicators now.
|
||||||
- Added support for Advancements tabs.
|
- Added support for Advancements tabs.
|
||||||
|
|
||||||
### v1.1.1
|
### 1.1.1
|
||||||
|
|
||||||
## v1.2.0-1.3.0
|
## 1.2.0-1.3.0
|
||||||
|
|
||||||
- Improved rotation algorithm ([#11](https://github.com/LambdAurora/LambdaControls/issues/11)).
|
- Improved rotation algorithm ([#11](https://github.com/LambdAurora/LambdaControls/issues/11)).
|
||||||
- Added virtual mouse.
|
- Added virtual mouse.
|
||||||
@@ -60,21 +76,21 @@ This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAur
|
|||||||
- And more!
|
- And more!
|
||||||
- v1.3.0 specific: Updated to Minecraft 1.16.1
|
- v1.3.0 specific: Updated to Minecraft 1.16.1
|
||||||
|
|
||||||
### v1.3.1
|
### 1.3.1
|
||||||
|
|
||||||
- Fixed broken inventory interactions ([#13](https://github.com/LambdAurora/LambdaControls/issues/13))
|
- Fixed broken inventory interactions ([#13](https://github.com/LambdAurora/LambdaControls/issues/13))
|
||||||
- Fixed virtual mouse preventing continuous attack (thus making breaking blocks impossible).
|
- Fixed virtual mouse preventing continuous attack (thus making breaking blocks impossible).
|
||||||
- Added support for [ModUpdater](https://gitea.thebrokenrail.com/TheBrokenRail/ModUpdater) hopefully.
|
- Added support for [ModUpdater](https://gitea.thebrokenrail.com/TheBrokenRail/ModUpdater) hopefully.
|
||||||
- Updated [SpruceUI](https://github.com/LambdAurora/SpruceUI) to 1.5.2.
|
- Updated [SpruceUI](https://github.com/LambdAurora/SpruceUI) to 1.5.2.
|
||||||
|
|
||||||
### v1.3.2
|
### 1.3.2
|
||||||
|
|
||||||
- Added vertical reacharound.
|
- Added vertical reacharound.
|
||||||
- Added more API for compatibility handlers.
|
- Added more API for compatibility handlers.
|
||||||
- Improved reacharound API.
|
- Improved reacharound API.
|
||||||
- Improved [REI](https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items) compatibility.
|
- Improved [REI](https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items) compatibility.
|
||||||
|
|
||||||
## v1.4.0
|
## 1.4.0
|
||||||
|
|
||||||
- Added analog movements ([#10](https://github.com/LambdAurora/LambdaControls/issues/10)).
|
- Added analog movements ([#10](https://github.com/LambdAurora/LambdaControls/issues/10)).
|
||||||
- Improved Ok Zoomer compability.
|
- Improved Ok Zoomer compability.
|
||||||
@@ -89,3 +105,47 @@ This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAur
|
|||||||
- Started to work on action ring.
|
- Started to work on action ring.
|
||||||
- Will allow for better compability with other mods.
|
- Will allow for better compability with other mods.
|
||||||
- Might be interesting for keyboard users too.
|
- 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:
|
: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.
|
Feel free to propose changes to this document in a pull request.
|
||||||
|
|
||||||
**Table of Contents**
|
**Table of Contents**
|
||||||
@@ -17,8 +17,8 @@ Feel free to propose changes to this document in a pull request.
|
|||||||
|
|
||||||
## Code of Conduct
|
## 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).
|
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 [aurora42lambda@gmail.com](mailto:aurora42lambda@gmail.com).
|
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?
|
## 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.
|
[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.
|
Knowing how to code in Java is necessary if you contribute to the code.
|
||||||
|
|
||||||
### Minecraft
|
### Minecraft
|
||||||
@@ -45,9 +45,9 @@ As it is a Minecraft mod you should know a bit how Minecraft works and how moddi
|
|||||||
|
|
||||||
### Git
|
### 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?
|
## How can I contribute?
|
||||||
|
|
||||||
@@ -55,21 +55,21 @@ Git commits should be and must be signed.
|
|||||||
|
|
||||||
#### Before submitting a bug report
|
#### 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.
|
- 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?
|
#### 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
|
### Suggesting enhancements
|
||||||
|
|
||||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/LambdAurora/LambdaControls/issues).
|
Enhancement suggestions are tracked as [GitHub issues](https://github.com/TeamMidnightDust/MidnightControls/issues).
|
||||||
Check out the [feature request](https://github.com/LambdAurora/LambdaControls/blob/master/.github/ISSUE_TEMPLATE/feature_request.md) guide.
|
Check out the [feature request](https://github.com/TeamMidnightDust/MidnightControls/blob/1.17/.github/ISSUE_TEMPLATE/feature_request.md) guide.
|
||||||
|
|
||||||
### Do pull requests
|
### 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...
|
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)!
|
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
|
### Git commit messages
|
||||||
|
|
||||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
* 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.
|
* (Not for the message) Don't forget to sign the commit.
|
||||||
|
|
||||||
### Naming convention
|
### 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.
|
`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
|
### Brace placement
|
||||||
|
|
||||||
Every braces should be at the end of the line of function declaration, etc...
|
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.
|
|
||||||
|
|
||||||
### Quick note for users of the Intellij IDEA IDE
|
### 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.
|
||||||
3
LICENSE
3
LICENSE
@@ -1,6 +1,7 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright © 2020 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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
86
README.md
86
README.md
@@ -1,45 +1,73 @@
|
|||||||
# LambdaControls
|
<div align="center">
|
||||||
|
<img src=https://github.com/TeamMidnightDust/MidnightControls/blob/1.19/src/main/resources/assets/midnightcontrols/icon.png>
|
||||||
|
|
||||||

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

|
[](https://www.curseforge.com/minecraft/mc-mods/midnightcontrols)
|
||||||

|
[![Modrinth]](https://modrinth.com/mod/midnightcontrols)
|
||||||

|
[](https://github.com/TeamMidnightDust/MidnightControls/releases)
|
||||||
[](https://www.curseforge.com/minecraft/mc-mods/lambdacontrols)
|

|
||||||
|
[![Mod loader: Quilt/Fabric]][Quilt]
|
||||||
|
[](https://www.oracle.com/java/technologies/downloads/#java21)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
A Fabric Minecraft mod which adds better controls, reach-around and controller support.
|
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?
|
## What's this mod?
|
||||||
|
|
||||||
This mod adds better controls, reach-around features, etc.
|
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!
|
||||||
|
|
||||||
Haven't you dreamed to travel in your modded Minecraft world with your controller? Yes? Then this mod is made for you!
|
## Features:
|
||||||
|
|
||||||
This mod also adds controller support.
|
- Controller support
|
||||||
|
- Touchscreen support
|
||||||
## ✅ Features:
|
- Keyboard controls to look around.
|
||||||
|
- Toggleable on screen button indicator (like in Bedrock Edition).
|
||||||
- Controller support
|
- Vertical reach-around.
|
||||||
- Touchscreen support (very experimental and buggy).
|
- Many Bedrock Edition features:
|
||||||
- Keyboard controls to look around.
|
|
||||||
- Toggleable on screen button indicator (like in Bedrock Edition).
|
|
||||||
- Vertical reach-around.
|
|
||||||
- Many Bedrock Edition features:
|
|
||||||
- Toggleable fly drifting
|
- Toggleable fly drifting
|
||||||
- Front block placing (be careful with this one)
|
- Front block placing (be careful with this one)
|
||||||
- New controls settings!
|
- New controls settings!
|
||||||
- Many options in config to change to your liking.
|
- Many options in config to change to your liking.
|
||||||
- Many controllers supported and in a simply way your own controller mappings.
|
- Many controllers supported and in a simple way your own controller mappings.
|
||||||
- An easy API for developers to add their own button bindings.
|
- An easy API for developers to add their own button bindings.
|
||||||
|
|
||||||
## 🎮 Supported Controllers:
|
## 🎮 Supported Controllers:
|
||||||
|
|
||||||
- Dualshock controllers
|
- DualShock controllers
|
||||||
- Xbox controllers
|
- DualSense controllers
|
||||||
- Switch Pro controllers
|
- Xbox controllers
|
||||||
- Joycons
|
- Switch Pro controllers
|
||||||
- And many more!
|
- Joy-Cons
|
||||||
|
- Steam controller and Steam Deck (WIP)
|
||||||
|
- And many more!
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Just do `./gradlew :fabric:build` and everything should build just fine!
|
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!
|
||||||
|
|
||||||
|
## FAQ:
|
||||||
|
### The controller does not work, and it's name appears in orange, what can I do?
|
||||||
|
Create a custom mapping as pointed out in the [wiki](https://midnightdust.eu/wiki/midnightcontrols/)
|
||||||
|
|
||||||
|
[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+
|
||||||
|
|||||||
113
build.gradle
113
build.gradle
@@ -1,57 +1,88 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||||
id 'maven-publish'
|
id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
|
||||||
|
id "me.shedaniel.unified-publishing" version "0.1.+" apply false
|
||||||
|
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
architectury {
|
||||||
group = project.maven_group
|
minecraft = rootProject.minecraft_version
|
||||||
version = project.mod_version
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = "https://api.modrinth.com/maven"
|
||||||
|
}
|
||||||
|
flatDir {
|
||||||
|
dirs("localMaven")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply plugin: "dev.architectury.loom"
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
maven {
|
||||||
mavenCentral()
|
url = "https://api.modrinth.com/maven"
|
||||||
maven { url = 'https://aperlambda.github.io/maven' }
|
}
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
flatDir {
|
||||||
|
dirs("../localMaven")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||||
|
// The following line declares the mojmap mappings, you may use other mappings as well
|
||||||
|
//mappings loom.officialMojangMappings()
|
||||||
|
// The following line declares the yarn mappings you may select this one as well.
|
||||||
|
mappings loom.layered {
|
||||||
|
it.mappings("net.fabricmc:yarn:$rootProject.yarn_mappings:v2")
|
||||||
|
it.mappings("dev.architectury:yarn-mappings-patch-neoforge:$rootProject.yarn_mappings_patch_neoforge_version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply plugin: "java"
|
||||||
|
apply plugin: "architectury-plugin"
|
||||||
|
apply plugin: "maven-publish"
|
||||||
|
|
||||||
|
archivesBaseName = rootProject.archives_base_name
|
||||||
|
version = rootProject.mod_version
|
||||||
|
group = rootProject.maven_group
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://aperlambda.github.io/maven' }
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation('org.aperlambda:lambdajcommon:1.8.1') {
|
||||||
|
exclude group: 'com.google.code.gson'
|
||||||
|
exclude group: 'com.google.guava'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
|
options.release = 21
|
||||||
|
}
|
||||||
|
ext {
|
||||||
|
releaseChangelog = {
|
||||||
|
def changes = new StringBuilder()
|
||||||
|
changes << "## MidnightControls v$project.version for $project.minecraft_version\n[View the changelog](https://www.github.com/TeamMidnightDust/MidnightControls/commits/)"
|
||||||
|
def proc = "git log --max-count=1 --pretty=format:%s".execute()
|
||||||
|
proc.in.eachLine { line ->
|
||||||
|
def processedLine = line.toString()
|
||||||
|
if (!processedLine.contains("New translations") && !processedLine.contains("Merge") && !processedLine.contains("branch")) {
|
||||||
|
changes << "\n- ${processedLine.capitalize()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proc.waitFor()
|
||||||
|
return changes.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
java {
|
||||||
repositories {
|
withSourcesJar()
|
||||||
mavenLocal()
|
|
||||||
maven {
|
|
||||||
name = "GithubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/LambdAurora/LambdaControls")
|
|
||||||
credentials {
|
|
||||||
username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
|
|
||||||
password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// select the repositories you want to publish to
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|||||||
65
common/build.gradle
Normal file
65
common/build.gradle
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
architectury {
|
||||||
|
common(rootProject.enabled_platforms.split(","))
|
||||||
|
}
|
||||||
|
loom {
|
||||||
|
accessWidenerPath = file("src/main/resources/midnightcontrols.accesswidener")
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
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.isxander.dev/releases' }
|
||||||
|
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'}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
||||||
|
// Do NOT use other classes from fabric loader
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
|
// Using the Fabric version of midnightlib here to create a common config and get useful utilities
|
||||||
|
// Just make sure NOT to use classes from the .fabric classpath
|
||||||
|
modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
|
||||||
|
modCompileOnlyApi "maven.modrinth:obsidianui:${rootProject.obsidianui_version}-fabric"
|
||||||
|
modCompileOnlyApi ("com.terraformersmc:modmenu:${project.modmenu_version}") {
|
||||||
|
exclude(group: "net.fabricmc.fabric-api")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compatibility mods
|
||||||
|
modCompileOnlyApi "io.github.cottonmc:LibGui:${project.libgui_version}"
|
||||||
|
modCompileOnlyApi "org.quiltmc:quilt-json5:1.0.0"
|
||||||
|
modImplementation "maven.modrinth:sodium:${project.sodium_version}-fabric"
|
||||||
|
modCompileOnlyApi "maven.modrinth:emi:${project.emi_version}"
|
||||||
|
modCompileOnlyApi "maven.modrinth:emotecraft:${project.emotecraft_version}"
|
||||||
|
modCompileOnlyApi "io.github.kosmx:bendy-lib:${project.bendylib_version}"
|
||||||
|
modCompileOnlyApi "dev.isxander:yet-another-config-lib:${project.yacl_version}"
|
||||||
|
modCompileOnlyApi "maven.modrinth:inventory-tabs-updated:${project.inventorytabs_version}"
|
||||||
|
modCompileOnlyApi "maven.modrinth:bedrockify:${project.bedrockify_version}"
|
||||||
|
// Required for Inventory Tabs
|
||||||
|
modCompileOnlyApi("me.shedaniel.cloth:cloth-config-fabric:${project.clothconfig_version}") {
|
||||||
|
exclude(group: "net.fabricmc.fabric-api")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenCommon(MavenPublication) {
|
||||||
|
artifactId = rootProject.archives_base_name
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
||||||
|
repositories {
|
||||||
|
// Add repositories to publish to here.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols;
|
package eu.midnightdust.midnightcontrols;
|
||||||
|
|
||||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.thinkingstudio.obsidianui.util.Nameable;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -19,11 +19,10 @@ import java.util.Optional;
|
|||||||
* Represents the controls mode.
|
* Represents the controls mode.
|
||||||
*
|
*
|
||||||
* @author LambdAurora
|
* @author LambdAurora
|
||||||
* @version 1.1.0
|
* @version 1.7.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public enum ControlsMode implements Nameable
|
public enum ControlsMode {
|
||||||
{
|
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
CONTROLLER,
|
CONTROLLER,
|
||||||
TOUCHSCREEN;
|
TOUCHSCREEN;
|
||||||
@@ -31,11 +30,10 @@ public enum ControlsMode implements Nameable
|
|||||||
/**
|
/**
|
||||||
* Returns the next controls mode available.
|
* Returns the next controls mode available.
|
||||||
*
|
*
|
||||||
* @return The next available controls mode.
|
* @return the next available controls mode
|
||||||
*/
|
*/
|
||||||
public ControlsMode next()
|
public ControlsMode next() {
|
||||||
{
|
var v = values();
|
||||||
ControlsMode[] v = values();
|
|
||||||
if (v.length == this.ordinal() + 1)
|
if (v.length == this.ordinal() + 1)
|
||||||
return v[0];
|
return v[0];
|
||||||
return v[this.ordinal() + 1];
|
return v[this.ordinal() + 1];
|
||||||
@@ -44,28 +42,24 @@ public enum ControlsMode implements Nameable
|
|||||||
/**
|
/**
|
||||||
* Gets the translation key of this controls mode.
|
* Gets the translation key of this controls mode.
|
||||||
*
|
*
|
||||||
* @return The translated key of this controls mode.
|
* @return the translated key of this controls mode
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public String getTranslationKey()
|
public String getTranslationKey() {
|
||||||
{
|
return "midnightcontrols.controls_mode." + this.getName();
|
||||||
return "lambdacontrols.controls_mode." + this.getName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public @NotNull String getName() {
|
||||||
public @NotNull String getName()
|
|
||||||
{
|
|
||||||
return this.name().toLowerCase();
|
return this.name().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the controls mode from its identifier.
|
* Gets the controls mode from its identifier.
|
||||||
*
|
*
|
||||||
* @param id The identifier of the controls mode.
|
* @param id the identifier of the controls mode
|
||||||
* @return The controls mode if found, else empty.
|
* @return the controls mode if found, else empty
|
||||||
*/
|
*/
|
||||||
public static Optional<ControlsMode> byId(@NotNull String id)
|
public static Optional<ControlsMode> byId(@NotNull String id) {
|
||||||
{
|
|
||||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the MidnightControls mod.
|
||||||
|
*
|
||||||
|
* @author LambdAurora & Motschen
|
||||||
|
* @version 1.8.0
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public class MidnightControls {
|
||||||
|
public static boolean isExtrasLoaded;
|
||||||
|
|
||||||
|
public static final Logger logger = LogManager.getLogger("MidnightControls");
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
isExtrasLoaded = PlatformFunctions.isModLoaded("midnightcontrols-extra");
|
||||||
|
log("Initializing MidnightControls...");
|
||||||
|
}
|
||||||
|
public static Identifier id(String path) {
|
||||||
|
return Identifier.of(MidnightControlsConstants.NAMESPACE, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a message to the terminal.
|
||||||
|
*
|
||||||
|
* @param info the message to print
|
||||||
|
*/
|
||||||
|
public static void log(String info) {
|
||||||
|
logger.info("[MidnightControls] {}", info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a warning to the terminal.
|
||||||
|
*
|
||||||
|
* @param warning the warning to print
|
||||||
|
*/
|
||||||
|
public static void warn(String warning) {
|
||||||
|
logger.warn("[MidnightControls] {}", warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = id("controls_mode");
|
||||||
|
public static final Identifier FEATURE_CHANNEL = id("feature");
|
||||||
|
public static final Identifier HELLO_CHANNEL = id("hello");
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
import org.thinkingstudio.obsidianui.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public MidnightControlsFeature(@NotNull String key) {
|
||||||
|
this(key, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the feature.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
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,263 @@
|
|||||||
|
/*
|
||||||
|
* 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.lib.util.PlatformFunctions;
|
||||||
|
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.RingScreen;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.KeyBindingIDAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.ring.ButtonBindingRingAction;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.ring.MidnightRing;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import org.thinkingstudio.obsidianui.hud.HudManager;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
|
||||||
|
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
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;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the midnightcontrols client mod.
|
||||||
|
*
|
||||||
|
* @author Motschen, LambdAurora
|
||||||
|
* @version 1.10.0
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public class MidnightControlsClient extends MidnightControls {
|
||||||
|
public static boolean lateInitDone = false;
|
||||||
|
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(id("look_up"),
|
||||||
|
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.midnightcontrols");
|
||||||
|
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(id("look_right"),
|
||||||
|
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.midnightcontrols");
|
||||||
|
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(id("look_down"),
|
||||||
|
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.midnightcontrols");
|
||||||
|
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(id("look_left"),
|
||||||
|
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.midnightcontrols");
|
||||||
|
public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(id("ring"),
|
||||||
|
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.midnightcontrols");
|
||||||
|
public static final Identifier CONTROLLER_BUTTONS = id("textures/gui/controller_buttons.png");
|
||||||
|
public static final Identifier CONTROLLER_EXPANDED = id("textures/gui/controller_expanded.png");
|
||||||
|
public static final Identifier CONTROLLER_AXIS = id("textures/gui/controller_axis.png");
|
||||||
|
public static final Identifier WAYLAND_CURSOR_TEXTURE_LIGHT = id("cursor/light/mouse_pointer");
|
||||||
|
public static final Identifier WAYLAND_CURSOR_TEXTURE_DARK = id("cursor/dark/mouse_pointer");
|
||||||
|
public static final File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt");
|
||||||
|
public static final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
public static final MidnightInput input = new MidnightInput();
|
||||||
|
public static final MidnightRing ring = new MidnightRing();
|
||||||
|
public static final MidnightReacharound reacharound = new MidnightReacharound();
|
||||||
|
public static boolean isWayland;
|
||||||
|
private static MidnightControlsHud hud;
|
||||||
|
private static ControlsMode previousControlsMode;
|
||||||
|
|
||||||
|
public static void initClient() {
|
||||||
|
ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY);
|
||||||
|
|
||||||
|
int delay = 0; // delay for 0 sec.
|
||||||
|
int period = 1; // repeat every 0.001 sec. (1000 times a second)
|
||||||
|
Timer timer = new Timer();
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
public void run() { // TODO: Add a try/catch here after the alpha testing period
|
||||||
|
if (lateInitDone && client.isRunning()) {
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput)) {
|
||||||
|
input.tickCameraStick();
|
||||||
|
input.updateCamera();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, delay, period);
|
||||||
|
|
||||||
|
HudManager.register(hud = new MidnightControlsHud());
|
||||||
|
isWayland = GLFW.glfwGetVersionString().contains("Wayland");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when Minecraft is initializing.
|
||||||
|
*/
|
||||||
|
public static void onMcInit(@NotNull MinecraftClient client) {
|
||||||
|
ButtonBinding.init(client.options);
|
||||||
|
MidnightControlsConfig.load();
|
||||||
|
if (MidnightControlsConfig.configVersion < 2) {
|
||||||
|
MidnightControlsConfig.mouseScreens.remove("me.jellysquid.mods.sodium.client.gui");
|
||||||
|
MidnightControlsConfig.mouseScreens.remove("net.coderbot.iris.gui");
|
||||||
|
MidnightControlsConfig.mouseScreens.remove("net.minecraft.class_5375");
|
||||||
|
MidnightControlsConfig.mouseScreens.remove("net.minecraft.client.gui.screen.pack.PackScreen");
|
||||||
|
MidnightControlsConfig.configVersion = 2;
|
||||||
|
MidnightControlsConfig.write(MidnightControlsConstants.NAMESPACE);
|
||||||
|
}
|
||||||
|
hud.setVisible(MidnightControlsConfig.hudEnable);
|
||||||
|
Controller.updateMappings();
|
||||||
|
try {
|
||||||
|
GLFW.glfwSetJoystickCallback((jid, event) -> {
|
||||||
|
if (event == GLFW.GLFW_CONNECTED) {
|
||||||
|
var controller = Controller.byId(jid);
|
||||||
|
client.getToastManager().add(new SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.connected", jid),
|
||||||
|
Text.literal(controller.getName())));
|
||||||
|
} else if (event == GLFW.GLFW_DISCONNECTED) {
|
||||||
|
client.getToastManager().add(new SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.disconnected", jid),
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
|
||||||
|
switchControlsMode();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {e.fillInStackTrace();}
|
||||||
|
|
||||||
|
MidnightControlsCompat.init();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This method is called to initialize keybindings
|
||||||
|
*/
|
||||||
|
public static void initKeybindings() {
|
||||||
|
if (lateInitDone) return;
|
||||||
|
if (KeyBindingIDAccessor.getKEYS_BY_ID() == null || KeyBindingIDAccessor.getKEYS_BY_ID().isEmpty()) return;
|
||||||
|
if (PlatformFunctions.isModLoaded("voxelmap") && !KeyBindingIDAccessor.getKEYS_BY_ID().containsKey("key.minimap.toggleingamewaypoints")) return;
|
||||||
|
if (PlatformFunctions.isModLoaded("wynntils") && KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().noneMatch(b -> Objects.equals(b.getValue().getCategory(), "Wynntils"))) return;
|
||||||
|
for (int i = 0; i < KeyBindingIDAccessor.getKEYS_BY_ID().size(); ++i) {
|
||||||
|
KeyBinding keyBinding = KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().toList().get(i).getValue();
|
||||||
|
if (MidnightControlsConfig.excludedKeybindings.stream().noneMatch(excluded -> keyBinding.getTranslationKey().startsWith(excluded))) {
|
||||||
|
if (!keyBinding.getTranslationKey().contains(MidnightControlsConstants.NAMESPACE)) {
|
||||||
|
AtomicReference<ButtonCategory> category = new AtomicReference<>();
|
||||||
|
InputManager.streamCategories().forEach(buttonCategory -> {
|
||||||
|
if (buttonCategory.getIdentifier().equals(validVanillaId(keyBinding.getCategory())))
|
||||||
|
category.set(buttonCategory);
|
||||||
|
});
|
||||||
|
if (category.get() == null) {
|
||||||
|
category.set(new ButtonCategory(validVanillaId(keyBinding.getCategory())));
|
||||||
|
InputManager.registerCategory(category.get());
|
||||||
|
}
|
||||||
|
ButtonBinding buttonBinding = new ButtonBinding.Builder(keyBinding.getTranslationKey()).category(category.get()).linkKeybind(keyBinding).register();
|
||||||
|
if (MidnightControlsConfig.debug) {
|
||||||
|
MidnightControls.log(keyBinding.getTranslationKey());
|
||||||
|
MidnightControls.log(String.valueOf(buttonBinding));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputManager.loadButtonBindings();
|
||||||
|
lateInitDone = true;
|
||||||
|
}
|
||||||
|
private static Identifier validVanillaId(String path) {
|
||||||
|
for(int i = 0; i < path.length(); ++i) {
|
||||||
|
if (!Identifier.isPathCharacterValid(path.charAt(i))) {
|
||||||
|
path = path.replace(path.charAt(i), '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Identifier.ofVanilla(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called every Minecraft tick.
|
||||||
|
*
|
||||||
|
* @param client the client instance
|
||||||
|
*/
|
||||||
|
public static void onTick(@NotNull MinecraftClient client) {
|
||||||
|
initKeybindings();
|
||||||
|
input.tick();
|
||||||
|
reacharound.tick();
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput))
|
||||||
|
input.tickController();
|
||||||
|
|
||||||
|
if (BINDING_RING.wasPressed()) {
|
||||||
|
ring.loadFromUnbound();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
RainbowColor.tick();
|
||||||
|
TouchInput.tick();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Called when opening a screen.
|
||||||
|
*/
|
||||||
|
public static void onScreenOpen(Screen screen) {
|
||||||
|
if (screen == null && MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN) {
|
||||||
|
screen = new TouchscreenOverlay();
|
||||||
|
screen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
|
||||||
|
client.skipGameRender = false;
|
||||||
|
client.currentScreen = screen;
|
||||||
|
} else if (screen != null) {
|
||||||
|
MidnightControlsClient.input.onScreenOpen(client.getWindow().getWidth(), client.getWindow().getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when leaving a server.
|
||||||
|
*/
|
||||||
|
public static void onLeave() {
|
||||||
|
MidnightControlsFeature.resetAllAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches the controls mode if the auto switch is enabled.
|
||||||
|
*/
|
||||||
|
public static void switchControlsMode() {
|
||||||
|
if (MidnightControlsConfig.autoSwitchMode) {
|
||||||
|
if (MidnightControlsConfig.getController().isGamepad()) {
|
||||||
|
previousControlsMode = MidnightControlsConfig.controlsMode;
|
||||||
|
MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
|
||||||
|
} else {
|
||||||
|
if (previousControlsMode == null) {
|
||||||
|
previousControlsMode = ControlsMode.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidnightControlsConfig.controlsMode = previousControlsMode;
|
||||||
|
}
|
||||||
|
NetworkUtil.sendPayloadC2S(new ControlsModePayload(MidnightControlsConfig.controlsMode.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the HUD is enabled or not.
|
||||||
|
*
|
||||||
|
* @param enabled true if the HUD is enabled, else false
|
||||||
|
*/
|
||||||
|
public static void setHudEnabled(boolean enabled) {
|
||||||
|
MidnightControlsConfig.hudEnable = enabled;
|
||||||
|
hud.setVisible(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final MidnightControlsClient INSTANCE = new MidnightControlsClient();
|
||||||
|
/**
|
||||||
|
* Gets the midnightcontrols client instance.
|
||||||
|
*
|
||||||
|
* @return the midnightcontrols client instance
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static MidnightControlsClient get() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.common.collect.Maps;
|
||||||
|
import com.mojang.blaze3d.platform.GlDebugInfo;
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfig;
|
||||||
|
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.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.CameraMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.screen.ChatScreen;
|
||||||
|
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||||
|
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 final String CONTROLLER = "controller";
|
||||||
|
public static final String TOUCH = "touch";
|
||||||
|
public static final String GAMEPLAY = "gameplay";
|
||||||
|
public static final String SCREENS = "screens";
|
||||||
|
public static final String VISUAL = "visual";
|
||||||
|
public static final String MISC = "misc";
|
||||||
|
public static boolean isEditing = false;
|
||||||
|
@Hidden @Entry public static int configVersion = 2;
|
||||||
|
// General
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.controls_mode") public static ControlsMode controlsMode = ControlsMode.DEFAULT;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.auto_switch_mode") public static boolean autoSwitchMode = true;
|
||||||
|
@Entry(category = MISC, name = "Debug") public static boolean debug = false;
|
||||||
|
// HUD
|
||||||
|
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_enable") public static boolean hudEnable = true;
|
||||||
|
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_side") public static HudSide hudSide = HudSide.LEFT;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.move_chat") public static boolean moveChat = false;
|
||||||
|
// Gameplay
|
||||||
|
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.analog_movement") public static boolean analogMovement = true;
|
||||||
|
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.double_tap_to_sprint") public static boolean doubleTapToSprint = true;
|
||||||
|
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sneak") public static boolean controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
|
||||||
|
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sprint") public static boolean controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
|
||||||
|
@Entry(category = GAMEPLAY, 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(category = GAMEPLAY, 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(category = GAMEPLAY, 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(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.horizontal") public static boolean horizontalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
|
||||||
|
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.vertical") public static boolean verticalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
|
||||||
|
@Entry(category = VISUAL, name = "Reacharound Outline") public static boolean shouldRenderReacharoundOutline = true;
|
||||||
|
@Entry(category = VISUAL, name = "Reacharound Outline Color", isColor = true) public static String reacharoundOutlineColorHex = "#ffffff";
|
||||||
|
@Entry(category = VISUAL, name = "Reacharound Outline Alpha", isSlider = true, min = 0, max = 255) public static int reacharoundOutlineColorAlpha = 102;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.right_dead_zone", isSlider = true, min = 0.05, max = 1) public static double rightDeadZone = 0.25;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.left_dead_zone", isSlider = true, min = 0.05, max = 1) public static double leftDeadZone = 0.25;
|
||||||
|
@Entry(category = CONTROLLER, name = "Trigger Dead-Zone", isSlider = true, min = 0.05, max = 1) public static double triggerDeadZone = 0.1;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_y_axis") public static boolean invertRightYAxis = false;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_x_axis") public static boolean invertRightXAxis = false;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double rotationSpeed = 35.0; //used for x-axis, name kept for compatibility
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.y_axis_rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double yAxisRotationSpeed = rotationSpeed;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.camera_mode") public static CameraMode cameraMode = CameraMode.FLAT;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.mouse_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double mouseSpeed = 25.0;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.joystick_as_mouse") public static boolean joystickAsMouse = false;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_as_mouse") public static boolean eyeTrackerAsMouse = false;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_deadzone", isSlider = true, min = 0, max = 0.4) public static double eyeTrackerDeadzone = 0.05;
|
||||||
|
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.unfocused_input") public static boolean unfocusedInput = false;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse") public static boolean virtualMouse = false;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse.skin") public static VirtualMouseSkin virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
|
||||||
|
@Entry(category = SCREENS, name = "midnightcontrols.menu.hide_cursor") public static boolean hideNormalMouse = false;
|
||||||
|
@Entry(category = CONTROLLER, name = "Controller ID") @Hidden public static Object controllerID = 0;
|
||||||
|
@Entry(category = CONTROLLER, name = "2nd Controller ID") @Hidden public static Object secondControllerID = -1;
|
||||||
|
@Entry(category = VISUAL, name = "midnightcontrols.menu.controller_type") public static ControllerType controllerType = ControllerType.DEFAULT;
|
||||||
|
@Entry(category = SCREENS, name = "Mouse screens") public static List<String> mouseScreens = Lists.newArrayList("net.minecraft.client.gui.screen.advancement",
|
||||||
|
"net.minecraft.class_457", "net.minecraft.class_408", "net.minecraft.class_3872", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen",
|
||||||
|
"hardcorequesting.client.interfaces.GuiQuestBook", "hardcorequesting.client.interfaces.GuiReward", "hardcorequesting.client.interfaces.EditTrackerScreen",
|
||||||
|
"me.shedaniel.clothconfig2.gui.ClothConfigScreen", "com.mamiyaotaru.voxelmap.gui.GuiWaypoints", "com.mamiyaotaru.voxelmap.gui.GuiPersistentMap");
|
||||||
|
@Entry(category = SCREENS, name = "Arrow screens") public static List<String> arrowScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName());
|
||||||
|
@Entry(category = SCREENS, name = "WASD screens") public static List<String> wasdScreens = Lists.newArrayList("com.ultreon.devices.core.Laptop");
|
||||||
|
@Entry(category = TOUCH, name = "Screens with close button") public static List<String> closeButtonScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName(), AdvancementsScreen.class.getCanonicalName(), RingScreen.class.getCanonicalName());
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_with_controller") public static boolean touchInControllerMode = false;
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double touchSpeed = 50.0;
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.invert_touch") public static boolean invertTouch = false;
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_mode") public static TouchMode touchMode = TouchMode.CROSSHAIR;
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_break_delay", isSlider = true, min = 50, max = 500) public static int touchBreakDelay = 120;
|
||||||
|
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_transparency", isSlider = true, min = 0, max = 100) public static int touchTransparency = 75;
|
||||||
|
@Entry(category = TOUCH, name = "Touch Outline Color", isColor = true) public static String touchOutlineColorHex = "#ffffff";
|
||||||
|
@Entry(category = TOUCH, name = "Touch Outline Alpha", isSlider = true, min = 0, max = 255) public static int touchOutlineColorAlpha = 150;
|
||||||
|
@Entry(category = TOUCH, name = "Left Touch button bindings") public static List<String> leftTouchBinds = Lists.newArrayList("debug_screen", "screenshot","toggle_perspective");
|
||||||
|
@Entry(category = TOUCH, name = "Right Touch button bindings") public static List<String> rightTouchBinds = Lists.newArrayList("screenshot","toggle_perspective", "use");
|
||||||
|
|
||||||
|
@Entry @Hidden public static Map<String, String> BINDING = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
|
||||||
|
@Entry(category = CONTROLLER, name = "Max analog value: Left X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftX = 1;
|
||||||
|
@Entry(category = CONTROLLER, name = "Max analog value: Left Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftY = 1;
|
||||||
|
@Entry(category = CONTROLLER, name = "Max analog value: Right X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightX = 1;
|
||||||
|
@Entry(category = CONTROLLER, name = "Max analog value: Right Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightY = 1;
|
||||||
|
@Entry(category = CONTROLLER, name = "Trigger button fix") public static boolean triggerFix = true;
|
||||||
|
@Entry(category = CONTROLLER, name = "Excluded Controllers (Name Regex)") public static List<String> excludedControllers = Lists.newArrayList(".*(Keyboard)$", ".*(Touchpad)$", ".*(Pen)$", ".*(Finger)$");
|
||||||
|
@Entry(category = MISC, name = "Excluded Keybindings") public static List<String> excludedKeybindings = Lists.newArrayList("key.forward", "key.left", "key.back", "key.right", "key.jump", "key.sneak", "key.sprint", "key.inventory",
|
||||||
|
"key.swapOffhand", "key.drop", "key.use", "key.attack", "key.chat", "key.playerlist", "key.screenshot", "key.togglePerspective", "key.smoothCamera", "key.fullscreen", "key.saveToolbarActivator", "key.loadToolbarActivator",
|
||||||
|
"key.pickItem", "key.hotbar.1", "key.hotbar.2", "key.hotbar.3", "key.hotbar.4", "key.hotbar.5", "key.hotbar.6", "key.hotbar.7", "key.hotbar.8", "key.hotbar.9");
|
||||||
|
@Entry(category = GAMEPLAY, name = "Enable Hints") public static boolean enableHints = true;
|
||||||
|
@Entry(category = SCREENS, name = "Enable Shortcut in Controls Options") public static boolean shortcutInControls = true;
|
||||||
|
@Entry(category = MISC, name = "Ring Bindings (WIP)") public static List<String> ringBindings = new ArrayList<>();
|
||||||
|
@Entry(category = MISC, name = "Ignored Unbound Keys") public static List<String> ignoredUnboundKeys = Lists.newArrayList("inventorytabs.key.next_tab");
|
||||||
|
@Entry @Hidden public static Map<String, Map<String, String>> controllerBindingProfiles = new HashMap<>();
|
||||||
|
private static Map<String, String> currentBindingProfile = new HashMap<>();
|
||||||
|
private static Controller prevController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the configuration
|
||||||
|
*/
|
||||||
|
public static void load() {
|
||||||
|
MidnightControlsConfig.init(MidnightControlsConstants.NAMESPACE, MidnightControlsConfig.class);
|
||||||
|
MidnightControls.log("Configuration loaded.");
|
||||||
|
// Controller controls.
|
||||||
|
InputManager.loadButtonBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the configuration.
|
||||||
|
*/
|
||||||
|
public static void save() {
|
||||||
|
MidnightControlsConfig.write(MidnightControlsConstants.NAMESPACE);
|
||||||
|
MidnightControls.log("Configuration saved.");
|
||||||
|
MidnightControlsFeature.refreshEnabled();
|
||||||
|
}
|
||||||
|
public static void updateBindingsForController(Controller controller) {
|
||||||
|
if (controller.isConnected() && controller.isGamepad() && controllerBindingProfiles.containsKey(controller.getGuid()))
|
||||||
|
currentBindingProfile = controllerBindingProfiles.get(controller.getGuid());
|
||||||
|
else currentBindingProfile = Maps.newHashMap(BINDING);
|
||||||
|
InputManager.loadButtonBindings();
|
||||||
|
}
|
||||||
|
public static Map<String, String> getBindingsForController() {
|
||||||
|
return currentBindingProfile;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
if (prevController != controller) updateBindingsForController(controller);
|
||||||
|
prevController = controller;
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the used controller.
|
||||||
|
*
|
||||||
|
* @param controller the controller
|
||||||
|
*/
|
||||||
|
public static void setController(Controller controller) {
|
||||||
|
MidnightControlsConfig.controllerID = controller.id();
|
||||||
|
MidnightControlsConfig.write(MidnightControlsConstants.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
return switch (axis) {
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_X -> MidnightControlsConfig.maxAnalogValueLeftX;
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_Y -> MidnightControlsConfig.maxAnalogValueLeftY;
|
||||||
|
case GLFW_GAMEPAD_AXIS_RIGHT_X -> MidnightControlsConfig.maxAnalogValueRightX;
|
||||||
|
default -> MidnightControlsConfig.maxAnalogValueRightY;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setAxisMaxValue(int axis, double value) {
|
||||||
|
switch (axis) {
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_X -> MidnightControlsConfig.maxAnalogValueLeftX = value;
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_Y -> MidnightControlsConfig.maxAnalogValueLeftY = value;
|
||||||
|
case GLFW_GAMEPAD_AXIS_RIGHT_X -> MidnightControlsConfig.maxAnalogValueRightX = value;
|
||||||
|
default -> MidnightControlsConfig.maxAnalogValueRightY = 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 = getBindingsForController().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) {
|
||||||
|
MidnightControls.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
|
||||||
|
MidnightControlsConfig.setButtonBinding(button, new int[]{-1});
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setButton(buttons);
|
||||||
|
} catch (Exception e) {
|
||||||
|
MidnightControls.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
|
||||||
|
setButtonBinding(button, button.getButton());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkValidity(@NotNull ButtonBinding binding, @NotNull String input, String group) {
|
||||||
|
if (group == null) {
|
||||||
|
MidnightControls.warn("Malformed config value \"" + input + "\" for binding \"" + binding.getName() + "\".");
|
||||||
|
setButtonBinding(binding, binding.getButton());
|
||||||
|
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);
|
||||||
|
getBindingsForController().put("controller.controls." + binding.getName(), binding.getButtonCode());
|
||||||
|
if (controllerBindingProfiles.containsKey(getController().getGuid())) controllerBindingProfiles.get(getController().getGuid()).put("controller.controls." + binding.getName(), binding.getButtonCode());
|
||||||
|
else BINDING.put("controller.controls." + binding.getName(), binding.getButtonCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBackButton(int btn, boolean isBtn, ButtonState state) {
|
||||||
|
if (!isBtn && state == ButtonState.NONE)
|
||||||
|
return false;
|
||||||
|
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axisAsButton(btn, state == ButtonState.PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isForwardButton(int btn, boolean isBtn, ButtonState state) {
|
||||||
|
if (!isBtn && state == ButtonState.NONE)
|
||||||
|
return false;
|
||||||
|
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axisAsButton(btn, state == ButtonState.PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeftButton(int btn, boolean isBtn, ButtonState state) {
|
||||||
|
if (!isBtn && state == ButtonState.NONE)
|
||||||
|
return false;
|
||||||
|
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axisAsButton(btn, state == ButtonState.PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRightButton(int btn, boolean isBtn, ButtonState state) {
|
||||||
|
if (!isBtn && state == ButtonState.NONE)
|
||||||
|
return false;
|
||||||
|
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axisAsButton(btn, state == ButtonState.PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
|
||||||
|
controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
|
||||||
|
fastBlockPlacing = false;
|
||||||
|
flyDrifting = true;
|
||||||
|
verticalFlyDrifting = true;
|
||||||
|
horizontalReacharound = false;
|
||||||
|
verticalReacharound = false;
|
||||||
|
shouldRenderReacharoundOutline = true;
|
||||||
|
reacharoundOutlineColorHex = "#ffffff";
|
||||||
|
reacharoundOutlineColorAlpha = 102;
|
||||||
|
rightDeadZone = 0.25;
|
||||||
|
leftDeadZone = 0.25;
|
||||||
|
invertRightYAxis = false;
|
||||||
|
invertRightXAxis = false;
|
||||||
|
rotationSpeed = 35.0;
|
||||||
|
yAxisRotationSpeed = rotationSpeed;
|
||||||
|
mouseSpeed = 25.0;
|
||||||
|
unfocusedInput = false;
|
||||||
|
virtualMouse = false;
|
||||||
|
virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
|
||||||
|
controllerID = 0;
|
||||||
|
secondControllerID = -1;
|
||||||
|
controllerType = ControllerType.DEFAULT;
|
||||||
|
mouseScreens = Lists.newArrayList("net.minecraft.client.gui.screen.advancement", "net.minecraft.class_457", "net.minecraft.class_408", "net.minecraft.class_3872", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen", "me.shedaniel.clothconfig2.gui.ClothConfigScreen", "com.mamiyaotaru.voxelmap.gui.GuiWaypoints", "com.mamiyaotaru.voxelmap.gui.GuiPersistentMap");
|
||||||
|
BINDING = new HashMap<>();
|
||||||
|
maxAnalogValueLeftX = 1;
|
||||||
|
maxAnalogValueLeftY = 1;
|
||||||
|
maxAnalogValueRightX = 1;
|
||||||
|
maxAnalogValueRightY = 1;
|
||||||
|
triggerFix = true;
|
||||||
|
enableHints = true;
|
||||||
|
shortcutInControls = true;
|
||||||
|
ringBindings = new ArrayList<>();
|
||||||
|
ignoredUnboundKeys = Lists.newArrayList("inventorytabs.key.next_tab");
|
||||||
|
controllerBindingProfiles = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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") && GlDebugInfo.getCpuInfo().contains("AMD Custom APU")) return ControllerType.STEAM_DECK;
|
||||||
|
else if (controller.contains("steam")) return ControllerType.STEAM_CONTROLLER;
|
||||||
|
else if (controller.contains("dualsense") || controller.contains("ps5")) return ControllerType.DUALSENSE;
|
||||||
|
else if (controller.contains("dualshock") || controller.contains("ps4") || controller.contains("sony")) return ControllerType.DUALSHOCK;
|
||||||
|
else if (controller.contains("switch") || controller.contains("joy-con") || controller.contains("wii") || controller.contains("nintendo")) return ControllerType.SWITCH;
|
||||||
|
else if (controller.contains("ouya")) return ControllerType.OUYA;
|
||||||
|
else return ControllerType.DEFAULT;
|
||||||
|
}
|
||||||
|
public static boolean doMixedInput() {
|
||||||
|
return touchInControllerMode && controlsMode == ControlsMode.CONTROLLER;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,819 @@
|
|||||||
|
/*
|
||||||
|
* 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 eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.EmotecraftCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.LibGuiCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.YACLCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.AdvancementsScreenAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.MouseAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.InventoryUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.storage.AxisStorage;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.storage.ButtonStorage;
|
||||||
|
import net.minecraft.client.gui.screen.option.KeybindsScreen;
|
||||||
|
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||||
|
import net.minecraft.client.gui.widget.PressableWidget;
|
||||||
|
import net.minecraft.client.gui.widget.SliderWidget;
|
||||||
|
import net.minecraft.entity.vehicle.BoatEntity;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||||
|
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.enums.CameraMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
|
||||||
|
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
|
||||||
|
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.AbstractSprucePressableButtonWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceElement;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
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.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.ingame.StonecutterScreen;
|
||||||
|
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.text.TranslatableTextContent;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.glfw.GLFWGamepadState;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the midnightcontrols' input handler.
|
||||||
|
*
|
||||||
|
* @author Motschen, LambdAurora
|
||||||
|
* @version 1.10.0
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public class MidnightInput {
|
||||||
|
public static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
|
||||||
|
// Cooldowns
|
||||||
|
public int actionGuiCooldown = 0;
|
||||||
|
public int joystickCooldown = 0;
|
||||||
|
public boolean ignoreNextARelease = false;
|
||||||
|
public boolean ignoreNextXRelease = false;
|
||||||
|
private double targetYaw = 0.0;
|
||||||
|
private double targetPitch = 0.0;
|
||||||
|
private float prevXAxis = 0.f;
|
||||||
|
private float prevYAxis = 0.f;
|
||||||
|
public float mouseSpeedX = 0.f;
|
||||||
|
public 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.
|
||||||
|
*/
|
||||||
|
public void tick() {
|
||||||
|
this.targetYaw = 0.F;
|
||||||
|
this.targetPitch = 0.F;
|
||||||
|
|
||||||
|
// Handles the key bindings.
|
||||||
|
if (MidnightControlsClient.BINDING_LOOK_UP.isPressed()) {
|
||||||
|
this.handleFlatLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_Y, -0.8F, 0d));
|
||||||
|
} else if (MidnightControlsClient.BINDING_LOOK_DOWN.isPressed()) {
|
||||||
|
this.handleFlatLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 0d));
|
||||||
|
}
|
||||||
|
if (MidnightControlsClient.BINDING_LOOK_LEFT.isPressed()) {
|
||||||
|
this.handleFlatLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_X, -0.8F, 0d));
|
||||||
|
} else if (MidnightControlsClient.BINDING_LOOK_RIGHT.isPressed()) {
|
||||||
|
this.handleFlatLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 0d));
|
||||||
|
}
|
||||||
|
|
||||||
|
InputManager.INPUT_MANAGER.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called every Minecraft tick for controller input update.
|
||||||
|
*/
|
||||||
|
public void tickController() {
|
||||||
|
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;
|
||||||
|
if (this.joystickCooldown > 0)
|
||||||
|
--this.joystickCooldown;
|
||||||
|
|
||||||
|
InputManager.updateStates();
|
||||||
|
|
||||||
|
var controller = MidnightControlsConfig.getController();
|
||||||
|
|
||||||
|
if (controller.isConnected()) {
|
||||||
|
var state = controller.getState();
|
||||||
|
this.fetchButtonInput(state, false);
|
||||||
|
this.fetchTriggerInput(state, false);
|
||||||
|
this.fetchJoystickInput(state, false, false);
|
||||||
|
}
|
||||||
|
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
|
||||||
|
.ifPresent(joycon -> {
|
||||||
|
var state = joycon.getState();
|
||||||
|
this.fetchButtonInput(state, true);
|
||||||
|
this.fetchTriggerInput(state, true);
|
||||||
|
this.fetchJoystickInput(state, true, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
|
||||||
|
|
||||||
|
if (allowInput)
|
||||||
|
InputManager.updateBindings();
|
||||||
|
|
||||||
|
if (this.controlsInput != null) {
|
||||||
|
InputManager.STATES.forEach((num, button) -> {
|
||||||
|
if (button.isPressed()) System.out.println(num);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.controlsInput != null && InputManager.STATES.int2ObjectEntrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::isUnpressed)) {
|
||||||
|
if (MidnightControlsConfig.debug) MidnightControls.log("Starting MidnightInput Button Edit");
|
||||||
|
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 1000 times a second for smooth camera input
|
||||||
|
*/
|
||||||
|
public void tickCameraStick() {
|
||||||
|
var controller = MidnightControlsConfig.getController();
|
||||||
|
|
||||||
|
if (controller.isConnected()) {
|
||||||
|
this.fetchJoystickInput(controller.getState(), false, true);
|
||||||
|
}
|
||||||
|
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
|
||||||
|
.ifPresent(joycon -> this.fetchJoystickInput(joycon.getState(), true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called before the screen is rendered.
|
||||||
|
*
|
||||||
|
* @param screen the screen to render
|
||||||
|
*/
|
||||||
|
public void onPreRenderScreen(@NotNull Screen screen) {
|
||||||
|
if (!isScreenInteractive(screen)) {
|
||||||
|
InputManager.INPUT_MANAGER.updateMousePosition(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called to update the camera
|
||||||
|
*/
|
||||||
|
public void updateCamera() {
|
||||||
|
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) (client.player.prevYaw + (this.targetYaw * 0.175));
|
||||||
|
float rotationPitch = (float) (client.player.prevPitch + (this.targetPitch * 0.175));
|
||||||
|
client.player.prevYaw = rotationYaw;
|
||||||
|
client.player.prevPitch = MathHelper.clamp(rotationPitch, -90.f, 90.f);
|
||||||
|
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 windowWidth the window width
|
||||||
|
* @param windowHeight the window height
|
||||||
|
*/
|
||||||
|
public void onScreenOpen(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 GLFWGamepadState gamepadState, boolean leftJoycon) {
|
||||||
|
var buffer = gamepadState.buttons();
|
||||||
|
for (int i = 0; i < buffer.limit(); i++) {
|
||||||
|
int btn = leftJoycon ? ButtonBinding.controller2Button(i) : i;
|
||||||
|
boolean pressed = buffer.get() == (byte) 1;
|
||||||
|
var state = ButtonState.NONE;
|
||||||
|
var previousState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
|
||||||
|
|
||||||
|
if (pressed != previousState.isPressed()) {
|
||||||
|
state = pressed ? ButtonState.PRESS : ButtonState.RELEASE;
|
||||||
|
this.handleButton(ButtonStorage.of(btn, state));
|
||||||
|
if (pressed)
|
||||||
|
BUTTON_COOLDOWNS.put(btn, 5);
|
||||||
|
} else if (pressed) {
|
||||||
|
state = ButtonState.REPEAT;
|
||||||
|
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
|
||||||
|
BUTTON_COOLDOWNS.put(btn, 5);
|
||||||
|
this.handleButton(ButtonStorage.of(btn, state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputManager.STATES.put(btn, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final MathUtil.PolarUtil polarUtil = new MathUtil.PolarUtil();
|
||||||
|
|
||||||
|
private void fetchJoystickInput(@NotNull GLFWGamepadState gamepadState, boolean leftJoycon, boolean cameraTick) {
|
||||||
|
var buffer = gamepadState.axes();
|
||||||
|
|
||||||
|
polarUtil.calculate(buffer.get(GLFW_GAMEPAD_AXIS_LEFT_X), buffer.get(GLFW_GAMEPAD_AXIS_LEFT_Y), 1, MidnightControlsConfig.leftDeadZone);
|
||||||
|
float leftX = polarUtil.polarX;
|
||||||
|
float leftY = polarUtil.polarY;
|
||||||
|
polarUtil.calculate(buffer.get(GLFW_GAMEPAD_AXIS_RIGHT_X), buffer.get(GLFW_GAMEPAD_AXIS_RIGHT_Y), 1, MidnightControlsConfig.rightDeadZone);
|
||||||
|
float rightX = polarUtil.polarX;
|
||||||
|
float rightY = polarUtil.polarY;
|
||||||
|
|
||||||
|
boolean isRadialMenu = client.currentScreen instanceof RingScreen || (PlatformFunctions.isModLoaded("emotecraft") && EmotecraftCompat.isEmotecraftScreen(client.currentScreen));
|
||||||
|
|
||||||
|
if (!isRadialMenu) {
|
||||||
|
for (int i = cameraTick ? GLFW_GAMEPAD_AXIS_RIGHT_X : 0; i < (cameraTick ? GLFW_GAMEPAD_AXIS_LEFT_TRIGGER : GLFW_GAMEPAD_AXIS_RIGHT_X); i++) {
|
||||||
|
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
|
||||||
|
float value = buffer.get(i);
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_X -> {
|
||||||
|
if (MidnightControlsConfig.analogMovement) value = leftX;
|
||||||
|
}
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_Y -> {
|
||||||
|
if (MidnightControlsConfig.analogMovement) value = leftY;
|
||||||
|
}
|
||||||
|
case GLFW_GAMEPAD_AXIS_RIGHT_X -> value = rightX;
|
||||||
|
case GLFW_GAMEPAD_AXIS_RIGHT_Y -> value = rightY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
|
||||||
|
value *= -1.0F;
|
||||||
|
|
||||||
|
this.handleJoystickAxis(AxisStorage.of(axis, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
boolean leftStickActive = leftX != 0 || leftY != 0;
|
||||||
|
handleRadialMenu(leftStickActive ? leftX : rightX, leftStickActive ? leftY : rightY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchTriggerInput(@NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
|
||||||
|
var buffer = gamepadState.axes();
|
||||||
|
|
||||||
|
for (int i = GLFW_GAMEPAD_AXIS_LEFT_TRIGGER; i <= GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER; i++) {
|
||||||
|
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
|
||||||
|
float value = buffer.get(i);
|
||||||
|
|
||||||
|
this.handleTriggerAxis(AxisStorage.of(axis, value, MidnightControlsConfig.triggerDeadZone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleButton(ButtonStorage storage) {
|
||||||
|
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
|
||||||
|
if (storage.state == ButtonState.PRESS && !this.controlsInput.currentButtons.contains(storage.button)) {
|
||||||
|
this.controlsInput.currentButtons.add(storage.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 (client.currentScreen != null && storage.state.isPressed() && storage.button == GLFW_GAMEPAD_BUTTON_Y &&
|
||||||
|
MidnightControlsConfig.arrowScreens.contains(client.currentScreen.getClass().getCanonicalName())) {
|
||||||
|
pressKeyboardKey(client, GLFW.GLFW_KEY_ENTER);
|
||||||
|
this.screenCloseCooldown = 5;
|
||||||
|
}
|
||||||
|
else if (storage.state.isPressed()) {
|
||||||
|
if (client.currentScreen != null && storage.isDpad() && this.actionGuiCooldown == 0) {
|
||||||
|
switch (storage.button) {
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> this.changeFocus(client.currentScreen, NavigationDirection.UP);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> this.handleLeftRight(client.currentScreen, false);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> this.handleLeftRight(client.currentScreen, true);
|
||||||
|
}
|
||||||
|
if (MidnightControlsConfig.wasdScreens.contains(client.currentScreen.getClass().getCanonicalName())) {
|
||||||
|
switch (storage.button) {
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> pressKeyboardKey(client, GLFW.GLFW_KEY_W);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> pressKeyboardKey(client, GLFW.GLFW_KEY_S);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> pressKeyboardKey(client, GLFW.GLFW_KEY_A);
|
||||||
|
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> pressKeyboardKey(client, GLFW.GLFW_KEY_D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (storage.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; // Set the cooldown to 5 ticks to avoid unintended button presses.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PlatformFunctions.isModLoaded("libgui")) LibGuiCompat.handlePress(client.currentScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storage.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 && client.currentScreen != null) {
|
||||||
|
var accessor = (MouseAccessor) client.mouse;
|
||||||
|
accessor.midnightcontrols$onCursorPos(client.getWindow().getHandle(), client.mouse.getX(), client.mouse.getY());
|
||||||
|
switch (storage.state) {
|
||||||
|
// Button pressed
|
||||||
|
case PRESS -> accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), GLFW_MOUSE_BUTTON_LEFT, 1, 0);
|
||||||
|
case RELEASE -> { // Button released
|
||||||
|
accessor.midnightcontrols$onMouseButton(client.getWindow().getHandle(), GLFW_MOUSE_BUTTON_LEFT, 0, 0);
|
||||||
|
client.currentScreen.setDragging(false);
|
||||||
|
}
|
||||||
|
case REPEAT -> client.currentScreen.setDragging(true); // Button held down / dragging
|
||||||
|
}
|
||||||
|
this.screenCloseCooldown = 5;
|
||||||
|
} else {
|
||||||
|
this.ignoreNextARelease = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (storage.button == GLFW.GLFW_GAMEPAD_BUTTON_X && client.currentScreen != null && !isScreenInteractive(client.currentScreen)
|
||||||
|
&& this.actionGuiCooldown == 0) {
|
||||||
|
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 (client.currentScreen instanceof HandledScreen<?> handledScreen && ((HandledScreenAccessor) handledScreen).midnightcontrols$getSlotAt(
|
||||||
|
mouseX, mouseY) != null) return;
|
||||||
|
if (!this.ignoreNextXRelease && client.currentScreen != null) {
|
||||||
|
if (storage.state == ButtonState.PRESS) client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2);
|
||||||
|
else if (storage.state == ButtonState.RELEASE) client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2);
|
||||||
|
this.screenCloseCooldown = 5;
|
||||||
|
} else {
|
||||||
|
this.ignoreNextXRelease = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void handleTriggerAxis(AxisStorage storage) {
|
||||||
|
storage.setupButtonStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleJoystickAxis(AxisStorage storage) {
|
||||||
|
storage.setupButtonStates();
|
||||||
|
|
||||||
|
this.handleJoystickMovement(storage);
|
||||||
|
|
||||||
|
if (this.handleScreenScrolling(client.currentScreen, storage)) return;
|
||||||
|
|
||||||
|
storage.absValue = (float) MathHelper.clamp(storage.absValue / MidnightControlsConfig.getAxisMaxValue(storage.axis), 0.f, 1.f);
|
||||||
|
if (client.currentScreen == null) {
|
||||||
|
// Handles the look direction.
|
||||||
|
this.handleLook(storage);
|
||||||
|
} else {
|
||||||
|
boolean allowMouseControl = true;
|
||||||
|
|
||||||
|
if (this.actionGuiCooldown == 0 && MidnightControlsConfig.isMovementAxis(storage.axis) && isScreenInteractive(client.currentScreen)) {
|
||||||
|
if (MidnightControlsConfig.isForwardButton(storage.axis, false, storage.buttonState)) {
|
||||||
|
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.UP);
|
||||||
|
} else if (MidnightControlsConfig.isBackButton(storage.axis, false, storage.buttonState)) {
|
||||||
|
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
|
||||||
|
} else if (MidnightControlsConfig.isLeftButton(storage.axis, false, storage.buttonState)) {
|
||||||
|
allowMouseControl = this.handleLeftRight(client.currentScreen, false);
|
||||||
|
} else if (MidnightControlsConfig.isRightButton(storage.axis, false, storage.buttonState)) {
|
||||||
|
allowMouseControl = this.handleLeftRight(client.currentScreen, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float movementX = 0.f;
|
||||||
|
float movementY = 0.f;
|
||||||
|
|
||||||
|
if (MidnightControlsConfig.isBackButton(storage.axis, false, (storage.value > 0 ? ButtonState.PRESS : ButtonState.RELEASE))) {
|
||||||
|
movementY = storage.absValue;
|
||||||
|
} else if (MidnightControlsConfig.isForwardButton(storage.axis, false, (storage.value > 0 ? ButtonState.PRESS : ButtonState.RELEASE))) {
|
||||||
|
movementY = -storage.absValue;
|
||||||
|
} else if (MidnightControlsConfig.isLeftButton(storage.axis, false, (storage.value > 0 ? ButtonState.PRESS : ButtonState.RELEASE))) {
|
||||||
|
movementX = -storage.absValue;
|
||||||
|
} else if (MidnightControlsConfig.isRightButton(storage.axis, false, (storage.value > 0 ? ButtonState.PRESS : ButtonState.RELEASE))) {
|
||||||
|
movementX = storage.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) < storage.deadZone && Math.abs(prevYAxis) < storage.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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryUtil.moveMouseToClosestSlot(client.currentScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevXAxis = movementX;
|
||||||
|
this.prevYAxis = movementY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleJoystickMovement(AxisStorage storage) {
|
||||||
|
float axisValue = storage.absValue;
|
||||||
|
if (!MidnightControlsConfig.analogMovement || (client.player != null && client.player.getVehicle() instanceof BoatEntity)) {
|
||||||
|
axisValue = (float) (storage.absValue - storage.deadZone);
|
||||||
|
axisValue /= (float) (1.0 - storage.deadZone);
|
||||||
|
axisValue *= (float) storage.deadZone;
|
||||||
|
}
|
||||||
|
axisValue = (float) Math.min(axisValue / MidnightControlsConfig.getAxisMaxValue(storage.axis), 1);
|
||||||
|
if (AxisStorage.isLeftAxis(storage.axis)) MidnightControlsCompat.handleMovement(storage, axisValue);
|
||||||
|
InputManager.BUTTON_VALUES.put(storage.getButtonId(true), storage.polarity == AxisStorage.Polarity.PLUS ? axisValue : 0.f);
|
||||||
|
InputManager.BUTTON_VALUES.put(storage.getButtonId(false), storage.polarity == AxisStorage.Polarity.MINUS ? axisValue : 0.f);
|
||||||
|
storage.absValue = axisValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleScreenScrolling(Screen screen, AxisStorage storage) {
|
||||||
|
if (screen == null) return false;
|
||||||
|
// @TODO allow rebinding to left stick
|
||||||
|
int preferredAxis = true ? GLFW_GAMEPAD_AXIS_RIGHT_Y : GLFW_GAMEPAD_AXIS_LEFT_Y;
|
||||||
|
|
||||||
|
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
|
||||||
|
if (storage.buttonState != ButtonState.NONE && !this.controlsInput.currentButtons.contains(storage.getButtonId(storage.buttonState == ButtonState.PRESS))) {
|
||||||
|
|
||||||
|
this.controlsInput.currentButtons.add(storage.getButtonId(storage.buttonState == ButtonState.PRESS));
|
||||||
|
|
||||||
|
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 true;
|
||||||
|
} else if (storage.absValue >= storage.deadZone) {
|
||||||
|
if (screen instanceof CreativeInventoryScreen creativeInventoryScreen) {
|
||||||
|
if (storage.axis == preferredAxis) {
|
||||||
|
var accessor = (CreativeInventoryScreenAccessor) creativeInventoryScreen;
|
||||||
|
if (accessor.midnightcontrols$hasScrollbar() && storage.absValue >= storage.deadZone) {
|
||||||
|
creativeInventoryScreen.mouseScrolled(0.0, 0.0, 0, -(storage.value * 0.0175f));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (screen instanceof AdvancementsScreen advancementsScreen) {
|
||||||
|
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_X || storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
|
||||||
|
var accessor = (AdvancementsScreenAccessor) advancementsScreen;
|
||||||
|
AdvancementTab tab = accessor.getSelectedTab();
|
||||||
|
tab.move(storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_X ? -storage.value * 1.0 : 0.0, storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y ? -storage.value * 5.0 : 0.0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (screen != null) {
|
||||||
|
if (storage.axis == preferredAxis && !handleListWidgetScrolling(screen.children(), storage.value)) {
|
||||||
|
try {
|
||||||
|
screen.mouseScrolled(0.0, 0.0, 0, -(storage.value * 0.0175f));
|
||||||
|
} catch (NullPointerException ignored) {}
|
||||||
|
} else if (isScreenInteractive(screen)) {
|
||||||
|
if (joystickCooldown == 0) {
|
||||||
|
switch (storage.axis) {
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_Y -> {
|
||||||
|
this.changeFocus(screen, storage.value > 0 ? NavigationDirection.UP : NavigationDirection.DOWN);
|
||||||
|
joystickCooldown = 4;
|
||||||
|
}
|
||||||
|
case GLFW_GAMEPAD_AXIS_LEFT_X -> {
|
||||||
|
this.handleLeftRight(screen, storage.value > 0);
|
||||||
|
joystickCooldown = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public void handleRadialMenu(float x, float y) {
|
||||||
|
int index = -1;
|
||||||
|
float border = 0.3f;
|
||||||
|
if (x < -border) {
|
||||||
|
index = 3;
|
||||||
|
if (y < -border) index = 0;
|
||||||
|
else if (y > border) index = 5;
|
||||||
|
} else if (x > border) {
|
||||||
|
index = 4;
|
||||||
|
if (y < -border) index = 2;
|
||||||
|
else if (y > border) index = 7;
|
||||||
|
} else {
|
||||||
|
if (y < -border) index = 1;
|
||||||
|
else if (y > border) index = 6;
|
||||||
|
}
|
||||||
|
if (client.currentScreen instanceof RingScreen && index > -1) RingPage.selected = index;
|
||||||
|
if (PlatformFunctions.isModLoaded("emotecraft") && EmotecraftCompat.isEmotecraftScreen(client.currentScreen)) EmotecraftCompat.handleEmoteSelector(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleListWidgetScrolling(List<? extends Element> children, float value) {
|
||||||
|
return children.stream().filter(element -> element instanceof SpruceEntryListWidget)
|
||||||
|
.map(element -> (SpruceEntryListWidget<?>) element)
|
||||||
|
.filter(AbstractSpruceWidget::isFocusedOrHovered)
|
||||||
|
.anyMatch(element -> {
|
||||||
|
element.mouseScrolled(0.0, 0.0, 0, -value);
|
||||||
|
return true;
|
||||||
|
}) ||
|
||||||
|
children.stream().filter(element -> element instanceof EntryListWidget)
|
||||||
|
.map(element -> (EntryListWidget<?>) element)
|
||||||
|
.filter(element -> element.getType().isFocused())
|
||||||
|
.anyMatch(element -> {
|
||||||
|
element.mouseScrolled(0.0, 0.0, 0, -value);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if (PlatformFunctions.isModLoaded("yet-another-config-lib") && YACLCompat.handleAButton(screen, focused)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else pressKeyboardKey(screen, GLFW_KEY_ENTER);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("yet-another-config-lib") && YACLCompat.handleLeftRight(screen, right)) {
|
||||||
|
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) {
|
||||||
|
switch (element) {
|
||||||
|
case SpruceElement spruceElement -> {
|
||||||
|
if (spruceElement.requiresCursor())
|
||||||
|
return true;
|
||||||
|
return !spruceElement.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
|
||||||
|
}
|
||||||
|
case SliderWidget slider -> {
|
||||||
|
if (slider.active) {
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// case AlwaysSelectedEntryListWidget<?> alwaysSelectedEntryListWidget -> {
|
||||||
|
// //TODO ((EntryListWidgetAccessor) element).midnightcontrols$moveSelection(right ? EntryListWidget.MoveDirection.DOWN : EntryListWidget.MoveDirection.UP);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
case ParentElement entryList -> {
|
||||||
|
var focused = entryList.getFocused();
|
||||||
|
if (focused == null)
|
||||||
|
return true;
|
||||||
|
return this.handleRightLeftElement(focused, right);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private double prevX = 0;
|
||||||
|
private double prevY = 0;
|
||||||
|
private double xValue;
|
||||||
|
private AxisStorage.Polarity xPolarity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the look direction input.
|
||||||
|
*
|
||||||
|
* @param storage the state of the provided axis
|
||||||
|
*/
|
||||||
|
public void handleLook(AxisStorage storage) {
|
||||||
|
if (client.player == null || !(storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y || storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_X)) return;
|
||||||
|
// Handles the look direction.
|
||||||
|
if (MidnightControlsConfig.cameraMode == CameraMode.FLAT) handleFlatLook(storage);
|
||||||
|
else handleAdaptiveLook(storage);
|
||||||
|
MidnightControlsCompat.handleCamera(this.targetYaw, this.targetPitch);
|
||||||
|
}
|
||||||
|
private void handleFlatLook(AxisStorage storage) {
|
||||||
|
if (storage.polarity != AxisStorage.Polarity.ZERO) {
|
||||||
|
double rotation = Math.pow(storage.value, 2.0) * 0.11D * storage.polarity.multiplier;
|
||||||
|
|
||||||
|
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) this.targetPitch = rotation * MidnightControlsConfig.getRightYAxisSign() * MidnightControlsConfig.yAxisRotationSpeed / 2;
|
||||||
|
else this.targetYaw = rotation * MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void handleAdaptiveLook(AxisStorage storage) {
|
||||||
|
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
|
||||||
|
xValue = storage.value;
|
||||||
|
xPolarity = storage.polarity;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
|
||||||
|
double xStep = (MidnightControlsConfig.rotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
|
||||||
|
float yValue = storage.value;
|
||||||
|
AxisStorage.Polarity yPolarity = storage.polarity;
|
||||||
|
|
||||||
|
double cursorDeltaX = 2 * xValue - this.prevX;
|
||||||
|
double cursorDeltaY = 2 * yValue - this.prevY;
|
||||||
|
boolean slowdown = client.options.getPerspective().isFirstPerson() && Objects.requireNonNull(client.player).isUsingSpyglass();
|
||||||
|
double x = cursorDeltaX * xStep * (slowdown ? xStep : 1);
|
||||||
|
double y = cursorDeltaY * yStep * (slowdown ? yStep : 1);
|
||||||
|
|
||||||
|
double powXValue = Math.pow(x, 2.0);
|
||||||
|
double powYValue = Math.pow(y, 2.0);
|
||||||
|
|
||||||
|
if (xPolarity != AxisStorage.Polarity.ZERO) {
|
||||||
|
double sign = MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed;
|
||||||
|
this.targetYaw = sign * powXValue * 0.11D * xPolarity.multiplier;
|
||||||
|
}
|
||||||
|
if (yPolarity != AxisStorage.Polarity.ZERO) {
|
||||||
|
double sign = MidnightControlsConfig.getRightYAxisSign() * MidnightControlsConfig.yAxisRotationSpeed;
|
||||||
|
this.targetPitch = sign * powYValue * 0.11D * yPolarity.multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevY = yValue;
|
||||||
|
this.prevX = xValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void handleTouchscreenLook(AxisStorage storage) {
|
||||||
|
if (storage.polarity != AxisStorage.Polarity.ZERO) {
|
||||||
|
double rotation = storage.value * 0.11D * MidnightControlsConfig.touchSpeed/5;
|
||||||
|
|
||||||
|
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) this.targetPitch = rotation;
|
||||||
|
else this.targetYaw = rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
switch (direction) {
|
||||||
|
case UP -> pressKeyboardKey(screen, GLFW.GLFW_KEY_UP);
|
||||||
|
case DOWN -> pressKeyboardKey(screen, GLFW.GLFW_KEY_DOWN);
|
||||||
|
case LEFT -> pressKeyboardKey(screen, GLFW.GLFW_KEY_LEFT);
|
||||||
|
case RIGHT -> pressKeyboardKey(screen, GLFW.GLFW_KEY_RIGHT);
|
||||||
|
}
|
||||||
|
this.actionGuiCooldown = 5;
|
||||||
|
return true;
|
||||||
|
} catch (Exception exception) {MidnightControls.warn("Unknown exception encountered while trying to change focus: "+exception);}
|
||||||
|
return 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");
|
||||||
|
if (screen instanceof KeybindsScreen) return false;
|
||||||
|
|
||||||
|
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 (element.getMessage().getContent() instanceof TranslatableTextContent translatableText) {
|
||||||
|
if (set.stream().anyMatch(key -> translatableText.getKey().equals(key))) {
|
||||||
|
element.onPress();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isScreenInteractive(@NotNull Screen screen) {
|
||||||
|
return !(screen instanceof HandledScreen || MidnightControlsConfig.joystickAsMouse || MidnightControlsConfig.mouseScreens.stream().anyMatch(a -> screen.getClass().toString().contains(a))
|
||||||
|
|| (screen instanceof SpruceScreen && ((SpruceScreen) screen).requiresCursor())
|
||||||
|
|| MidnightControlsCompat.requireMouseOnScreen(screen));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pressKeyboardKey(MinecraftClient client, int key) {
|
||||||
|
client.keyboard.onKey(client.getWindow().getHandle(), key, 0, 1, 0);
|
||||||
|
}
|
||||||
|
public void pressKeyboardKey(Screen screen, int key) {
|
||||||
|
screen.keyPressed(key, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* 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.entity.attribute.EntityAttributes;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
this.lastReacharoundResult = this.tryVerticalReachAround();
|
||||||
|
if (this.lastReacharoundResult == null) {
|
||||||
|
this.lastReacharoundResult = this.tryHorizontalReachAround();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getPlayerRange(@NotNull MinecraftClient client) {
|
||||||
|
return client.player != null ? Double.valueOf(client.player.getAttributeValue(EntityAttributes.BLOCK_INTERACTION_RANGE)).floatValue() : 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a nullable block hit result if vertical reach-around is possible.
|
||||||
|
*
|
||||||
|
* @return a block hit result if vertical reach-around is possible, else {@code null}
|
||||||
|
*/
|
||||||
|
public @Nullable BlockHitResult tryVerticalReachAround() {
|
||||||
|
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.isReplaceable())) {
|
||||||
|
return new BlockHitResult(result.getPos(), Direction.DOWN, blockPos, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a nullable block hit result if horizontal reach-around is possible.
|
||||||
|
*
|
||||||
|
* @return a block hit result if horizontal reach-around is possible
|
||||||
|
*/
|
||||||
|
public @Nullable BlockHitResult tryHorizontalReachAround() {
|
||||||
|
if (!MidnightControlsFeature.HORIZONTAL_REACHAROUND.isAvailable())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (client.world != null && 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;
|
||||||
|
// Temporary pos, do not use
|
||||||
|
Vec3d playerPosi = client.player.getPos();
|
||||||
|
|
||||||
|
// Imitates var playerPos = client.player.getBlockPos().down();
|
||||||
|
Vec3d playerPos = new Vec3d(playerPosi.getX(), playerPosi.getY() - 1.0, playerPosi.getZ());
|
||||||
|
if (client.player.getY() - playerPos.getY() - 1.0 >= 0.25) {
|
||||||
|
// Imitates playerPos = playerPos.up();
|
||||||
|
playerPos = playerPosi;
|
||||||
|
this.onSlab = true;
|
||||||
|
} else {
|
||||||
|
this.onSlab = false;
|
||||||
|
}
|
||||||
|
var targetPos = new Vec3d(client.crosshairTarget.getPos().getX(), client.crosshairTarget.getPos().getY(), client.crosshairTarget.getPos().getZ()).subtract(playerPos);
|
||||||
|
var vector = new Vec3d(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
|
||||||
|
var blockPos = playerPos.add(vector);
|
||||||
|
|
||||||
|
// Some functions still need BlockPos, so this is here to let that happen
|
||||||
|
var blockyPos = BlockPos.ofFloored(blockPos);
|
||||||
|
|
||||||
|
var direction = client.player.getHorizontalFacing();
|
||||||
|
|
||||||
|
var state = client.world.getBlockState(blockyPos);
|
||||||
|
if (!state.isAir())
|
||||||
|
return null;
|
||||||
|
var adjacentBlockState = client.world.getBlockState(blockyPos.offset(direction.getOpposite()));
|
||||||
|
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BlockHitResult(blockPos, direction, blockyPos, 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,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2022 Motschen <motschen@midnightdust.eu>
|
||||||
|
*
|
||||||
|
* 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 me.juancarloscp52.bedrockify.client.BedrockifyClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents HQM compatibility handler.
|
||||||
|
*
|
||||||
|
* @author Motschen
|
||||||
|
* @version 1.7.0
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
public class BedrockifyCompat implements CompatHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle() {
|
||||||
|
BedrockifyClient.getInstance().settings.disableFlyingMomentum = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.storage.AxisStorage;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
default void handle() {
|
||||||
|
handle(MidnightControlsClient.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles compatibility of a mod.
|
||||||
|
*
|
||||||
|
* @param mod this mod instance
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default void handle(@NotNull MidnightControlsClient mod) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles custom camera updates
|
||||||
|
*
|
||||||
|
* @param client the Minecraft client instance
|
||||||
|
*/
|
||||||
|
default void handleCamera(@NotNull MinecraftClient client, double targetYaw, double targetPitch) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles movement for players as well as vehicles
|
||||||
|
*
|
||||||
|
* @param storage the storage containing info about the current axis
|
||||||
|
* @param adjustedValue the value of the axis, adjusted for max values and non-analogue movement, recommended for player movement
|
||||||
|
*/
|
||||||
|
default void handleMovement(@NotNull MinecraftClient client, AxisStorage storage, float adjustedValue) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles custom tab behavior
|
||||||
|
*
|
||||||
|
* @param forward whether the direction is forward or backward
|
||||||
|
*/
|
||||||
|
default boolean handleTabs(Screen screen, boolean forward) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles custom page behavior
|
||||||
|
*
|
||||||
|
* @param forward whether the direction is forward or backward
|
||||||
|
*/
|
||||||
|
default boolean handlePages(Screen screen, boolean forward) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,47 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat;
|
||||||
|
|
||||||
|
import dev.emi.emi.api.EmiApi;
|
||||||
|
import dev.emi.emi.config.EmiConfig;
|
||||||
|
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 org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
|
||||||
|
public class EMICompat implements CompatHandler {
|
||||||
|
public static boolean handleEmiPages(boolean direction) {
|
||||||
|
if (isEMIEnabled() && MidnightControlsClient.input.actionGuiCooldown == 0 && EmiScreenManager.getSearchPanel() != null && EmiScreenManager.getSearchPanel().pageLeft != null && EmiScreenManager.getSearchPanel().pageRight != null) {
|
||||||
|
if (direction) EmiScreenManager.getSearchPanel().pageRight.onPress();
|
||||||
|
else EmiScreenManager.getSearchPanel().pageLeft.onPress();
|
||||||
|
MidnightControlsClient.input.actionGuiCooldown = 5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void handle() {
|
||||||
|
ButtonCategory category = new ButtonCategory(id("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)->handleEmiPages(false)).cooldown()
|
||||||
|
.filter(((buttonBinding) -> EmiApi.getHandledScreen() != null))
|
||||||
|
.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)->handleEmiPages(true)).cooldown()
|
||||||
|
.filter(((buttonBinding) -> EmiApi.getHandledScreen() != null))
|
||||||
|
.register();
|
||||||
|
}
|
||||||
|
public static boolean isEMIEnabled() {
|
||||||
|
return EmiConfig.enabled;
|
||||||
|
}
|
||||||
|
public static boolean isSearchBarCentered() {
|
||||||
|
return EmiConfig.centerSearchBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import io.github.kosmx.emotes.arch.gui.EmoteMenuImpl;
|
||||||
|
import io.github.kosmx.emotes.arch.gui.screen.ingame.FastChosseScreen;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
|
||||||
|
public class EmotecraftCompat {
|
||||||
|
private static final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
|
public static void openEmotecraftScreen(Screen parent) {
|
||||||
|
client.setScreen(new EmoteMenuImpl(parent));
|
||||||
|
}
|
||||||
|
public static boolean isEmotecraftScreen(Screen screen) {
|
||||||
|
return screen instanceof FastChosseScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleEmoteSelector(int index) {
|
||||||
|
if (client.currentScreen instanceof FastChosseScreen) {
|
||||||
|
int x = client.getWindow().getWidth() / 2;
|
||||||
|
int y = client.getWindow().getHeight() / 2;
|
||||||
|
switch (index) {
|
||||||
|
case 0, 3, 5 -> x -= 200;
|
||||||
|
case 2, 4, 7 -> x += 200;
|
||||||
|
}
|
||||||
|
switch (index) {
|
||||||
|
case 0, 1, 2 -> y -= 200;
|
||||||
|
case 5, 6, 7 -> y += 200;
|
||||||
|
}
|
||||||
|
InputManager.queueMousePosition(x, y);
|
||||||
|
|
||||||
|
InputManager.INPUT_MANAGER.updateMousePosition(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.minecraft.client.gui.screen.Screen;
|
||||||
|
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
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,43 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat;
|
||||||
|
|
||||||
|
import com.kqp.inventorytabs.tabs.TabManager;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||||
|
|
||||||
|
public class InventoryTabsCompat implements CompatHandler {
|
||||||
|
@Override
|
||||||
|
public boolean handleTabs(Screen screen, boolean next) {
|
||||||
|
if (screen instanceof HandledScreen<?> && !(screen instanceof CreativeInventoryScreen)) {
|
||||||
|
TabManager tabManager = TabManager.getInstance();
|
||||||
|
int tabIndex = tabManager.tabs.indexOf(tabManager.currentTab);
|
||||||
|
if (next) {
|
||||||
|
if (tabIndex < tabManager.tabs.size() - 1) tabManager.onTabClick(tabManager.tabs.get(tabIndex + 1));
|
||||||
|
else tabManager.onTabClick(tabManager.tabs.getFirst());
|
||||||
|
} else {
|
||||||
|
if (tabIndex > 0) tabManager.onTabClick(tabManager.tabs.get(tabIndex - 1));
|
||||||
|
else tabManager.onTabClick(tabManager.tabs.getLast());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean handlePages(Screen screen, boolean next) {
|
||||||
|
if (screen instanceof HandledScreen<?> && !(screen instanceof CreativeInventoryScreen)) {
|
||||||
|
TabManager tabManager = TabManager.getInstance();
|
||||||
|
if (next) {
|
||||||
|
if (tabManager.canGoForwardAPage()) {
|
||||||
|
tabManager.setCurrentPage(tabManager.currentPage + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tabManager.canGoBackAPage()) {
|
||||||
|
tabManager.setCurrentPage(tabManager.currentPage - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 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,201 @@
|
|||||||
|
/*
|
||||||
|
* 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.lib.util.PlatformFunctions;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.storage.AxisStorage;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.log;
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a compatibility handler.
|
||||||
|
*
|
||||||
|
* @author LambdAurora, Motschen
|
||||||
|
* @version 1.10.0
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public class MidnightControlsCompat {
|
||||||
|
@Deprecated // INTERNAL -> PLEASE USE streamCompatHandlers() INSTEAD
|
||||||
|
public static final List<CompatHandler> HANDLERS = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes compatibility with other mods if needed.
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
if (PlatformFunctions.isModLoaded("emi")) {
|
||||||
|
log("Adding EMI compatibility...");
|
||||||
|
registerCompatHandler(new EMICompat());
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("hardcorequesting") && LambdaReflection.doesClassExist(HQMCompat.GUI_BASE_CLASS_PATH)) {
|
||||||
|
log("Adding HQM compatibility...");
|
||||||
|
registerCompatHandler(new HQMCompat());
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("bedrockify")) {
|
||||||
|
log("Adding Bedrockify compatibility...");
|
||||||
|
registerCompatHandler(new BedrockifyCompat());
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("yet-another-config-lib")) {
|
||||||
|
log("Adding YACL compatibility...");
|
||||||
|
registerCompatHandler(new YACLCompat());
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("sodium")) {
|
||||||
|
log("Adding Sodium compatibility...");
|
||||||
|
registerCompatHandler(new SodiumCompat());
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("inventorytabs")) {
|
||||||
|
log("Adding Inventory Tabs compatibility...");
|
||||||
|
registerCompatHandler(new InventoryTabsCompat());
|
||||||
|
}
|
||||||
|
HANDLERS.forEach(CompatHandler::handle);
|
||||||
|
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 streamCompatHandlers().anyMatch(handler -> handler.requireMouseOnScreen(screen));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles custom tabs for modded screens
|
||||||
|
*
|
||||||
|
* @param screen the screen
|
||||||
|
* @return true if the handle was fired and succeed, else false
|
||||||
|
*/
|
||||||
|
public static boolean handleTabs(Screen screen, boolean forward) {
|
||||||
|
return streamCompatHandlers().anyMatch(handler -> handler.handleTabs(screen, forward));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles custom pages for modded screens
|
||||||
|
*
|
||||||
|
* @param screen the screen
|
||||||
|
* @return true if the handle was fired and succeed, else false
|
||||||
|
*/
|
||||||
|
public static boolean handlePages(Screen screen, boolean forward) {
|
||||||
|
return streamCompatHandlers().anyMatch(handler -> handler.handlePages(screen, forward));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles the camera movement.
|
||||||
|
*
|
||||||
|
* @param targetYaw the target yaw
|
||||||
|
* @param targetPitch the target pitch
|
||||||
|
*/
|
||||||
|
public static void handleCamera(double targetYaw, double targetPitch) {
|
||||||
|
MidnightControlsCompat.HANDLERS.forEach(handler -> handler.handleCamera(client, targetYaw, targetPitch));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles movement for players as well as vehicles
|
||||||
|
*
|
||||||
|
* @param storage the storage containing info about the current axis
|
||||||
|
* @param adjustedValue the value of the axis, adjusted for max values and non-analogue movement, recommended for player movement
|
||||||
|
*/
|
||||||
|
public static void handleMovement(AxisStorage storage, float adjustedValue) {
|
||||||
|
streamCompatHandlers().forEach(handler -> handler.handleMovement(client, storage, adjustedValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.lib.util.PlatformFunctions;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This plugin is only present for the conditional mixins.
|
||||||
|
*
|
||||||
|
* @author LambdAurora & Motschen
|
||||||
|
* @version 1.6.0
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class MidnightControlsMixinPlugin implements IMixinConfigPlugin {
|
||||||
|
private String mixinPackage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad(String mixinPackage) {
|
||||||
|
this.mixinPackage = mixinPackage + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRefMapperConfig() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||||
|
final String mixinName = mixinClassName.substring(this.mixinPackage.length());
|
||||||
|
final String packageName = mixinName.substring(0, mixinName.lastIndexOf('.'));
|
||||||
|
|
||||||
|
if (packageName.startsWith("sodium") && !PlatformFunctions.isModLoaded("sodium"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return 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,20 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.mixin.sodium.SodiumOptionsGUIAccessor;
|
||||||
|
import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
|
||||||
|
public class SodiumCompat implements CompatHandler {
|
||||||
|
@Override
|
||||||
|
public boolean 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));
|
||||||
|
optionsGUI.setPage(accessor.getPages().get(i));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat;
|
||||||
|
|
||||||
|
import dev.isxander.yacl.gui.AbstractWidget;
|
||||||
|
import dev.isxander.yacl.gui.OptionListWidget;
|
||||||
|
import dev.isxander.yacl.gui.YACLScreen;
|
||||||
|
import dev.isxander.yacl.gui.controllers.ControllerWidget;
|
||||||
|
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
|
||||||
|
import net.minecraft.client.gui.Element;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
public class YACLCompat implements CompatHandler {
|
||||||
|
public static boolean handleAButton(Screen screen, Element element) {
|
||||||
|
if (element instanceof AbstractWidget abstractWidget) {
|
||||||
|
// imitate enter key press
|
||||||
|
return abstractWidget.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean handleLeftRight(Screen screen, boolean direction) {
|
||||||
|
if (screen instanceof YACLScreen yaclScreen) {
|
||||||
|
SliderControllerElement focusedSlider = yaclScreen.optionList.children().stream()
|
||||||
|
.filter(OptionListWidget.OptionEntry.class::isInstance)
|
||||||
|
.map(entry -> ((OptionListWidget.OptionEntry) entry).widget)
|
||||||
|
.filter(ControllerWidget.class::isInstance)
|
||||||
|
.map(ControllerWidget.class::cast)
|
||||||
|
.filter(SliderControllerElement.class::isInstance)
|
||||||
|
.map(SliderControllerElement.class::cast)
|
||||||
|
.filter(ControllerWidget::isHovered)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (focusedSlider == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
focusedSlider.incrementValue(direction ? 1 : -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleTabs(Screen screen, boolean direction) {
|
||||||
|
if (screen instanceof YACLScreen yaclScreen) {
|
||||||
|
int categoryIdx = yaclScreen.getCurrentCategoryIdx();
|
||||||
|
if (direction) categoryIdx++; else categoryIdx--;
|
||||||
|
if (categoryIdx < 0) categoryIdx = yaclScreen.config.categories().size() - 1;
|
||||||
|
if (categoryIdx >= yaclScreen.config.categories().size()) categoryIdx = 0;
|
||||||
|
|
||||||
|
yaclScreen.changeCategory(categoryIdx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.compat.mixin.sodium;
|
||||||
|
|
||||||
|
import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||||
|
import net.caffeinemc.mods.sodium.client.gui.options.OptionPage;
|
||||||
|
import net.caffeinemc.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,660 @@
|
|||||||
|
/*
|
||||||
|
* 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.base.Predicates;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
|
||||||
|
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.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
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 CONTROLS_RING = new Builder("controls_ring").buttons(GLFW_GAMEPAD_BUTTON_GUIDE).onlyInGame().cooldown()
|
||||||
|
.action((client, button1, value, action) -> {
|
||||||
|
if (action.isPressed()) {
|
||||||
|
MidnightControlsClient.ring.loadFromUnbound();
|
||||||
|
client.setScreen(new RingScreen());
|
||||||
|
}
|
||||||
|
if (action.isUnpressed() && client.currentScreen != null) client.currentScreen.close();
|
||||||
|
return true;
|
||||||
|
}).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 EXIT = new Builder("exit").buttons(GLFW_GAMEPAD_BUTTON_B).filter((buttonBinding) -> client.currentScreen != null && buttonBinding.cooldown == 0 && INVENTORY.cooldown == 0)
|
||||||
|
.action(InputHandlers.handleExit()).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.inGameHud.getDebugHud().toggleDebugHud(); 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 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)
|
||||||
|
.actions(InputHandlers::handleToggleSprint).onlyInGame().cooldown().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((binding) -> client.currentScreen != null)).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((binding) -> client.currentScreen != null)).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(30).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(30).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 Predicate<ButtonBinding> filter;
|
||||||
|
private final List<PressAction> actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION));
|
||||||
|
private final boolean hasCooldown;
|
||||||
|
private int cooldownLength = 5;
|
||||||
|
private int cooldown = 0;
|
||||||
|
private boolean pressed = false;
|
||||||
|
|
||||||
|
public ButtonBinding(String key, int[] defaultButton, List<PressAction> actions, Predicate<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, List<PressAction> actions, Predicate<ButtonBinding> filter, boolean hasCooldown, int cooldownLength) {
|
||||||
|
this.setButton(this.defaultButton = defaultButton);
|
||||||
|
this.key = key;
|
||||||
|
this.text = Text.translatable(this.key);
|
||||||
|
this.filter = filter;
|
||||||
|
this.actions.addAll(actions);
|
||||||
|
this.hasCooldown = hasCooldown;
|
||||||
|
this.cooldownLength = cooldownLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ButtonBinding(String key, int[] defaultButton, boolean hasCooldown) {
|
||||||
|
this(key, defaultButton, Collections.emptyList(), Predicates.alwaysTrue(), hasCooldown);
|
||||||
|
}
|
||||||
|
public ButtonBinding(String key, int[] defaultButton, boolean hasCooldown, int cooldownLength) {
|
||||||
|
this(key, defaultButton, Collections.emptyList(), Predicates.alwaysTrue(), hasCooldown, cooldownLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the button press state.
|
||||||
|
*
|
||||||
|
* @param pressed whether the button is pressed
|
||||||
|
*/
|
||||||
|
public void setPressed(boolean pressed) {
|
||||||
|
this.pressed = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @deprecated Use {@link #isPressed()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean isButtonDown() {
|
||||||
|
return isPressed();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns whether this button is down or not.
|
||||||
|
*
|
||||||
|
* @return true if the button is down, else false
|
||||||
|
*/
|
||||||
|
public boolean isPressed() {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @return true if the button binding is available, else false
|
||||||
|
*/
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return this.filter.test(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 = cooldownLength;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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.EXIT,
|
||||||
|
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,
|
||||||
|
ButtonBinding.CONTROLS_RING
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Predicate<ButtonBinding> filter = Predicates.alwaysTrue();
|
||||||
|
private boolean cooldown = false;
|
||||||
|
private int cooldownLength = 5;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
@Deprecated
|
||||||
|
public Builder(@NotNull org.aperlambda.lambdacommon.Identifier identifier) {
|
||||||
|
this(identifier.getNamespace() + "." + identifier.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Predicate<ButtonBinding> filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the filter of {@link ButtonBinding} to only in game.
|
||||||
|
*
|
||||||
|
* @return the builder instance
|
||||||
|
* @see #filter(Predicate)
|
||||||
|
* @see InputHandlers#inGame(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(Predicate)
|
||||||
|
* @see InputHandlers#inInventory(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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the cooldown enabled with a custom duration for {@link ButtonBinding}.
|
||||||
|
*
|
||||||
|
* @param cooldownLength duration of {@link ButtonBinding} cooldown
|
||||||
|
* @return the builder instance
|
||||||
|
*/
|
||||||
|
public Builder cooldown(int cooldownLength) {
|
||||||
|
this.cooldownLength = cooldownLength;
|
||||||
|
this.cooldown = true;
|
||||||
|
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, this.cooldownLength);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.controller;
|
package eu.midnightdust.midnightcontrols.client.controller;
|
||||||
|
|
||||||
import net.minecraft.client.resource.language.I18n;
|
import net.minecraft.client.resource.language.I18n;
|
||||||
import org.aperlambda.lambdacommon.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import org.aperlambda.lambdacommon.utils.Identifiable;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -26,47 +25,49 @@ import java.util.List;
|
|||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public class ButtonCategory implements Identifiable
|
public class ButtonCategory {
|
||||||
{
|
|
||||||
private final List<ButtonBinding> bindings = new ArrayList<>();
|
private final List<ButtonBinding> bindings = new ArrayList<>();
|
||||||
private final Identifier id;
|
private final Identifier id;
|
||||||
private int priority;
|
private final int priority;
|
||||||
|
|
||||||
public ButtonCategory(@NotNull Identifier id, int priority)
|
public ButtonCategory(@NotNull Identifier id, int priority) {
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ButtonCategory(@NotNull Identifier id)
|
public ButtonCategory(@NotNull Identifier id) {
|
||||||
{
|
this(id, 100);
|
||||||
|
}
|
||||||
|
@Deprecated
|
||||||
|
public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id, int priority) {
|
||||||
|
this(Identifier.of(id.getNamespace(), id.getName()), priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id) {
|
||||||
this(id, 100);
|
this(id, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerBinding(@NotNull ButtonBinding binding)
|
public void registerBinding(@NotNull ButtonBinding binding) {
|
||||||
{
|
|
||||||
if (this.bindings.contains(binding))
|
if (this.bindings.contains(binding))
|
||||||
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
|
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
|
||||||
this.bindings.add(binding);
|
this.bindings.add(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerAllBindings(@NotNull ButtonBinding... bindings)
|
public void registerAllBindings(@NotNull ButtonBinding... bindings) {
|
||||||
{
|
|
||||||
this.registerAllBindings(Arrays.asList(bindings));
|
this.registerAllBindings(Arrays.asList(bindings));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerAllBindings(@NotNull List<ButtonBinding> bindings)
|
public void registerAllBindings(@NotNull List<ButtonBinding> bindings) {
|
||||||
{
|
|
||||||
bindings.forEach(this::registerBinding);
|
bindings.forEach(this::registerBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the bindings assigned to this category.
|
* Gets the bindings assigned to this category.
|
||||||
*
|
*
|
||||||
* @return The bindings assigned to this category.
|
* @return the bindings assigned to this category
|
||||||
*/
|
*/
|
||||||
public @NotNull List<ButtonBinding> getBindings()
|
public @NotNull List<ButtonBinding> getBindings() {
|
||||||
{
|
|
||||||
return Collections.unmodifiableList(this.bindings);
|
return Collections.unmodifiableList(this.bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,30 +76,26 @@ public class ButtonCategory implements Identifiable
|
|||||||
* <p>
|
* <p>
|
||||||
* The translation key should be `modid.identifier_name`.
|
* The translation key should be `modid.identifier_name`.
|
||||||
*
|
*
|
||||||
* @return The translated name.
|
* @return the translated name
|
||||||
*/
|
*/
|
||||||
public @NotNull String getTranslatedName()
|
public @NotNull String getTranslatedName() {
|
||||||
{
|
|
||||||
if (this.id.getNamespace().equals("minecraft"))
|
if (this.id.getNamespace().equals("minecraft"))
|
||||||
return I18n.translate(this.id.getName());
|
return I18n.translate(this.id.getPath());
|
||||||
else
|
else
|
||||||
return I18n.translate(this.id.getNamespace() + "." + this.id.getName());
|
return I18n.translate(this.id.getNamespace() + "." + this.id.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the priority display of this category.
|
* Gets the priority display of this category.
|
||||||
* It will defines in which order the categories will display on the controls screen.
|
* It will defines in which order the categories will display on the controls screen.
|
||||||
*
|
*
|
||||||
* @return The priority of this category.
|
* @return the priority of this category
|
||||||
*/
|
*/
|
||||||
public int getPriority()
|
public int getPriority() {
|
||||||
{
|
|
||||||
return this.priority;
|
return this.priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public @NotNull Identifier getIdentifier() {
|
||||||
public @NotNull Identifier getIdentifier()
|
|
||||||
{
|
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* 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.URI;
|
||||||
|
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 java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import static org.lwjgl.BufferUtils.createByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a controller.
|
||||||
|
*
|
||||||
|
* @author LambdAurora
|
||||||
|
* @version 1.7.0
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public record Controller(int id) {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public @NotNull 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) {
|
||||||
|
MidnightControls.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
|
||||||
|
* @return the resource data
|
||||||
|
* @throws IOException If an IO error occurs.
|
||||||
|
*/
|
||||||
|
private static ByteBuffer ioResourceToBuffer(String resource) 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() {
|
||||||
|
CompletableFuture.supplyAsync(Controller::updateMappingsSync);
|
||||||
|
}
|
||||||
|
private static boolean updateMappingsSync() {
|
||||||
|
try {
|
||||||
|
MidnightControls.log("Updating controller mappings...");
|
||||||
|
Optional<File> databaseFile = getDatabaseFile();
|
||||||
|
if (databaseFile.isPresent()) {
|
||||||
|
var database = ioResourceToBuffer(databaseFile.get().getPath());
|
||||||
|
if (database != null) GLFW.glfwUpdateGamepadMappings(database);
|
||||||
|
}
|
||||||
|
if (!MidnightControlsClient.MAPPINGS_FILE.exists())
|
||||||
|
return false;
|
||||||
|
var buffer = ioResourceToBuffer(MidnightControlsClient.MAPPINGS_FILE.getPath());
|
||||||
|
if (buffer != null) GLFW.glfwUpdateGamepadMappings(buffer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.fillInStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.PERIODIC_NOTIFICATION,
|
||||||
|
Text.translatable("midnightcontrols.controller.mappings.error"), Text.literal(string)));
|
||||||
|
}
|
||||||
|
MidnightControls.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.log(String.format("Controller #%d name: \"%s\"\n GUID: %s\n Gamepad: %s",
|
||||||
|
controller.id,
|
||||||
|
controller.getName(),
|
||||||
|
controller.getGuid(),
|
||||||
|
controller.isGamepad()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<File> getDatabaseFile() {
|
||||||
|
File databaseFile = new File("config/gamecontrollerdatabase.txt");
|
||||||
|
try {
|
||||||
|
BufferedInputStream in = new BufferedInputStream(URI.create("https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt").toURL().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 e) {return Optional.empty();}
|
||||||
|
return Optional.of(databaseFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* 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.Lists;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightInput;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.InventoryTabsCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.*;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.InventoryUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.ToggleSneakSprintUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.platform.ItemGroupUtil;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.screen.TitleScreen;
|
||||||
|
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.*;
|
||||||
|
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
|
||||||
|
import net.minecraft.client.gui.widget.TabNavigationWidget;
|
||||||
|
import net.minecraft.client.util.ScreenshotRecorder;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
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 in-game
|
||||||
|
if (client.currentScreen == null && client.player != null) {
|
||||||
|
if (!client.player.isSpectator()) {
|
||||||
|
var inv = client.player.getInventory();
|
||||||
|
if (next)
|
||||||
|
inv.setSelectedSlot(inv.selectedSlot < 8 ? inv.selectedSlot + 1 : inv.selectedSlot - 8);
|
||||||
|
else
|
||||||
|
inv.setSelectedSlot(inv.selectedSlot > 0 ? inv.selectedSlot - 1 : inv.selectedSlot + 8);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (client.inGameHud.getSpectatorHud().isOpen()) {
|
||||||
|
client.inGameHud.getSpectatorHud().cycleSlot(next ? -1 : 1);
|
||||||
|
} else {
|
||||||
|
float g = MathHelper.clamp(client.player.getAbilities().getFlySpeed() + (next ? 1 : -1) * 0.005F, 0.0F, 0.2F);
|
||||||
|
client.player.getAbilities().setFlySpeed(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (client.currentScreen instanceof RingScreen) {
|
||||||
|
MidnightControlsClient.ring.cyclePage(next);
|
||||||
|
} else if (client.currentScreen instanceof CreativeInventoryScreenAccessor inventory) {
|
||||||
|
inventory.midnightcontrols$setSelectedTab(ItemGroupUtil.cycleTab(next, client));
|
||||||
|
return true;
|
||||||
|
} else if (client.currentScreen instanceof RecipeBookScreen<?> recipeBookScreen) {
|
||||||
|
RecipeBookWidget<?> recipeBook = ((RecipeBookScreenAccessor) recipeBookScreen).getRecipeBook();
|
||||||
|
|
||||||
|
var recipeBookAccessor = (RecipeBookWidgetAccessor) recipeBook;
|
||||||
|
var tabs = recipeBookAccessor.getTabButtons();
|
||||||
|
var currentTab = recipeBookAccessor.getCurrentTab();
|
||||||
|
if (currentTab == null || !recipeBook.isOpen()) {
|
||||||
|
return MidnightControlsCompat.handleTabs(client.currentScreen, next);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
recipeBookAccessor.setCurrentTab(currentTab = tabs.get(nextTab));
|
||||||
|
currentTab.setToggled(true);
|
||||||
|
recipeBookScreen.refreshRecipeBook();
|
||||||
|
return true;
|
||||||
|
} else if (client.currentScreen instanceof AdvancementsScreenAccessor screen) {
|
||||||
|
var tabs = screen.getTabs().values().stream().distinct().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().getAdvancementEntry(), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (client.currentScreen != null && client.currentScreen.children().stream().anyMatch(e -> e instanceof TabNavigationWidget)) {
|
||||||
|
return Lists.newCopyOnWriteArrayList(client.currentScreen.children()).stream().anyMatch(e -> {
|
||||||
|
if (e instanceof TabNavigationWidget tabs) {
|
||||||
|
TabNavigationWidgetAccessor accessor = (TabNavigationWidgetAccessor) tabs;
|
||||||
|
int tabIndex = accessor.getTabs().indexOf(accessor.getTabManager().getCurrentTab());
|
||||||
|
if (next ? tabIndex+1 < accessor.getTabs().size() : tabIndex > 0) {
|
||||||
|
if (next) tabs.selectTab(tabIndex + 1, true);
|
||||||
|
else tabs.selectTab(tabIndex - 1, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else return MidnightControlsCompat.handleTabs(client.currentScreen, next);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PressAction handlePage(boolean next) {
|
||||||
|
return (client, button, value, action) -> {
|
||||||
|
if (action == ButtonState.RELEASE)
|
||||||
|
return false;
|
||||||
|
if (client.currentScreen instanceof CreativeInventoryScreen creativeScreen) {
|
||||||
|
return ItemGroupUtil.cyclePage(next, creativeScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return MidnightControlsCompat.handlePages(client.currentScreen, next);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static PressAction handleExit() {
|
||||||
|
return (client, button, value, action) -> {
|
||||||
|
if (client.currentScreen != null && client.currentScreen.getClass() != TitleScreen.class) {
|
||||||
|
if (!MidnightControlsCompat.handleMenuBack(client, client.currentScreen))
|
||||||
|
if (!MidnightControlsClient.input.tryGoBack(client.currentScreen))
|
||||||
|
client.currentScreen.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
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.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 = accessor.midnightcontrols$getSlotAt(x, y);
|
||||||
|
|
||||||
|
int slotId;
|
||||||
|
if (slot == null) {
|
||||||
|
if (button.getName().equals("take_all")) {
|
||||||
|
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.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "take" -> {
|
||||||
|
clickData = GLFW_MOUSE_BUTTON_2;
|
||||||
|
}
|
||||||
|
case "quick_move" -> {
|
||||||
|
actionType = SlotActionType.QUICK_MOVE;
|
||||||
|
}
|
||||||
|
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.currentScreen instanceof RingScreen)
|
||||||
|
client.openGameMenu(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) {
|
||||||
|
return ToggleSneakSprintUtil.toggleSneak(button);
|
||||||
|
}
|
||||||
|
public static boolean handleToggleSprint(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
|
||||||
|
return ToggleSneakSprintUtil.toggleSprint(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
Optional<Slot> closestSlot = InventoryUtil.findClosestSlot(inventory, direction);
|
||||||
|
|
||||||
|
if (closestSlot.isPresent()) {
|
||||||
|
var slot = closestSlot.get();
|
||||||
|
int x = accessor.getX() + slot.x + 8;
|
||||||
|
int y = accessor.getY() + 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 binding the affected binding
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public static boolean always(@NotNull ButtonBinding binding) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the client is in game or not.
|
||||||
|
*
|
||||||
|
* @param binding the affected binding
|
||||||
|
* @return true if the client is in game, else false
|
||||||
|
*/
|
||||||
|
public static boolean inGame(@NotNull ButtonBinding binding) {
|
||||||
|
return (client.currentScreen == null && MidnightControlsClient.input.screenCloseCooldown <= 0) || client.currentScreen instanceof TouchscreenOverlay || client.currentScreen instanceof RingScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the client is in a non-interactive screen (which means require mouse input) or not.
|
||||||
|
*
|
||||||
|
* @param binding the affected binding
|
||||||
|
* @return true if the client is in a non-interactive screen, else false
|
||||||
|
*/
|
||||||
|
public static boolean inNonInteractiveScreens(@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 binding the affected binding
|
||||||
|
* @return true if the client is in an inventory, else false
|
||||||
|
*/
|
||||||
|
public static boolean inInventory(@NotNull ButtonBinding binding) {
|
||||||
|
return client.currentScreen instanceof HandledScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the client is in the advancements screen or not.
|
||||||
|
*
|
||||||
|
* @param binding the affected binding
|
||||||
|
* @return true if the client is in the advancements screen, else false
|
||||||
|
*/
|
||||||
|
public static boolean inAdvancements(@NotNull ButtonBinding binding) {
|
||||||
|
return client.currentScreen instanceof AdvancementsScreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
/*
|
||||||
|
* 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.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.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.resource.language.I18n;
|
||||||
|
import net.minecraft.client.util.InputUtil;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
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.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void controllerTick() {
|
||||||
|
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.getRenderTickCounter().getTickDelta(true) + 0.5;
|
||||||
|
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getRenderTickCounter().getTickDelta(true) + 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.getPath());
|
||||||
|
}
|
||||||
|
private static ButtonBinding tempBinding;
|
||||||
|
/**
|
||||||
|
* Returns the binding matching the given string.
|
||||||
|
*
|
||||||
|
* @param name the name of the binding to get
|
||||||
|
* @return true if the binding is registered, else false
|
||||||
|
*/
|
||||||
|
public static ButtonBinding getBinding(@NotNull String name) {
|
||||||
|
if (BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name)))
|
||||||
|
BINDINGS.forEach(binding -> {
|
||||||
|
if (binding.getName().equalsIgnoreCase(name)) InputManager.tempBinding = binding;
|
||||||
|
});
|
||||||
|
return tempBinding;
|
||||||
|
}
|
||||||
|
private static List<ButtonBinding> unboundBindings;
|
||||||
|
public static List<ButtonBinding> getUnboundBindings() {
|
||||||
|
unboundBindings = new ArrayList<>();
|
||||||
|
BINDINGS.forEach(binding -> {
|
||||||
|
if (binding.isNotBound() && !MidnightControlsConfig.ignoredUnboundKeys.contains(binding.getTranslationKey())) unboundBindings.add(binding);
|
||||||
|
});
|
||||||
|
unboundBindings.sort(Comparator.comparing(s -> I18n.translate(s.getTranslationKey())));
|
||||||
|
return unboundBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static @NotNull ButtonBinding registerBinding(@NotNull org.aperlambda.lambdacommon.Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull Predicate<ButtonBinding> filter, boolean hasCooldown) {
|
||||||
|
return registerBinding(Identifier.of(id.getNamespace(), id.getName()), defaultButton, actions, filter, hasCooldown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static @NotNull ButtonBinding registerBinding(@NotNull org.aperlambda.lambdacommon.Identifier id, int[] defaultButton, boolean hasCooldown) {
|
||||||
|
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull Predicate<ButtonBinding> filter, boolean hasCooldown) {
|
||||||
|
return registerBinding(new ButtonBinding(id.getNamespace() + "." + id.getPath(), 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 org.aperlambda.lambdacommon.Identifier identifier, int priority) {
|
||||||
|
return registerCategory(Identifier.of(identifier.getNamespace(), identifier.getName()), priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ButtonCategory registerCategory(@NotNull org.aperlambda.lambdacommon.Identifier identifier) {
|
||||||
|
return registerCategory(Identifier.of(identifier.getNamespace(), identifier.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Identifier.of("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() {
|
||||||
|
var skipButtons = new IntArrayList();
|
||||||
|
record ButtonStateValue(ButtonState state, float value) {
|
||||||
|
}
|
||||||
|
var states = new Object2ObjectOpenHashMap<ButtonBinding, ButtonStateValue>();
|
||||||
|
for (var binding : BINDINGS) {
|
||||||
|
var state = binding.isAvailable() ? getBindingState(binding) : ButtonState.NONE;
|
||||||
|
if (skipButtons.intStream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
|
||||||
|
if (binding.isPressed())
|
||||||
|
state = ButtonState.RELEASE;
|
||||||
|
else
|
||||||
|
state = ButtonState.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == ButtonState.RELEASE && !binding.isPressed()) {
|
||||||
|
state = ButtonState.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.setPressed(state.isPressed());
|
||||||
|
binding.update();
|
||||||
|
if (binding.isPressed())
|
||||||
|
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 Identifier id, InputUtil.Type type, int code, @NotNull String category) {
|
||||||
|
return new KeyBinding(String.format("key.%s.%s", id.getNamespace(), id.getPath()), type, code, category);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.controller;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
|
import net.minecraft.entity.attribute.EntityAttributes;
|
||||||
|
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 slowdownFactor = 1.f;
|
||||||
|
private float movementForward = 0.f;
|
||||||
|
private float movementSideways = 0.f;
|
||||||
|
private final MathUtil.PolarUtil polarUtil = new MathUtil.PolarUtil();
|
||||||
|
|
||||||
|
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;
|
||||||
|
// TODO
|
||||||
|
// player.input.playerInput.pressingForward = this.pressingForward;
|
||||||
|
// player.input.pressingBack = this.pressingBack;
|
||||||
|
// player.input.pressingLeft = this.pressingLeft;
|
||||||
|
// player.input.pressingRight = this.pressingRight;
|
||||||
|
|
||||||
|
polarUtil.calculate(this.movementSideways, this.movementForward, this.slowdownFactor);
|
||||||
|
player.input.movementForward = polarUtil.polarY;
|
||||||
|
player.input.movementSideways = polarUtil.polarX;
|
||||||
|
|
||||||
|
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 = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slowdownFactor = client.player.shouldSlowDown() ? (MathHelper.clamp(
|
||||||
|
0.3F + (float) client.player.getAttributeValue(EntityAttributes.SNEAKING_SPEED),
|
||||||
|
0.0F,
|
||||||
|
1.0F
|
||||||
|
)) : 1.f;
|
||||||
|
|
||||||
|
if (button == ButtonBinding.FORWARD || button == ButtonBinding.BACK) {
|
||||||
|
// Handle forward movement.
|
||||||
|
this.pressingForward = direction > 0;
|
||||||
|
this.pressingBack = direction < 0;
|
||||||
|
this.movementForward = direction * value;
|
||||||
|
} else {
|
||||||
|
// Handle sideways movement.
|
||||||
|
this.pressingLeft = direction > 0;
|
||||||
|
this.pressingRight = direction < 0;
|
||||||
|
this.movementSideways = direction * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.enums.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.isPressed());
|
||||||
|
else
|
||||||
|
((KeyBindingAccessor) binding).midnightcontrols$handlePressState(button.isPressed());
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client;
|
package eu.midnightdust.midnightcontrols.client.enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a button state.
|
* Represents a button state.
|
||||||
@@ -16,8 +16,7 @@ package me.lambdaurora.lambdacontrols.client;
|
|||||||
* @version 1.1.0
|
* @version 1.1.0
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public enum ButtonState
|
public enum ButtonState {
|
||||||
{
|
|
||||||
NONE(0),
|
NONE(0),
|
||||||
PRESS(1),
|
PRESS(1),
|
||||||
RELEASE(2),
|
RELEASE(2),
|
||||||
@@ -25,28 +24,25 @@ public enum ButtonState
|
|||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|
||||||
ButtonState(int id)
|
ButtonState(int id) {
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this state is a pressed state.
|
* Returns whether this state is a pressed state.
|
||||||
*
|
*
|
||||||
* @return True if this state is a pressed state, else false.
|
* @return true if this state is a pressed state, else false
|
||||||
*/
|
*/
|
||||||
public boolean isPressed()
|
public boolean isPressed() {
|
||||||
{
|
|
||||||
return this == PRESS || this == REPEAT;
|
return this == PRESS || this == REPEAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this state is an unpressed state.
|
* Returns whether this state is an unpressed state.
|
||||||
*
|
*
|
||||||
* @return True if this state is an unpressed state, else false.
|
* @return true if this state is an unpressed state, else false
|
||||||
*/
|
*/
|
||||||
public boolean isUnpressed()
|
public boolean isUnpressed() {
|
||||||
{
|
|
||||||
return this == RELEASE || this == NONE;
|
return this == RELEASE || this == NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.enums;
|
||||||
|
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public enum CameraMode {
|
||||||
|
FLAT, ADAPTIVE;
|
||||||
|
public Text getTranslatedText() {
|
||||||
|
return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name());
|
||||||
|
}
|
||||||
|
public @NotNull CameraMode next() {
|
||||||
|
var v = values();
|
||||||
|
if (v.length == this.ordinal() + 1)
|
||||||
|
return v[0];
|
||||||
|
return v[this.ordinal() + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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.enums;
|
||||||
|
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
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 {
|
||||||
|
DEFAULT(0),
|
||||||
|
DUALSHOCK(1),
|
||||||
|
DUALSENSE(2),
|
||||||
|
SWITCH(3),
|
||||||
|
XBOX_360(4),
|
||||||
|
XBOX(5),
|
||||||
|
STEAM_DECK(6),
|
||||||
|
STEAM_CONTROLLER(7),
|
||||||
|
OUYA(8),
|
||||||
|
NUMBERED(9);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client;
|
package eu.midnightdust.midnightcontrols.client.enums;
|
||||||
|
|
||||||
import net.minecraft.client.resource.language.I18n;
|
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.text.TranslatableText;
|
|
||||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -25,26 +23,23 @@ import java.util.Optional;
|
|||||||
* @version 1.4.0
|
* @version 1.4.0
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public enum HudSide implements Nameable
|
public enum HudSide {
|
||||||
{
|
|
||||||
LEFT,
|
LEFT,
|
||||||
RIGHT;
|
RIGHT;
|
||||||
|
|
||||||
private final Text text;
|
private final Text text;
|
||||||
|
|
||||||
HudSide()
|
HudSide() {
|
||||||
{
|
this.text = Text.translatable(this.getTranslationKey());
|
||||||
this.text = new TranslatableText(this.getTranslationKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next side available.
|
* Returns the next side available.
|
||||||
*
|
*
|
||||||
* @return The next available side.
|
* @return the next available side
|
||||||
*/
|
*/
|
||||||
public @NotNull HudSide next()
|
public @NotNull HudSide next() {
|
||||||
{
|
var v = values();
|
||||||
HudSide[] v = values();
|
|
||||||
if (v.length == this.ordinal() + 1)
|
if (v.length == this.ordinal() + 1)
|
||||||
return v[0];
|
return v[0];
|
||||||
return v[this.ordinal() + 1];
|
return v[this.ordinal() + 1];
|
||||||
@@ -53,37 +48,33 @@ public enum HudSide implements Nameable
|
|||||||
/**
|
/**
|
||||||
* Returns the translation key of this hud side.
|
* Returns the translation key of this hud side.
|
||||||
*
|
*
|
||||||
* @return The translation key of this hude side.
|
* @return the translation key of this hude side
|
||||||
*/
|
*/
|
||||||
public @NotNull String getTranslationKey()
|
public @NotNull String getTranslationKey() {
|
||||||
{
|
return "midnightcontrols.hud_side." + this.getName();
|
||||||
return "lambdacontrols.hud_side." + this.getName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the translated text of this hud side.
|
* Gets the translated text of this hud side.
|
||||||
*
|
*
|
||||||
* @return The translated text of this hud side.
|
* @return the translated text of this hud side
|
||||||
*/
|
*/
|
||||||
public @NotNull Text getTranslatedText()
|
public @NotNull Text getTranslatedText() {
|
||||||
{
|
|
||||||
return this.text;
|
return this.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public @NotNull String getName() {
|
||||||
public @NotNull String getName()
|
return this.name();
|
||||||
{
|
|
||||||
return this.name().toLowerCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the hud side from its identifier.
|
* Gets the hud side from its identifier.
|
||||||
*
|
*
|
||||||
* @param id The identifier of the hud side.
|
* @param id the identifier of the hud side
|
||||||
* @return The hud side if found, else empty.
|
* @return the hud side if found, else empty
|
||||||
*/
|
*/
|
||||||
public static @NotNull Optional<HudSide> byId(@NotNull String id)
|
@Deprecated
|
||||||
{
|
public static @NotNull Optional<HudSide> byId(@NotNull String id) {
|
||||||
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.enums;
|
||||||
|
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public enum TouchMode {
|
||||||
|
CROSSHAIR, FINGER_POS;
|
||||||
|
public Text getTranslatedText() {
|
||||||
|
return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name());
|
||||||
|
}
|
||||||
|
public @NotNull TouchMode next() {
|
||||||
|
var v = values();
|
||||||
|
if (v.length == this.ordinal() + 1)
|
||||||
|
return v[0];
|
||||||
|
return v[this.ordinal() + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 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.enums;
|
||||||
|
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static @NotNull Optional<VirtualMouseSkin> byId(@NotNull String id) {
|
||||||
|
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
|
||||||
|
}
|
||||||
|
public String getSpritePath() {
|
||||||
|
return switch (this) {
|
||||||
|
case DEFAULT_LIGHT -> "cursor/light/default";
|
||||||
|
case DEFAULT_DARK -> "cursor/dark/default";
|
||||||
|
case SECOND_LIGHT -> "cursor/light/secondary";
|
||||||
|
case SECOND_DARK -> "cursor/dark/secondary";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.option.SpruceOption;
|
||||||
|
import org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.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 copyGuidOption;
|
||||||
|
private final SpruceOption reloadMappingsOption;
|
||||||
|
private String mappings;
|
||||||
|
private SpruceTextAreaWidget textArea;
|
||||||
|
|
||||||
|
protected MappingsStringInputWidget(Position position, int width, int height) {
|
||||||
|
super(position, width, height);
|
||||||
|
|
||||||
|
this.reloadMappingsOption = ReloadControllerMappingsOption.newOption(btn -> {
|
||||||
|
this.writeMappings();
|
||||||
|
});
|
||||||
|
this.copyGuidOption = SpruceSimpleActionOption.of("midnightcontrols.menu.copy_controller_guid", button -> client.keyboard.setClipboard(MidnightControlsConfig.getController().getGuid()));
|
||||||
|
|
||||||
|
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.PERIODIC_NOTIFICATION,
|
||||||
|
Text.translatable("midnightcontrols.controller.mappings.error.write"), Text.empty()));
|
||||||
|
e.fillInStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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), 257));
|
||||||
|
this.addChild(this.copyGuidOption.createWidget(Position.of(this.width / 2 + 105, this.height - 29), 65));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
super.renderWidget(context, mouseX, mouseY, delta);
|
||||||
|
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.multiple_mapping_tip"), this.textArea.getX() + this.textArea.getWidth() / 2, this.textArea.getY() + this.textArea.getHeight() - 12, 0x888888);
|
||||||
|
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.current_controller_guid", MidnightControlsConfig.getController().getGuid()), this.textArea.getX() + this.textArea.getWidth() / 2, this.height - 21, 0xFFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* 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.enums.HudSide;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import net.minecraft.client.render.RenderTickCounter;
|
||||||
|
import org.thinkingstudio.obsidianui.hud.Hud;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the midnightcontrols HUD.
|
||||||
|
*
|
||||||
|
* @author LambdAurora
|
||||||
|
* @version 1.7.0
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public class MidnightControlsHud extends Hud {
|
||||||
|
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
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 boolean showSwapHandsAction = false;
|
||||||
|
private int useWidth = 0;
|
||||||
|
private int useButtonWidth = 0;
|
||||||
|
private String attackAction = "";
|
||||||
|
private String placeAction = "";
|
||||||
|
private int ticksDisplayedCrosshair = 0;
|
||||||
|
private static boolean isCrammed = false;
|
||||||
|
|
||||||
|
public MidnightControlsHud() {
|
||||||
|
super(id("hud/button_indicator"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(@NotNull MinecraftClient client, int screenWidth, int screenHeight) {
|
||||||
|
super.init(client, screenWidth, screenHeight);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the MidnightControls HUD.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, RenderTickCounter tickCounter) {
|
||||||
|
if (this.client == null) return;
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && this.client.currentScreen == null) {
|
||||||
|
isCrammed = client.getWindow().getScaledWidth() < 520;
|
||||||
|
int y = bottom(2);
|
||||||
|
MatrixStack matrices = context.getMatrices();
|
||||||
|
matrices.push();
|
||||||
|
this.renderFirstIcons(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||||
|
this.renderSecondIcons(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||||
|
this.renderFirstSection(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||||
|
this.renderSecondSection(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||||
|
matrices.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MidnightControlsClient.reacharound.isLastReacharoundVertical()) {
|
||||||
|
// Render crosshair indicator.
|
||||||
|
var window = this.client.getWindow();
|
||||||
|
var text = "[ ]";
|
||||||
|
|
||||||
|
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickCounter.getTickDelta(true)) / 5F;
|
||||||
|
scale *= scale;
|
||||||
|
int opacity = ((int) (255 * scale)) << 24;
|
||||||
|
|
||||||
|
context.drawText(client.textRenderer, text, (int) (window.getScaledWidth() / 2.f - this.client.textRenderer.getWidth(text) / 2.f),
|
||||||
|
(int) (window.getScaledHeight() / 2.f - 4), 0xCCCCCC | opacity, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderFirstIcons(DrawContext context, int x, int y) {
|
||||||
|
int offset = 2 + this.inventoryWidth + this.inventoryButtonWidth + 4;
|
||||||
|
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x : x - this.inventoryButtonWidth;
|
||||||
|
if (!ButtonBinding.INVENTORY.isNotBound()) this.drawButton(context, currentX, y, ButtonBinding.INVENTORY, true);
|
||||||
|
if (!ButtonBinding.SWAP_HANDS.isNotBound() && !isCrammed && showSwapHandsAction) this.drawButton(context, 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 -= 20;
|
||||||
|
}
|
||||||
|
if (!ButtonBinding.DROP_ITEM.isNotBound() && client.player != null)
|
||||||
|
this.drawButton(context, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderSecondIcons(DrawContext context, int x, int y) {
|
||||||
|
int offset;
|
||||||
|
int currentX = x;
|
||||||
|
if (isCrammed && showSwapHandsAction && !this.client.options.getShowSubtitles().getValue() && !ButtonBinding.SWAP_HANDS.isNotBound()) {
|
||||||
|
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
|
||||||
|
currentX -= this.useButtonWidth;
|
||||||
|
this.drawButton(context, currentX, y, ButtonBinding.SWAP_HANDS, true);
|
||||||
|
currentX = x;
|
||||||
|
y -= 20;
|
||||||
|
}
|
||||||
|
if (!this.placeAction.isEmpty() && (!ButtonBinding.USE.isNotBound()) ) {
|
||||||
|
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
|
||||||
|
currentX -= this.useButtonWidth;
|
||||||
|
this.drawButton(context, 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 -= 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
|
||||||
|
currentX -= this.attackButtonWidth;
|
||||||
|
|
||||||
|
if (!ButtonBinding.ATTACK.isNotBound()) this.drawButton(context, currentX, y, ButtonBinding.ATTACK, this.attackWidth != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderFirstSection(DrawContext context, int x, int y) {
|
||||||
|
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.inventoryButtonWidth + 2 : x - this.inventoryButtonWidth - 2 - this.inventoryWidth;
|
||||||
|
if (!ButtonBinding.INVENTORY.isNotBound()) this.drawTip(context, currentX, y, ButtonBinding.INVENTORY, true);
|
||||||
|
currentX += MidnightControlsConfig.hudSide == HudSide.LEFT ? this.inventoryWidth + 4 + this.swapHandsButtonWidth + 2
|
||||||
|
: -this.swapHandsWidth - 2 - this.swapHandsButtonWidth - 4;
|
||||||
|
if (!ButtonBinding.SWAP_HANDS.isNotBound() && !isCrammed && showSwapHandsAction) this.drawTip(context, 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 -= 20;
|
||||||
|
currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.dropItemButtonWidth + 2 : x - this.dropItemButtonWidth - 2 - this.dropItemWidth;
|
||||||
|
}
|
||||||
|
if (!ButtonBinding.DROP_ITEM.isNotBound() && client.player != null) this.drawTip(context, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderSecondSection(DrawContext context, int x, int y) {
|
||||||
|
int currentX = x;
|
||||||
|
|
||||||
|
if (isCrammed && showSwapHandsAction && !this.client.options.getShowSubtitles().getValue() && !ButtonBinding.SWAP_HANDS.isNotBound()) {
|
||||||
|
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.swapHandsButtonWidth + 2 : -this.swapHandsButtonWidth - 2 - this.swapHandsWidth;
|
||||||
|
|
||||||
|
this.drawTip(context, currentX, y, ButtonBinding.SWAP_HANDS, true);
|
||||||
|
|
||||||
|
currentX = x;
|
||||||
|
y -= 20;
|
||||||
|
}
|
||||||
|
if (!this.placeAction.isEmpty()) {
|
||||||
|
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.useButtonWidth + 2 : -this.useButtonWidth - 2 - this.useWidth;
|
||||||
|
|
||||||
|
this.drawTip(context, currentX, y, this.placeAction, true);
|
||||||
|
|
||||||
|
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.LEFT) {
|
||||||
|
currentX -= 4;
|
||||||
|
} else {
|
||||||
|
currentX = x;
|
||||||
|
y -= 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.attackButtonWidth + 2 : -this.attackButtonWidth - 2 - this.attackWidth;
|
||||||
|
|
||||||
|
if (!ButtonBinding.ATTACK.isNotBound()) this.drawTip(context, currentX, y, this.attackAction, this.attackWidth != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (this.client == null) return;
|
||||||
|
super.tick();
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
|
||||||
|
if (this.client.crosshairTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String placeAction;
|
||||||
|
|
||||||
|
// Update "Use" tip status.
|
||||||
|
BlockHitResult placeHitResult;
|
||||||
|
if (this.client.crosshairTarget.getType() == HitResult.Type.MISS) {
|
||||||
|
placeHitResult = MidnightControlsClient.reacharound.getLastReacharoundResult();
|
||||||
|
this.attackAction = "";
|
||||||
|
this.attackWidth = 0;
|
||||||
|
} else {
|
||||||
|
if (this.client.crosshairTarget.getType() == HitResult.Type.BLOCK)
|
||||||
|
placeHitResult = (BlockHitResult) this.client.crosshairTarget;
|
||||||
|
else
|
||||||
|
placeHitResult = null;
|
||||||
|
|
||||||
|
this.attackAction = this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "midnightcontrols.action.hit" : ButtonBinding.ATTACK.getTranslationKey();
|
||||||
|
this.attackWidth = this.width(attackAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MidnightControlsClient.reacharound.isLastReacharoundVertical()) {
|
||||||
|
if (this.ticksDisplayedCrosshair < 5)
|
||||||
|
this.ticksDisplayedCrosshair++;
|
||||||
|
} else {
|
||||||
|
this.ticksDisplayedCrosshair = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var customAttackAction = MidnightControlsCompat.getAttackActionAt(this.client, 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 (placeHitResult != null && stack.getItem() instanceof BlockItem) {
|
||||||
|
placeAction = "midnightcontrols.action.place";
|
||||||
|
} else {
|
||||||
|
placeAction = ButtonBinding.USE.getTranslationKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var customUseAction = MidnightControlsCompat.getUseActionAt(this.client, placeHitResult);
|
||||||
|
if (customUseAction != null)
|
||||||
|
placeAction = customUseAction;
|
||||||
|
|
||||||
|
this.placeAction = placeAction;
|
||||||
|
this.showSwapHandsAction = !this.client.player.getMainHandStack().isEmpty() || !this.client.player.getOffHandStack().isEmpty();
|
||||||
|
|
||||||
|
// 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 (this.client.getWindow().getScaledHeight() - 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(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display) {
|
||||||
|
if (display)
|
||||||
|
MidnightControlsRenderer.drawButton(context, x, y, button, this.client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawTip(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display) {
|
||||||
|
this.drawTip(context, x, y, button.getTranslationKey(), display);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawTip(DrawContext context, 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;
|
||||||
|
context.drawText(this.client.textRenderer, translatedAction, x, (y + textY), 14737632, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
* 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.ControlsMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.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.enums.VirtualMouseSkin;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.DrawContextAccessor;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.render.*;
|
||||||
|
import net.minecraft.client.resource.language.I18n;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.ColorHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(DrawContext context, int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client) {
|
||||||
|
return drawButton(context, x, y, button.getButton(), client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ButtonSize drawButton(DrawContext context, 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(context, 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(DrawContext context, 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.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);
|
||||||
|
context.drawTexture(RenderLayer::getGuiTextured, axis ? MidnightControlsClient.CONTROLLER_AXIS : button >= 15 && button <= 19 ? MidnightControlsClient.CONTROLLER_EXPANDED :MidnightControlsClient.CONTROLLER_BUTTONS
|
||||||
|
, 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(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client) {
|
||||||
|
return drawButtonTip(context, x, y, button.getButton(), button.getTranslationKey(), display, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int drawButtonTip(DrawContext context, int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client) {
|
||||||
|
if (display) {
|
||||||
|
int buttonWidth = drawButton(context, x, y, button, client).length();
|
||||||
|
|
||||||
|
var translatedAction = I18n.translate(action);
|
||||||
|
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - client.textRenderer.fontHeight / 2) + 1;
|
||||||
|
|
||||||
|
return context.drawTextWithShadow(client.textRenderer, translatedAction, (x + buttonWidth + 2), (y + textY), 14737632);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -10;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) {
|
||||||
|
return 15 + 5 + textRenderer.getWidth(action);
|
||||||
|
}
|
||||||
|
public static void renderWaylandCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
|
||||||
|
if (MidnightControlsConfig.virtualMouse || client.currentScreen == null || MidnightControlsConfig.controlsMode != ControlsMode.CONTROLLER) return;
|
||||||
|
|
||||||
|
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
|
||||||
|
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / client.getWindow().getHeight();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Identifier spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_LIGHT;
|
||||||
|
if (MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.DEFAULT_DARK || MidnightControlsConfig.virtualMouseSkin == VirtualMouseSkin.SECOND_DARK)
|
||||||
|
spritePath = MidnightControlsClient.WAYLAND_CURSOR_TEXTURE_DARK;
|
||||||
|
Sprite sprite = client.getGuiAtlasManager().getSprite(spritePath);
|
||||||
|
drawUnalignedTexturedQuad(RenderLayer::getGuiTextured, sprite.getAtlasId(), context, mouseX, mouseX + 8, mouseY, mouseY + 8, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
|
||||||
|
} catch (IllegalStateException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void renderVirtualCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
|
||||||
|
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|
||||||
|
|| MidnightInput.isScreenInteractive(client.currentScreen)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
float mouseX = (float) client.mouse.getX() * client.getWindow().getScaledWidth() / client.getWindow().getWidth();
|
||||||
|
float mouseY = (float) client.mouse.getY() * client.getWindow().getScaledHeight() / 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 && client.currentScreen != null) {
|
||||||
|
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, (int) mouseX, (int) mouseY);
|
||||||
|
|
||||||
|
if (slot != null) {
|
||||||
|
mouseX = slot.x();
|
||||||
|
mouseY = slot.y();
|
||||||
|
hoverSlot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hoverSlot) {
|
||||||
|
mouseX -= 8;
|
||||||
|
mouseY -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Sprite sprite = client.getGuiAtlasManager().getSprite(id(MidnightControlsConfig.virtualMouseSkin.getSpritePath() + (hoverSlot ? "_slot" : "")));
|
||||||
|
drawUnalignedTexturedQuad(RenderLayer::getGuiTextured, sprite.getAtlasId(), context, mouseX, mouseX + 16, mouseY, mouseY + 16, 999, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV());
|
||||||
|
} catch (IllegalStateException ignored) {}
|
||||||
|
}
|
||||||
|
private static void drawUnalignedTexturedQuad(Function<Identifier, RenderLayer> renderLayers, Identifier texture, DrawContext context, float x1, float x2, float y1, float y2, float z, float u1, float u2, float v1, float v2) {
|
||||||
|
RenderLayer renderLayer = renderLayers.apply(texture);
|
||||||
|
//RenderSystem.setShaderTexture(0, texture);
|
||||||
|
Matrix4f matrix4f = context.getMatrices().peek().getPositionMatrix();
|
||||||
|
//BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE);
|
||||||
|
VertexConsumer vertexConsumer = ((DrawContextAccessor)context).getVertexConsumers().getBuffer(renderLayer);
|
||||||
|
vertexConsumer.vertex(matrix4f, x1, y1, z).texture(u1, v1).color(ColorHelper.getWhite(1.0f));
|
||||||
|
vertexConsumer.vertex(matrix4f, x1, y2, z).texture(u1, v2).color(ColorHelper.getWhite(1.0f));
|
||||||
|
vertexConsumer.vertex(matrix4f, x2, y2, z).texture(u2, v2).color(ColorHelper.getWhite(1.0f));
|
||||||
|
vertexConsumer.vertex(matrix4f, x2, y1, z).texture(u2, v1).color(ColorHelper.getWhite(1.0f));
|
||||||
|
context.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ButtonSize(int length, int height) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,525 @@
|
|||||||
|
/*
|
||||||
|
* 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.MidnightControlsConstants;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
|
||||||
|
import org.thinkingstudio.obsidianui.background.Background;
|
||||||
|
import org.thinkingstudio.obsidianui.mixin.DrawContextAccessor;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
|
||||||
|
import eu.midnightdust.lib.util.MidnightColorUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.Controller;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.SpruceTexts;
|
||||||
|
import org.thinkingstudio.obsidianui.option.*;
|
||||||
|
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceOptionListWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.tabbed.SpruceTabbedWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
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 org.joml.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/";
|
||||||
|
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 joystickAsMouseOption;
|
||||||
|
private final SpruceOption eyeTrackingAsMouseOption;
|
||||||
|
private final SpruceOption eyeTrackingDeadzone;
|
||||||
|
private final SpruceOption virtualMouseOption;
|
||||||
|
private final SpruceOption hideCursorOption;
|
||||||
|
private final SpruceOption resetOption;
|
||||||
|
private final SpruceOption advancedConfigOption;
|
||||||
|
// Gameplay options
|
||||||
|
private final SpruceOption analogMovementOption;
|
||||||
|
private final SpruceOption doubleTapToSprintOption;
|
||||||
|
private final SpruceOption autoJumpOption;
|
||||||
|
private final SpruceOption controllerToggleSneakOption;
|
||||||
|
private final SpruceOption controllerToggleSprintOption;
|
||||||
|
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;
|
||||||
|
private final SpruceOption moveChatOption;
|
||||||
|
// 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));
|
||||||
|
if (MidnightControlsConfig.debug) System.out.println(Controller.byId(id).getName() + "'s Controller GUID: " + Controller.byId(id).getGuid());
|
||||||
|
},
|
||||||
|
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.menu.controller2.tooltip"));
|
||||||
|
private final SpruceOption unfocusedInputOption;
|
||||||
|
private final SpruceOption invertsRightXAxis;
|
||||||
|
private final SpruceOption invertsRightYAxis;
|
||||||
|
private final SpruceOption cameraModeOption;
|
||||||
|
private final SpruceOption toggleControllerProfileOption;
|
||||||
|
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.concat(".tooltip"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Touch options
|
||||||
|
private final SpruceOption touchWithControllerOption;
|
||||||
|
private final SpruceOption touchSpeedOption;
|
||||||
|
private final SpruceOption touchBreakDelayOption;
|
||||||
|
private final SpruceOption invertTouchOption;
|
||||||
|
private final SpruceOption touchTransparencyOption;
|
||||||
|
private final SpruceOption touchModeOption;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
Controller candidate = Controller.byId(newId);
|
||||||
|
boolean connected = candidate.isConnected();
|
||||||
|
if (MidnightControlsConfig.excludedControllers.stream().anyMatch(exclusion -> candidate.getName().matches(exclusion))) connected = false;
|
||||||
|
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 != null && this.client.player != null) {
|
||||||
|
NetworkUtil.sendPayloadC2S(new ControlsModePayload(next.getName()));
|
||||||
|
}
|
||||||
|
}, option -> option.getDisplayText(Text.translatable(MidnightControlsConfig.controlsMode.getTranslationKey())),
|
||||||
|
Text.translatable("midnightcontrols.menu.controls_mode.tooltip"));
|
||||||
|
this.autoSwitchModeOption = new SpruceToggleBooleanOption("midnightcontrols.menu.auto_switch_mode", () -> MidnightControlsConfig.autoSwitchMode,
|
||||||
|
value -> MidnightControlsConfig.autoSwitchMode = value, Text.translatable("midnightcontrols.menu.auto_switch_mode.tooltip"));
|
||||||
|
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.menu.rotation_speed.tooltip"));
|
||||||
|
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.menu.y_axis_rotation_speed.tooltip"));
|
||||||
|
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.menu.mouse_speed.tooltip"));
|
||||||
|
this.joystickAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.joystick_as_mouse",
|
||||||
|
() -> MidnightControlsConfig.joystickAsMouse, value -> MidnightControlsConfig.joystickAsMouse = value,
|
||||||
|
Text.translatable("midnightcontrols.menu.joystick_as_mouse.tooltip"));
|
||||||
|
this.eyeTrackingAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.eye_tracker_as_mouse",
|
||||||
|
() -> MidnightControlsConfig.eyeTrackerAsMouse, value -> MidnightControlsConfig.eyeTrackerAsMouse = value,
|
||||||
|
Text.translatable("midnightcontrols.menu.eye_tracker_as_mouse.tooltip"));
|
||||||
|
this.eyeTrackingDeadzone = new SpruceDoubleInputOption("midnightcontrols.menu.eye_tracker_deadzone",
|
||||||
|
() -> MidnightControlsConfig.eyeTrackerDeadzone, value -> MidnightControlsConfig.eyeTrackerDeadzone = value,
|
||||||
|
Text.translatable("midnightcontrols.menu.eye_tracker_deadzone.tooltip"));
|
||||||
|
this.resetOption = SpruceSimpleActionOption.reset(btn -> {
|
||||||
|
MidnightControlsConfig.reset();
|
||||||
|
var client = MinecraftClient.getInstance();
|
||||||
|
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
|
||||||
|
});
|
||||||
|
this.advancedConfigOption = SpruceSimpleActionOption.of("midnightcontrols.midnightconfig.title", button -> client.setScreen(MidnightControlsConfig.getScreen(this, MidnightControlsConstants.NAMESPACE)));
|
||||||
|
// Gameplay options
|
||||||
|
this.analogMovementOption = new SpruceToggleBooleanOption("midnightcontrols.menu.analog_movement",
|
||||||
|
() -> MidnightControlsConfig.analogMovement, value -> MidnightControlsConfig.analogMovement = value,
|
||||||
|
Text.translatable("midnightcontrols.menu.analog_movement.tooltip"));
|
||||||
|
this.doubleTapToSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.double_tap_to_sprint",
|
||||||
|
() -> MidnightControlsConfig.doubleTapToSprint, value -> MidnightControlsConfig.doubleTapToSprint = value,
|
||||||
|
Text.translatable("midnightcontrols.menu.double_tap_to_sprint.tooltip"));
|
||||||
|
this.autoJumpOption = new SpruceToggleBooleanOption("options.autoJump",
|
||||||
|
() -> this.client.options.getAutoJump().getValue(),
|
||||||
|
newValue -> this.client.options.getAutoJump().setValue(newValue),
|
||||||
|
null);
|
||||||
|
this.controllerToggleSneakOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sneak",
|
||||||
|
() -> MidnightControlsConfig.controllerToggleSneak, value -> MidnightControlsConfig.controllerToggleSneak = value,
|
||||||
|
null);
|
||||||
|
this.controllerToggleSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sprint",
|
||||||
|
() -> MidnightControlsConfig.controllerToggleSprint, value -> MidnightControlsConfig.controllerToggleSprint = value,
|
||||||
|
null);
|
||||||
|
this.fastBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fast_block_placing", () -> MidnightControlsConfig.fastBlockPlacing,
|
||||||
|
value -> MidnightControlsConfig.fastBlockPlacing = value, Text.translatable("midnightcontrols.menu.fast_block_placing.tooltip"));
|
||||||
|
this.frontBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.horizontal", () -> MidnightControlsConfig.horizontalReacharound,
|
||||||
|
value -> MidnightControlsConfig.horizontalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.horizontal.tooltip"));
|
||||||
|
this.verticalReacharoundOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.vertical", () -> MidnightControlsConfig.verticalReacharound,
|
||||||
|
value -> MidnightControlsConfig.verticalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.vertical.tooltip"));
|
||||||
|
this.flyDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting", () -> MidnightControlsConfig.flyDrifting,
|
||||||
|
value -> MidnightControlsConfig.flyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting.tooltip"));
|
||||||
|
this.flyVerticalDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting_vertical", () -> MidnightControlsConfig.verticalFlyDrifting,
|
||||||
|
value -> MidnightControlsConfig.verticalFlyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting_vertical.tooltip"));
|
||||||
|
// 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.menu.controller_type.tooltip"));
|
||||||
|
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,
|
||||||
|
MidnightControlsClient::setHudEnabled, Text.translatable("midnightcontrols.menu.hud_enable.tooltip"));
|
||||||
|
this.hudSideOption = new SpruceCyclingOption("midnightcontrols.menu.hud_side",
|
||||||
|
amount -> MidnightControlsConfig.hudSide = MidnightControlsConfig.hudSide.next(),
|
||||||
|
option -> option.getDisplayText(MidnightControlsConfig.hudSide.getTranslatedText()),
|
||||||
|
Text.translatable("midnightcontrols.menu.hud_side.tooltip"));
|
||||||
|
this.moveChatOption = new SpruceToggleBooleanOption("midnightcontrols.menu.move_chat", () -> MidnightControlsConfig.moveChat,
|
||||||
|
value -> MidnightControlsConfig.moveChat = value, Text.translatable("midnightcontrols.menu.move_chat.tooltip"));
|
||||||
|
// Controller options
|
||||||
|
this.toggleControllerProfileOption = new SpruceToggleBooleanOption("midnightcontrols.menu.separate_controller_profile", () -> MidnightControlsConfig.controllerBindingProfiles.containsKey(MidnightControlsConfig.getController().getGuid()), value -> {
|
||||||
|
if (value) {
|
||||||
|
MidnightControlsConfig.controllerBindingProfiles.put(MidnightControlsConfig.getController().getGuid(), MidnightControlsConfig.getBindingsForController());
|
||||||
|
MidnightControlsConfig.updateBindingsForController(MidnightControlsConfig.getController());
|
||||||
|
} else {
|
||||||
|
MidnightControlsConfig.controllerBindingProfiles.remove(MidnightControlsConfig.getController().getGuid());
|
||||||
|
MidnightControlsConfig.updateBindingsForController(MidnightControlsConfig.getController());
|
||||||
|
}
|
||||||
|
|
||||||
|
}, Text.empty());
|
||||||
|
this.cameraModeOption = new SpruceCyclingOption("midnightcontrols.menu.camera_mode",
|
||||||
|
amount -> MidnightControlsConfig.cameraMode = MidnightControlsConfig.cameraMode.next(),
|
||||||
|
option -> option.getDisplayText(MidnightControlsConfig.cameraMode.getTranslatedText()),
|
||||||
|
Text.translatable("midnightcontrols.menu.camera_mode.tooltip"));
|
||||||
|
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.menu.right_dead_zone.tooltip"));
|
||||||
|
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.menu.left_dead_zone.tooltip"));
|
||||||
|
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.menu.unfocused_input.tooltip"));
|
||||||
|
this.virtualMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.virtual_mouse", () -> MidnightControlsConfig.virtualMouse,
|
||||||
|
value -> MidnightControlsConfig.virtualMouse = value, Text.translatable("midnightcontrols.menu.virtual_mouse.tooltip"));
|
||||||
|
this.hideCursorOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hide_cursor", () -> MidnightControlsConfig.hideNormalMouse,
|
||||||
|
value -> MidnightControlsConfig.hideNormalMouse = value, Text.translatable("midnightcontrols.menu.hide_cursor.tooltip"));
|
||||||
|
// Touch options
|
||||||
|
this.touchModeOption = new SpruceCyclingOption("midnightcontrols.menu.touch_mode",
|
||||||
|
amount -> MidnightControlsConfig.touchMode = MidnightControlsConfig.touchMode.next(),
|
||||||
|
option -> option.getDisplayText(MidnightControlsConfig.touchMode.getTranslatedText()),
|
||||||
|
Text.translatable("midnightcontrols.menu.touch_mode.tooltip"));
|
||||||
|
this.touchWithControllerOption = new SpruceToggleBooleanOption("midnightcontrols.menu.touch_with_controller", () -> MidnightControlsConfig.touchInControllerMode,
|
||||||
|
value -> MidnightControlsConfig.touchInControllerMode = value, Text.translatable("midnightcontrols.menu.touch_with_controller.tooltip"));
|
||||||
|
this.touchSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.touch_speed", 0.0, 150.0, .5f,
|
||||||
|
() -> MidnightControlsConfig.touchSpeed,
|
||||||
|
value -> MidnightControlsConfig.touchSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||||
|
Text.translatable("midnightcontrols.menu.touch_speed.tooltip"));
|
||||||
|
this.touchBreakDelayOption = new SpruceDoubleOption("midnightcontrols.menu.touch_break_delay", 50, 500, 1f,
|
||||||
|
() -> (double) MidnightControlsConfig.touchBreakDelay,
|
||||||
|
value -> MidnightControlsConfig.touchBreakDelay = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||||
|
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
|
||||||
|
this.touchTransparencyOption = new SpruceDoubleOption("midnightcontrols.menu.touch_transparency", 0, 100, 1f,
|
||||||
|
() -> (double) MidnightControlsConfig.touchTransparency,
|
||||||
|
value -> MidnightControlsConfig.touchTransparency = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
|
||||||
|
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
|
||||||
|
this.invertTouchOption = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_touch", () -> MidnightControlsConfig.invertTouch,
|
||||||
|
value -> MidnightControlsConfig.invertTouch = value, Text.translatable("midnightcontrols.menu.invert_touch.tooltip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(ButtonWidget.builder(SpruceTexts.GUI_DONE, btn -> this.client.setScreen(this.parent))
|
||||||
|
.dimensions(this.width / 2 - 155 + 160, this.height - 29, 150, 20).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
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.touch"), null,
|
||||||
|
this::buildTouchTab);
|
||||||
|
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);
|
||||||
|
list.addSingleOptionEntry(this.hideCursorOption);
|
||||||
|
list.addSingleOptionEntry(this.joystickAsMouseOption);
|
||||||
|
list.addSingleOptionEntry(this.eyeTrackingAsMouseOption);
|
||||||
|
list.addSingleOptionEntry(this.advancedConfigOption);
|
||||||
|
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);
|
||||||
|
list.addSingleOptionEntry(this.controllerToggleSneakOption);
|
||||||
|
list.addSingleOptionEntry(this.controllerToggleSprintOption);
|
||||||
|
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);
|
||||||
|
list.addSingleOptionEntry(this.moveChatOption);
|
||||||
|
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.toggleControllerProfileOption);
|
||||||
|
list.addSingleOptionEntry(this.unfocusedInputOption);
|
||||||
|
list.addSingleOptionEntry(this.cameraModeOption);
|
||||||
|
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 SpruceOptionListWidget buildTouchTab(int width, int height) {
|
||||||
|
var list = new SpruceOptionListWidget(Position.origin(), width, height);
|
||||||
|
list.setBackground(new MidnightControlsBackground(130));
|
||||||
|
list.addSingleOptionEntry(this.touchSpeedOption);
|
||||||
|
list.addSingleOptionEntry(this.touchWithControllerOption);
|
||||||
|
list.addSingleOptionEntry(this.invertTouchOption);
|
||||||
|
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
|
||||||
|
list.addSingleOptionEntry(this.touchModeOption);
|
||||||
|
list.addSingleOptionEntry(this.touchBreakDelayOption);
|
||||||
|
list.addSingleOptionEntry(this.touchTransparencyOption);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpruceContainerWidget buildMappingsStringEditorTab(int width, int height) {
|
||||||
|
return new MappingsStringInputWidget(Position.origin(), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderTitle(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
context.drawCenteredTextWithShadow(this.textRenderer, I18n.translate("midnightcontrols.menu.title"), this.width / 2, 8, 16777215);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MidnightControlsBackground implements Background {
|
||||||
|
private static int transparency = 160;
|
||||||
|
public MidnightControlsBackground() {}
|
||||||
|
public MidnightControlsBackground(int transparency) {
|
||||||
|
MidnightControlsBackground.transparency = transparency;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) {
|
||||||
|
fill(context, widget.getX(), widget.getY(), widget.getX() + widget.getWidth(), widget.getY() + widget.getHeight(), Color.black);
|
||||||
|
}
|
||||||
|
private static void fill(DrawContext context, int x2, int y2, int x1, int y1, Color color) {
|
||||||
|
RenderLayer renderLayer = RenderLayer.getGui();
|
||||||
|
VertexConsumer vertexConsumer = ((DrawContextAccessor)context).getVertexConsumers().getBuffer(renderLayer);
|
||||||
|
|
||||||
|
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;
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
vertexConsumer.vertex((float)x1, (float)y2, 0.0F).color(r, g, b, t);
|
||||||
|
vertexConsumer.vertex((float)x2, (float)y2, 0.0F).color(r, g, b, t);
|
||||||
|
vertexConsumer.vertex((float)x2, (float)y1, 0.0F).color(r, g, b, t);
|
||||||
|
vertexConsumer.vertex((float)x1, (float)y1, 0.0F).color(r, g, b, t);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
context.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption;
|
||||||
|
import org.thinkingstudio.obsidianui.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.PERIODIC_NOTIFICATION,
|
||||||
|
Text.translatable("midnightcontrols.controller.mappings.updated"), Text.empty()));
|
||||||
|
}, Text.translatable("midnightcontrols.tooltip.reload_controller_mappings"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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.RingButtonMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.ring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the controls ring screen.
|
||||||
|
*
|
||||||
|
* @author LambdAurora
|
||||||
|
* @version 1.4.3
|
||||||
|
* @since 1.4.3
|
||||||
|
*/
|
||||||
|
public class RingScreen extends Screen {
|
||||||
|
|
||||||
|
public RingScreen() {
|
||||||
|
super(Text.literal("midnightcontrols.menu.title.ring"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
if (ring.getMaxPages() > 1) {
|
||||||
|
this.addDrawableChild(ButtonWidget.builder(Text.of("◀"), button -> ring.cyclePage(false)).dimensions(5, 5, 20, 20).build());
|
||||||
|
this.addDrawableChild(ButtonWidget.builder(Text.of("▶"), button -> ring.cyclePage(true)).dimensions(width - 25, 5, 20, 20).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
super.render(context, mouseX, mouseY, delta);
|
||||||
|
|
||||||
|
RingPage page = ring.getCurrentPage();
|
||||||
|
page.render(context, this.textRenderer, this.width, this.height, mouseX, mouseY, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
super.close();
|
||||||
|
assert client != null;
|
||||||
|
client.currentScreen = null;
|
||||||
|
RingPage page = ring.getCurrentPage();
|
||||||
|
if (RingPage.selected >= 0 && page.actions[RingPage.selected] != null)
|
||||||
|
page.actions[RingPage.selected].activate(RingButtonMode.PRESS);
|
||||||
|
RingPage.selected = -1;
|
||||||
|
this.removed();
|
||||||
|
}
|
||||||
|
// @Override
|
||||||
|
// public boolean changeFocus(boolean lookForwards) {
|
||||||
|
// if (lookForwards) {
|
||||||
|
// if (RingPage.selected < 7) ++RingPage.selected;
|
||||||
|
// else RingPage.selected = -1;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// if (RingPage.selected > -1) --RingPage.selected;
|
||||||
|
// else RingPage.selected = 7;
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||||
|
if (ring.getCurrentPage().onClick(width, height, (int) mouseX, (int) mouseY)) {
|
||||||
|
this.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.SpruceTexts;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.AbstractSpruceIconButtonWidget;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
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(DrawContext context, 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(context, x, this.getY(), this.binding, MinecraftClient.getInstance());
|
||||||
|
this.iconWidth = size.length();
|
||||||
|
return size.height();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.SpruceTexts;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
|
||||||
|
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 {
|
||||||
|
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.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))));
|
||||||
|
ControlsListWidget bindingsListWidget = new ControlsListWidget(Position.of(this, 0, 43), this.width, this.height - 43 - 35, this);
|
||||||
|
bindingsListWidget.setBackground(new MidnightControlsSettingsScreen.MidnightControlsBackground(130));
|
||||||
|
this.addChild(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(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
context.drawCenteredTextWithShadow(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(context, 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 org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.SpruceTexts;
|
||||||
|
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
|
||||||
|
import org.thinkingstudio.obsidianui.navigation.NavigationUtils;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceSeparatorWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.resource.language.I18n;
|
||||||
|
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.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.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(DrawContext context, 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;
|
||||||
|
context.drawText(textRenderer, this.bindingName, this.getX(), (textY - 9 / 2), 16777215, true);
|
||||||
|
|
||||||
|
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(context, 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(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
this.separatorWidget.render(context, 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
import net.minecraft.advancement.Advancement;
|
import net.minecraft.advancement.Advancement;
|
||||||
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
|
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
|
||||||
@@ -22,8 +22,7 @@ import java.util.Map;
|
|||||||
* Represents an accessor of {@link AdvancementsScreen}.
|
* Represents an accessor of {@link AdvancementsScreen}.
|
||||||
*/
|
*/
|
||||||
@Mixin(AdvancementsScreen.class)
|
@Mixin(AdvancementsScreen.class)
|
||||||
public interface AdvancementsScreenAccessor
|
public interface AdvancementsScreenAccessor {
|
||||||
{
|
|
||||||
@Accessor("advancementHandler")
|
@Accessor("advancementHandler")
|
||||||
ClientAdvancementManager getAdvancementManager();
|
ClientAdvancementManager getAdvancementManager();
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.ChatScreen;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
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(ChatScreen.class)
|
||||||
|
public abstract class ChatScreenMixin extends Screen {
|
||||||
|
@Shadow protected TextFieldWidget chatField;
|
||||||
|
|
||||||
|
protected ChatScreenMixin(Text title) {
|
||||||
|
super(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("TAIL"), method = "init")
|
||||||
|
private void midnightcontrols$moveInputField(CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.moveChat) chatField.setY(4);
|
||||||
|
}
|
||||||
|
@Inject(method = "render", at = @At("HEAD"))
|
||||||
|
private void midnightcontrols$moveInputFieldBackground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, -this.height + 16, 0f);
|
||||||
|
}
|
||||||
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;render(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.BEFORE))
|
||||||
|
private void midnightcontrols$dontMoveOtherStuff(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, this.height - 16, 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,38 +1,47 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
import eu.midnightdust.midnightcontrols.MidnightControls;
|
||||||
import me.lambdaurora.lambdacontrols.client.controller.MovementHandler;
|
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.MinecraftClient;
|
||||||
import net.minecraft.client.input.Input;
|
import net.minecraft.client.input.Input;
|
||||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.entity.MovementType;
|
import net.minecraft.entity.MovementType;
|
||||||
|
import net.minecraft.network.encryption.PlayerPublicKey;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injects the anti fly drifting feature.
|
* Injects the anti fly drifting feature.
|
||||||
*/
|
*/
|
||||||
@Mixin(ClientPlayerEntity.class)
|
@Mixin(ClientPlayerEntity.class)
|
||||||
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
|
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity {
|
||||||
{
|
@Unique private boolean midnightcontrols$driftingPrevented = false;
|
||||||
private boolean lambdacontrols_driftingPrevented = false;
|
|
||||||
|
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) {
|
||||||
|
super(world, profile);
|
||||||
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected abstract boolean hasMovementInput();
|
protected abstract boolean hasMovementInput();
|
||||||
@@ -47,47 +56,43 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
|
|||||||
@Shadow
|
@Shadow
|
||||||
protected abstract boolean isCamera();
|
protected abstract boolean isCamera();
|
||||||
|
|
||||||
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile)
|
@Shadow protected int ticksLeftToDoubleTapSprint;
|
||||||
{
|
|
||||||
super(world, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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"))
|
@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)
|
public void onMove(MovementType type, Vec3d movement, CallbackInfo ci) {
|
||||||
{
|
if (!MidnightControlsConfig.doubleTapToSprint) ticksLeftToDoubleTapSprint = 0;
|
||||||
LambdaControlsClient mod = LambdaControlsClient.get();
|
if (!MidnightControls.isExtrasLoaded) return;
|
||||||
if (type == MovementType.SELF) {
|
if (type == MovementType.SELF) {
|
||||||
if (this.abilities.flying && (!mod.config.hasFlyDrifting() || !mod.config.hasFlyVerticalDrifting())) {
|
if (this.getAbilities().flying && (!MidnightControlsConfig.flyDrifting || !MidnightControlsConfig.verticalFlyDrifting)) {
|
||||||
if (!this.hasMovementInput()) {
|
if (!this.hasMovementInput()) {
|
||||||
if (!this.lambdacontrols_driftingPrevented) {
|
if (!this.midnightcontrols$driftingPrevented) {
|
||||||
if (!mod.config.hasFlyDrifting())
|
if (!MidnightControlsConfig.flyDrifting)
|
||||||
this.setVelocity(this.getVelocity().multiply(0, 1.0, 0));
|
this.setVelocity(this.getVelocity().multiply(0, 1.0, 0));
|
||||||
}
|
}
|
||||||
this.lambdacontrols_driftingPrevented = true;
|
this.midnightcontrols$driftingPrevented = true;
|
||||||
} else
|
} else
|
||||||
this.lambdacontrols_driftingPrevented = false;
|
this.midnightcontrols$driftingPrevented = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(Z)V", shift = At.Shift.AFTER))
|
@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)
|
public void onInputUpdate(CallbackInfo ci) {
|
||||||
{
|
|
||||||
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
|
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
|
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
|
||||||
public void onTickMovement(CallbackInfo ci)
|
public void onTickMovement(CallbackInfo ci) {
|
||||||
{
|
if (this.getAbilities().flying && this.isCamera()) {
|
||||||
if (this.abilities.flying && this.isCamera()) {
|
if (MidnightControlsConfig.verticalFlyDrifting || !MidnightControls.isExtrasLoaded)
|
||||||
if (LambdaControlsClient.get().config.hasFlyVerticalDrifting())
|
|
||||||
return;
|
return;
|
||||||
int moving = 0;
|
int moving = 0;
|
||||||
if (this.input.sneaking) {
|
if (this.input.playerInput.sneak()) {
|
||||||
--moving;
|
--moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.input.jumping) {
|
if (this.input.playerInput.jump()) {
|
||||||
++moving;
|
++moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
|
import net.minecraft.item.ItemGroups;
|
||||||
import net.minecraft.screen.slot.Slot;
|
import net.minecraft.screen.slot.Slot;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -22,38 +23,39 @@ import org.spongepowered.asm.mixin.gen.Invoker;
|
|||||||
* Represents an accessor to CreativeInventoryScreen.
|
* Represents an accessor to CreativeInventoryScreen.
|
||||||
*/
|
*/
|
||||||
@Mixin(CreativeInventoryScreen.class)
|
@Mixin(CreativeInventoryScreen.class)
|
||||||
public interface CreativeInventoryScreenAccessor
|
public interface CreativeInventoryScreenAccessor {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Gets the selected tab.
|
* Gets the selected tab.
|
||||||
*
|
*
|
||||||
* @return The selected tab index.
|
* @return the selected tab index
|
||||||
*/
|
*/
|
||||||
@Accessor("selectedTab")
|
@Accessor("selectedTab")
|
||||||
int getSelectedTab();
|
static ItemGroup getSelectedTab() {
|
||||||
|
return ItemGroups.getDefaultTab();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the selected tab.
|
* Sets the selected tab.
|
||||||
*
|
*
|
||||||
* @param group The tab's item group.
|
* @param group the tab's item group
|
||||||
*/
|
*/
|
||||||
@Invoker("setSelectedTab")
|
@Invoker("setSelectedTab")
|
||||||
void lambdacontrols_setSelectedTab(@NotNull ItemGroup group);
|
void midnightcontrols$setSelectedTab(@NotNull ItemGroup group);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the slot belongs to the creative inventory or not.
|
* Returns whether the slot belongs to the creative inventory or not.
|
||||||
*
|
*
|
||||||
* @param slot The slot to check.
|
* @param slot the slot to check
|
||||||
* @return True if the slot is from the creative inventory, else false.
|
* @return true if the slot is from the creative inventory, else false
|
||||||
*/
|
*/
|
||||||
@Invoker("isCreativeInventorySlot")
|
@Invoker("isCreativeInventorySlot")
|
||||||
boolean lambdacontrols_isCreativeInventorySlot(@Nullable Slot slot);
|
boolean midnightcontrols$isCreativeInventorySlot(@Nullable Slot slot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the current tab has a scrollbar or not.
|
* Returns whether the current tab has a scrollbar or not.
|
||||||
*
|
*
|
||||||
* @return True if the current tab has a scrollbar, else false.
|
* @return true if the current tab has a scrollbar, else false
|
||||||
*/
|
*/
|
||||||
@Invoker("hasScrollbar")
|
@Invoker("hasScrollbar")
|
||||||
boolean lambdacontrols_hasScrollbar();
|
boolean midnightcontrols$hasScrollbar();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.DrawContext;
|
||||||
|
import net.minecraft.client.render.VertexConsumerProvider;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(DrawContext.class)
|
||||||
|
public interface DrawContextAccessor {
|
||||||
|
@Accessor("vertexConsumers")
|
||||||
|
VertexConsumerProvider.Immediate getVertexConsumers();
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.gui.MidnightControlsSettingsScreen;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
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.OptionListWidget;
|
||||||
|
import net.minecraft.client.gui.widget.TextIconButtonWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects the new controls settings button.
|
||||||
|
*/
|
||||||
|
@Mixin(GameOptionsScreen.class)
|
||||||
|
public abstract class GameOptionsScreenMixin extends Screen {
|
||||||
|
@Shadow @Nullable protected OptionListWidget body;
|
||||||
|
@Unique TextIconButtonWidget midnightcontrols$button = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.menu.title.controller"), (button -> this.client.setScreen(new MidnightControlsSettingsScreen(this, false))), true)
|
||||||
|
.dimension(20,20).texture(id("icon/controller"), 20, 20).build();
|
||||||
|
|
||||||
|
protected GameOptionsScreenMixin(Text title) {
|
||||||
|
super(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "init", at = @At("TAIL"))
|
||||||
|
public void midnightcontrols$addMCButton(CallbackInfo ci) {
|
||||||
|
if (this.getClass().toString().equals(ControlsOptionsScreen.class.toString())) {
|
||||||
|
this.midnightcontrols$setButtonPos();
|
||||||
|
this.addDrawableChild(midnightcontrols$button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "refreshWidgetPositions", at = @At("TAIL"))
|
||||||
|
public void midnightcontrols$onResize(CallbackInfo ci) {
|
||||||
|
this.midnightcontrols$setButtonPos();
|
||||||
|
}
|
||||||
|
@Unique
|
||||||
|
public void midnightcontrols$setButtonPos() {
|
||||||
|
if (body != null) {
|
||||||
|
midnightcontrols$button.setPosition(body.getWidth() / 2 + 158, body.getY() + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.render.GameRenderer;
|
||||||
|
import net.minecraft.client.render.RenderTickCounter;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
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 abstract class GameRendererMixin {
|
||||||
|
@Shadow @Final MinecraftClient client;
|
||||||
|
|
||||||
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getX()D", shift = At.Shift.BEFORE))
|
||||||
|
private void midnightcontrols$onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) {
|
||||||
|
if (this.client.currentScreen != null && MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER)
|
||||||
|
MidnightControlsClient.input.onPreRenderScreen(this.client.currentScreen);
|
||||||
|
}
|
||||||
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE))
|
||||||
|
private void midnightcontrols$renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) {
|
||||||
|
MidnightControlsRenderer.renderVirtualCursor(drawContext, client);
|
||||||
|
if (MidnightControlsClient.isWayland) MidnightControlsRenderer.renderWaylandCursor(drawContext, client);
|
||||||
|
drawContext.draw();
|
||||||
|
}
|
||||||
|
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld")
|
||||||
|
private void midnigtcontrols$captureMatrices(RenderTickCounter tickCounter, CallbackInfo ci, @Local(ordinal = 1) Matrix4f matrices) {
|
||||||
|
TouchUtils.lastProjMat.set(RenderSystem.getProjectionMatrix());
|
||||||
|
TouchUtils.lastModMat.set(RenderSystem.getModelViewMatrix());
|
||||||
|
TouchUtils.lastWorldSpaceMatrix.set(matrices);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.PlatformFunctions;
|
||||||
|
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.EMICompat;
|
||||||
|
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.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
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;
|
||||||
|
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 {
|
||||||
|
@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(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.hudEnable) {
|
||||||
|
var client = MinecraftClient.getInstance();
|
||||||
|
int x = 2, y = client.getWindow().getScaledHeight() - 2 - MidnightControlsRenderer.ICON_SIZE;
|
||||||
|
if (PlatformFunctions.isModLoaded("emi") && EMICompat.isEMIEnabled()) {
|
||||||
|
x += 42;
|
||||||
|
}
|
||||||
|
if (!ButtonBinding.TAKE_ALL.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE_ALL,true, client) + 2;
|
||||||
|
if (!ButtonBinding.EXIT.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.EXIT, true, client) + 2;
|
||||||
|
if (PlatformFunctions.isModLoaded("roughlyenoughitems")) {
|
||||||
|
x = 2;
|
||||||
|
y -= 24;
|
||||||
|
}
|
||||||
|
if (PlatformFunctions.isModLoaded("emi") && EMICompat.isEMIEnabled() && EMICompat.isSearchBarCentered()) {
|
||||||
|
x = client.getWindow().getScaledWidth() - 4 - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.pickup"))
|
||||||
|
- client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.quick_move"))
|
||||||
|
- 2 * MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.TAKE) - MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.QUICK_MOVE);
|
||||||
|
y += 2;
|
||||||
|
}
|
||||||
|
if (!ButtonBinding.TAKE.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE, true, client);
|
||||||
|
if (!ButtonBinding.QUICK_MOVE.isNotBound()) MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.QUICK_MOVE, true, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.util.InputUtil;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(InputUtil.class)
|
||||||
|
public abstract class InputUtilMixin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author kabliz
|
||||||
|
* @reason This method is static, and there is a terrible UX issue if raw input is turned on at the same time as
|
||||||
|
* eye tracking. Raw input only tracks literal mice and not other devices, leading to the game appearing to be
|
||||||
|
* unresponsive and the player not understanding why. This overwrite preserves the user's mouse preferences,
|
||||||
|
* while not interfering with eye tracking, and the two modes can be switched between during a play session.
|
||||||
|
*/
|
||||||
|
@Inject(method = "isRawMouseMotionSupported", at = @At("HEAD"), cancellable = true)
|
||||||
|
private static void setRawMouseMotionSupported(CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (MidnightControlsConfig.eyeTrackerAsMouse) cir.setReturnValue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
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.Map;
|
||||||
|
|
||||||
|
@Mixin(KeyBinding.class)
|
||||||
|
public interface KeyBindingIDAccessor {
|
||||||
|
@Accessor @Final
|
||||||
|
static Map<String, KeyBinding> getKEYS_BY_ID() {return null;};
|
||||||
|
}
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
|
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||||
import net.minecraft.client.options.KeyBinding;
|
import net.minecraft.client.option.KeyBinding;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
@Mixin(KeyBinding.class)
|
@Mixin(KeyBinding.class)
|
||||||
public class KeyBindingMixin implements KeyBindingAccessor
|
public abstract class KeyBindingMixin implements KeyBindingAccessor {
|
||||||
{
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private int timesPressed;
|
private int timesPressed;
|
||||||
|
|
||||||
@@ -24,18 +23,16 @@ public class KeyBindingMixin implements KeyBindingAccessor
|
|||||||
private boolean pressed;
|
private boolean pressed;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean lambdacontrols_press()
|
public boolean midnightcontrols$press() {
|
||||||
{
|
|
||||||
boolean oldPressed = this.pressed;
|
boolean oldPressed = this.pressed;
|
||||||
if (!this.pressed)
|
if (!this.pressed)
|
||||||
this.pressed = true;
|
this.pressed = true;
|
||||||
++this.timesPressed;
|
++this.timesPressed;
|
||||||
return oldPressed != this.pressed;
|
return !oldPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean lambdacontrols_unpress()
|
public boolean midnightcontrols$unpress() {
|
||||||
{
|
|
||||||
if (this.pressed) {
|
if (this.pressed) {
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
import net.minecraft.client.Keyboard;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(Keyboard.class)
|
||||||
|
public class KeyboardMixin {
|
||||||
|
@Redirect(method = "onKey", at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;"))
|
||||||
|
private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) {
|
||||||
|
if (instance.currentScreen instanceof TouchscreenOverlay) return null;
|
||||||
|
return instance.currentScreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
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.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.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.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.reacharound;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
@Shadow public abstract void setScreen(Screen screen);
|
||||||
|
|
||||||
|
@Unique private BlockPos midnightcontrols$lastTargetPos;
|
||||||
|
@Unique private Vec3d midnightcontrols$lastPos;
|
||||||
|
@Unique private Direction midnightcontrols$lastTargetSide;
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void onInit(CallbackInfo ci) {
|
||||||
|
MidnightControlsClient.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("TAIL"), method = "setScreen")
|
||||||
|
private void setScreen(Screen screen, CallbackInfo info) {
|
||||||
|
if (MidnightControlsConfig.hideNormalMouse){
|
||||||
|
if (screen != null && !(screen instanceof TouchscreenOverlay)) GLFW.glfwSetInputMode(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_HIDDEN);
|
||||||
|
else GLFW.glfwSetInputMode(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
|
||||||
|
}
|
||||||
|
MidnightControlsClient.onScreenOpen(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "doItemUse()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"), cancellable = true)
|
||||||
|
private void onItemUse(CallbackInfo ci, @Local Hand hand, @Local ItemStack stackInHand) {
|
||||||
|
if (player != null && !stackInHand.isEmpty() && this.player.getPitch(0.f) > 35.0F && reacharound.isReacharoundAvailable()) {
|
||||||
|
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.MISS && this.player.isOnGround()) {
|
||||||
|
if (!stackInHand.isEmpty() && stackInHand.getItem() instanceof BlockItem) {
|
||||||
|
var hitResult = reacharound.getLastReacharoundResult();
|
||||||
|
|
||||||
|
if (hitResult == null || this.interactionManager == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitResult = 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is always supposed to be located at before the line 'this.profiler.swap("Keybindings");'
|
||||||
|
// @Redirect(method = "tick", at = @At(value = "FIELD",target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;", ordinal = 6))
|
||||||
|
// private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) {
|
||||||
|
// if (instance.currentScreen instanceof TouchscreenOverlay) return null;
|
||||||
|
// return instance.currentScreen;
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.Mouse;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
|
@Mixin(Mouse.class)
|
||||||
|
public interface MouseAccessor {
|
||||||
|
@Invoker("onCursorPos")
|
||||||
|
void midnightcontrols$onCursorPos(long window, double x, double y);
|
||||||
|
@Invoker("onMouseButton")
|
||||||
|
void midnightcontrols$onMouseButton(long window, int button, int action, int mods);
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.touch.gui.TouchscreenOverlay;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.Mouse;
|
||||||
|
import net.minecraft.client.util.GlfwUtil;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.item.ThrowablePotionItem;
|
||||||
|
import net.minecraft.item.consume.UseAction;
|
||||||
|
import net.minecraft.util.math.Smoother;
|
||||||
|
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.Unique;
|
||||||
|
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;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mouse.EyeTrackerHandler;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsConfig.doMixedInput;
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds extra access to the mouse.
|
||||||
|
*/
|
||||||
|
@Mixin(Mouse.class)
|
||||||
|
public abstract class MouseMixin implements MouseAccessor {
|
||||||
|
@Shadow @Final private MinecraftClient client;
|
||||||
|
|
||||||
|
@Shadow private double y;
|
||||||
|
|
||||||
|
@Shadow private double cursorDeltaX;
|
||||||
|
|
||||||
|
@Shadow private double cursorDeltaY;
|
||||||
|
|
||||||
|
@Shadow private double x;
|
||||||
|
|
||||||
|
@Shadow private boolean cursorLocked;
|
||||||
|
|
||||||
|
@Shadow private boolean hasResolutionChanged;
|
||||||
|
|
||||||
|
@Shadow private double glfwTime;
|
||||||
|
|
||||||
|
@Shadow @Final private Smoother cursorXSmoother;
|
||||||
|
|
||||||
|
@Shadow @Final private Smoother cursorYSmoother;
|
||||||
|
|
||||||
|
@Shadow private boolean leftButtonClicked;
|
||||||
|
|
||||||
|
@Inject(method = "onMouseButton", at = @At(value = "HEAD"), cancellable = true)
|
||||||
|
private void midnightcontrols$onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
|
||||||
|
if (window != this.client.getWindow().getHandle()) return;
|
||||||
|
if (action == 1 && button == GLFW.GLFW_MOUSE_BUTTON_4 && client.currentScreen != null) {
|
||||||
|
MidnightControlsClient.input.tryGoBack(client.currentScreen);
|
||||||
|
}
|
||||||
|
else if ((client.currentScreen == null && doMixedInput() || client.currentScreen instanceof TouchscreenOverlay) && client.player != null && button == GLFW_MOUSE_BUTTON_1) {
|
||||||
|
double mouseX = x / client.getWindow().getScaleFactor();
|
||||||
|
double mouseY = y / client.getWindow().getScaleFactor();
|
||||||
|
int centerX = client.getWindow().getScaledWidth() / 2;
|
||||||
|
if (action == 1 && mouseY >= (double) (client.getWindow().getScaledHeight() - 22) && 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)) {
|
||||||
|
client.player.getInventory().selectedSlot = slot;
|
||||||
|
ci.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action == 1) {
|
||||||
|
TouchInput.clickStartTime = System.currentTimeMillis();
|
||||||
|
boolean bl = false;
|
||||||
|
if (client.currentScreen instanceof TouchscreenOverlay overlay) bl = overlay.mouseClicked(mouseX, mouseY, button);
|
||||||
|
if (!bl) TouchInput.firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
|
||||||
|
if (client.currentScreen == null) ci.cancel();
|
||||||
|
}
|
||||||
|
else if (TouchInput.mouseReleased(mouseX, mouseY, button)) ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "isCursorLocked", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void midnightcontrols$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 midnightcontrols$onCursorLocked(CallbackInfo ci) {
|
||||||
|
if ((MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.virtualMouse) ||
|
||||||
|
MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN || doMixedInput())
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "updateMouse", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void midnightcontrols$updateMouse(CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.eyeTrackerAsMouse && cursorLocked && client.isWindowFocused()) {
|
||||||
|
// Eye Tracking is only for the camera controlling cursor, we need the normal cursor everywhere else.
|
||||||
|
if (!client.options.smoothCameraEnabled) {
|
||||||
|
cursorXSmoother.clear();
|
||||||
|
cursorYSmoother.clear();
|
||||||
|
}
|
||||||
|
EyeTrackerHandler.updateMouseWithEyeTracking(x + cursorDeltaX, y + cursorDeltaY, client,
|
||||||
|
glfwTime, leftButtonClicked, midnightcontrols$isUsingLongRangedTool(), cursorXSmoother, cursorYSmoother);
|
||||||
|
glfwTime = GlfwUtil.getTime();
|
||||||
|
cursorDeltaX = 0.0;
|
||||||
|
cursorDeltaY = 0.0;
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
if (doMixedInput() && client.isWindowFocused()) {
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private boolean midnightcontrols$isUsingLongRangedTool() {
|
||||||
|
if (client.player == null) return false;
|
||||||
|
ItemStack stack = client.player.getActiveItem();
|
||||||
|
return (leftButtonClicked && (stack.getUseAction() == UseAction.BOW || stack.getUseAction() == UseAction.CROSSBOW ||
|
||||||
|
stack.getUseAction() == UseAction.SPEAR || stack.getItem() instanceof ThrowablePotionItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "lockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;setCursorParameters(JIDD)V",shift = At.Shift.BEFORE), cancellable = true)
|
||||||
|
private void midnightcontrols$lockCursor(CallbackInfo ci) {
|
||||||
|
if ((doMixedInput() || MidnightControlsConfig.eyeTrackerAsMouse)) {
|
||||||
|
//In eye tracking mode, we cannot have the cursor locked to the center.
|
||||||
|
GLFW.glfwSetInputMode(client.getWindow().getHandle(), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
|
client.setScreen(null);
|
||||||
|
hasResolutionChanged = true;
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.RecipeBookScreen;
|
||||||
|
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(RecipeBookScreen.class)
|
||||||
|
public interface RecipeBookScreenAccessor {
|
||||||
|
@Accessor("recipeBook")
|
||||||
|
RecipeBookWidget<?> getRecipeBook();
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
|
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
|
||||||
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
|
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
|
||||||
@@ -18,8 +18,7 @@ import org.spongepowered.asm.mixin.gen.Invoker;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mixin(RecipeBookWidget.class)
|
@Mixin(RecipeBookWidget.class)
|
||||||
public interface RecipeBookWidgetAccessor
|
public interface RecipeBookWidgetAccessor {
|
||||||
{
|
|
||||||
@Accessor("tabButtons")
|
@Accessor("tabButtons")
|
||||||
List<RecipeGroupButtonWidget> getTabButtons();
|
List<RecipeGroupButtonWidget> getTabButtons();
|
||||||
|
|
||||||
@@ -28,7 +27,4 @@ public interface RecipeBookWidgetAccessor
|
|||||||
|
|
||||||
@Accessor("currentTab")
|
@Accessor("currentTab")
|
||||||
void setCurrentTab(RecipeGroupButtonWidget currentTab);
|
void setCurrentTab(RecipeGroupButtonWidget currentTab);
|
||||||
|
|
||||||
@Invoker("refreshResults")
|
|
||||||
void lambdacontrols_refreshResults(boolean resetCurrentPage);
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputHandlers;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.SilentTexturedButtonWidget;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.Drawable;
|
||||||
|
import net.minecraft.client.gui.Element;
|
||||||
|
import net.minecraft.client.gui.Selectable;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
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 static eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay.WIDGETS_LOCATION;
|
||||||
|
|
||||||
|
@Mixin(Screen.class)
|
||||||
|
public abstract class ScreenMixin {
|
||||||
|
@Shadow protected abstract <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement);
|
||||||
|
|
||||||
|
@Shadow public int width;
|
||||||
|
|
||||||
|
@Inject(method = "init(Lnet/minecraft/client/MinecraftClient;II)V", at = @At("TAIL"))
|
||||||
|
public void midnightcontrols$addCloseButton(MinecraftClient client, int width, int height, CallbackInfo ci) {
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && (MidnightControlsConfig.closeButtonScreens.stream().anyMatch(s -> this.getClass().getName().startsWith(s) || ((Object)this) instanceof HandledScreen<?>))) {
|
||||||
|
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(this.width - 30, 10), 20, 20, Text.empty(), btn ->
|
||||||
|
InputHandlers.handleExit().press(client, ButtonBinding.BACK, 0f, ButtonState.PRESS), 20, 160, 20, WIDGETS_LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mixin;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import net.minecraft.client.gui.tab.Tab;
|
||||||
|
import net.minecraft.client.gui.tab.TabManager;
|
||||||
|
import net.minecraft.client.gui.widget.TabNavigationWidget;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(TabNavigationWidget.class)
|
||||||
|
public interface TabNavigationWidgetAccessor {
|
||||||
|
@Accessor
|
||||||
|
TabManager getTabManager();
|
||||||
|
@Accessor
|
||||||
|
ImmutableList<Tab> getTabs();
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.MidnightColorUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.ControlsMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
|
||||||
|
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.BlockHitResult;
|
||||||
|
import net.minecraft.util.hit.HitResult;
|
||||||
|
import net.minecraft.util.math.ColorHelper;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
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.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.reacharound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
@Redirect(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/BlockHitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"))
|
||||||
|
private HitResult.Type dontRenderOutline(BlockHitResult instance) {
|
||||||
|
if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
|
||||||
|
return HitResult.Type.MISS;
|
||||||
|
}
|
||||||
|
return instance.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "renderTargetBlockOutline",
|
||||||
|
at = @At("HEAD")
|
||||||
|
)
|
||||||
|
private void onOutlineRender(Camera camera, VertexConsumerProvider.Immediate vertexConsumers, MatrixStack matrices, boolean translucent, CallbackInfo ci) {
|
||||||
|
if (((MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.touchInControllerMode) || MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN)
|
||||||
|
&& MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
|
||||||
|
this.midnightcontrols$renderFingerOutline(matrices, camera);
|
||||||
|
}
|
||||||
|
this.midnightcontrols$renderReacharoundOutline(matrices, camera);
|
||||||
|
}
|
||||||
|
@Unique
|
||||||
|
private void midnightcontrols$renderFingerOutline(MatrixStack matrices, Camera camera) {
|
||||||
|
if (TouchInput.firstHitResult == null || TouchInput.firstHitResult.getType() != HitResult.Type.BLOCK)
|
||||||
|
return;
|
||||||
|
BlockHitResult result = (BlockHitResult) TouchInput.firstHitResult;
|
||||||
|
var blockPos = result.getBlockPos();
|
||||||
|
if (this.world.getWorldBorder().contains(blockPos) && this.client.player != null) {
|
||||||
|
var outlineShape = this.world.getBlockState(blockPos).getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
|
||||||
|
Color rgb = MidnightColorUtil.hex2Rgb(MidnightControlsConfig.touchOutlineColorHex);
|
||||||
|
if (MidnightControlsConfig.touchOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
|
||||||
|
var pos = camera.getPos();
|
||||||
|
matrices.push();
|
||||||
|
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
|
||||||
|
VertexRendering.drawOutline(matrices, vertexConsumer, outlineShape, blockPos.getX() - pos.getX(), blockPos.getY() - pos.getY(), blockPos.getZ() - pos.getZ(),
|
||||||
|
ColorHelper.withAlpha(MidnightControlsConfig.touchOutlineColorAlpha, rgb.getRGB()));
|
||||||
|
matrices.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Unique
|
||||||
|
private void midnightcontrols$renderReacharoundOutline(MatrixStack matrices, Camera camera) {
|
||||||
|
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !MidnightControlsConfig.shouldRenderReacharoundOutline)
|
||||||
|
return;
|
||||||
|
var result = reacharound.getLastReacharoundResult();
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
var blockPos = result.getBlockPos();
|
||||||
|
if (this.world.getWorldBorder().contains(blockPos) && this.client.player != null) {
|
||||||
|
var stack = this.client.player.getStackInHand(Hand.MAIN_HAND);
|
||||||
|
if (stack == null || !(stack.getItem() instanceof BlockItem))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var block = ((BlockItem) stack.getItem()).getBlock();
|
||||||
|
result = 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()));
|
||||||
|
Color rgb = MidnightColorUtil.hex2Rgb(MidnightControlsConfig.reacharoundOutlineColorHex);
|
||||||
|
if (MidnightControlsConfig.reacharoundOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
|
||||||
|
matrices.push();
|
||||||
|
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
|
||||||
|
VertexRendering.drawOutline(matrices, vertexConsumer, outlineShape, blockPos.getX() - pos.getX(), blockPos.getY() - pos.getY(), blockPos.getZ() - pos.getZ(),
|
||||||
|
ColorHelper.withAlpha(MidnightControlsConfig.touchOutlineColorAlpha, rgb.getRGB()));
|
||||||
|
matrices.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.mouse;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.util.GlfwUtil;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import net.minecraft.util.math.Smoother;
|
||||||
|
|
||||||
|
public class EyeTrackerHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the updateMouse method in the Mouse.class, this changes the mouse algorithm to suit eye tracking.
|
||||||
|
* This requires the cursor to not be locked, and the raw input setting to be turned off.
|
||||||
|
*/
|
||||||
|
public static void updateMouseWithEyeTracking(double mouseX, double mouseY, MinecraftClient client, double lastMouseUpdateTime, boolean holdingLeftMouseButton, boolean usingLongRangedTool, Smoother smoothX, Smoother smoothY) {
|
||||||
|
if (client.player == null) return;
|
||||||
|
/* The player wants objects of interest to be moved under the crosshair that is always center of screen.
|
||||||
|
* Normal mouse controls operate with the delta values from the direction of mouse movement,
|
||||||
|
* but in eye tracking we want to use the cursor's actual x,y values (their point of gaze), relative to
|
||||||
|
* the screen center (where the crosshair is). This new eye tracking delta creates a vector that points
|
||||||
|
* from the crosshair to the gaze point. As the player keeps their eyes on the object of interest, we pull
|
||||||
|
* that object into the center until the object is underneath the crosshair.
|
||||||
|
*/
|
||||||
|
double deltaTime = GlfwUtil.getTime() - lastMouseUpdateTime;
|
||||||
|
|
||||||
|
// The center of screen is the new (0,0)
|
||||||
|
double centerX = client.getWindow().getWidth() / 2.0;
|
||||||
|
double centerY = client.getWindow().getHeight() / 2.0;
|
||||||
|
double gazeRawX = mouseX - centerX;
|
||||||
|
double gazeRawY = mouseY - centerY;
|
||||||
|
|
||||||
|
//This part follows the original mouse.java somewhat closely, with different constants
|
||||||
|
double feeling = 2.5;
|
||||||
|
double sensitivity = client.options.getMouseSensitivity().getValue() * feeling;
|
||||||
|
double spyglass = sensitivity * sensitivity * sensitivity;
|
||||||
|
double moveScalar = spyglass * 8.0;
|
||||||
|
|
||||||
|
double frameScalar;
|
||||||
|
if(client.options.getPerspective().isFirstPerson() && client.player.isUsingSpyglass()) {
|
||||||
|
frameScalar = spyglass;
|
||||||
|
} else {
|
||||||
|
frameScalar = moveScalar;
|
||||||
|
}
|
||||||
|
if(holdingLeftMouseButton && !usingLongRangedTool) {
|
||||||
|
frameScalar *= 0.5; //Don't move the camera so much while mining. It's annoying.
|
||||||
|
}
|
||||||
|
|
||||||
|
// The longest vector connects the center to the corner of the screen, so that is our maximum magnitude for
|
||||||
|
// normalization. We use normalized screen size vector for resolution independent control
|
||||||
|
double magnitudeMax = Math.sqrt(centerX*centerX + centerY*centerY);
|
||||||
|
double normalizedX = gazeRawX / magnitudeMax;
|
||||||
|
double normalizedY = gazeRawY / magnitudeMax;
|
||||||
|
|
||||||
|
double moveX = normalizedX * frameScalar;
|
||||||
|
double moveY = normalizedY * frameScalar;
|
||||||
|
if (client.options.smoothCameraEnabled) {
|
||||||
|
moveX = smoothX.smooth(moveX, moveScalar*deltaTime);
|
||||||
|
moveY = smoothY.smooth(moveY, moveScalar*deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The player entity's needs their facing rotated.
|
||||||
|
double invertY = 1.0;
|
||||||
|
double moveMagnitude = Math.sqrt(normalizedX*normalizedX + normalizedY*normalizedY);
|
||||||
|
if (client.options.getInvertYMouse().getValue()) {
|
||||||
|
invertY = -1.0;
|
||||||
|
}
|
||||||
|
boolean notInDeadzone = (moveMagnitude > MidnightControlsConfig.eyeTrackerDeadzone) && !usingLongRangedTool;
|
||||||
|
if (client.player != null && notInDeadzone) {
|
||||||
|
client.player.changeLookDirection(moveX, moveY * invertY);
|
||||||
|
client.getTutorialManager().onUpdateMouse(moveX, moveY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.google.gson.Gson;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.text.OrderedText;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ButtonBindingRingAction extends RingAction {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final ButtonBinding binding;
|
||||||
|
|
||||||
|
public ButtonBindingRingAction(@NotNull ButtonBinding binding) {
|
||||||
|
super();
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String getName() {
|
||||||
|
return this.binding.getTranslationKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAction(@NotNull RingButtonMode mode) {
|
||||||
|
binding.handle(MinecraftClient.getInstance(), 1.0f, ButtonState.PRESS);
|
||||||
|
if (binding.asKeyBinding().isPresent()) {
|
||||||
|
binding.asKeyBinding().get().setPressed(true);
|
||||||
|
((KeyBindingAccessor)binding.asKeyBinding().get()).midnightcontrols$press();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawIcon(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) {
|
||||||
|
List<OrderedText> lines = textRenderer.wrapLines(Text.translatable(this.getName()), MidnightRing.ELEMENT_SIZE);
|
||||||
|
for (int i = 0; i < lines.size(); ++i) {
|
||||||
|
context.drawCenteredTextWithShadow(textRenderer, lines.get(i), x + MidnightRing.ELEMENT_SIZE / 2, y + MidnightRing.ELEMENT_SIZE / 2 - textRenderer.fontHeight / 2 * (lines.size()-1) - textRenderer.fontHeight / 2 + textRenderer.fontHeight * i, 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 Gson config) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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 eu.midnightdust.midnightcontrols.MidnightControls;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
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 = 75;
|
||||||
|
|
||||||
|
private final Map<String, RingAction.Factory> actionFactories = new Object2ObjectOpenHashMap<>();
|
||||||
|
private final List<RingPage> pages = new ArrayList<>(Collections.singletonList(RingPage.DEFAULT));
|
||||||
|
private int currentPage = 0;
|
||||||
|
|
||||||
|
public MidnightRing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerAction(@NotNull String name, @NotNull RingAction.Factory factory) {
|
||||||
|
if (this.actionFactories.containsKey(name)) {
|
||||||
|
MidnightControls.warn("Tried to register a ring action twice: \"" + name + "\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.actionFactories.put(name, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the ring from configuration.
|
||||||
|
*/
|
||||||
|
public void loadFromConfig() {
|
||||||
|
List<String> configBindings = MidnightControlsConfig.ringBindings;
|
||||||
|
if (configBindings != null) {
|
||||||
|
this.pages.clear();
|
||||||
|
int bindingIndex = 0;
|
||||||
|
for (int i = 0; i < MathHelper.ceil(configBindings.size() / 8f); ++i) {
|
||||||
|
this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(configBindings.size() / 8f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String binding : configBindings) {
|
||||||
|
ButtonBinding buttonBinding = InputManager.getBinding(binding);
|
||||||
|
if (buttonBinding != null) {
|
||||||
|
RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f));
|
||||||
|
page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding));
|
||||||
|
++bindingIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.pages.isEmpty()) {
|
||||||
|
this.pages.add(RingPage.DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Loads the ring from all unbound keys.
|
||||||
|
*/
|
||||||
|
public void loadFromUnbound() {
|
||||||
|
List<ButtonBinding> unboundBindings = InputManager.getUnboundBindings();
|
||||||
|
if (unboundBindings != null) {
|
||||||
|
this.pages.clear();
|
||||||
|
int bindingIndex = 0;
|
||||||
|
for (int i = 0; i < MathHelper.ceil(unboundBindings.size() / 8f); ++i) {
|
||||||
|
this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(unboundBindings.size() / 8f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ButtonBinding buttonBinding : unboundBindings) {
|
||||||
|
if (buttonBinding != null) {
|
||||||
|
RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f));
|
||||||
|
page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding));
|
||||||
|
++bindingIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.pages.isEmpty()) {
|
||||||
|
this.pages.add(RingPage.DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public int getMaxPages() {
|
||||||
|
return this.pages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
public void cyclePage(boolean forwards) {
|
||||||
|
if (forwards) {
|
||||||
|
if (currentPage < pages.size()-1) ++currentPage;
|
||||||
|
else currentPage = 0;
|
||||||
|
} else {
|
||||||
|
if (currentPage > 0) --currentPage;
|
||||||
|
else currentPage = pages.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.google.gson.Gson;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
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 {
|
||||||
|
protected boolean activated = false;
|
||||||
|
|
||||||
|
public RingAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered, int index) {
|
||||||
|
context.fill(x, y, x + MidnightRing.ELEMENT_SIZE, y + MidnightRing.ELEMENT_SIZE, hovered || RingPage.selected == index ? 0xbb777777 : 0xbb000000);
|
||||||
|
drawIcon(context, textRenderer, x, y, hovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void drawIcon(@NotNull DrawContext context, @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 Gson config);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2020 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,
|
* Licensed under the MIT license. For more information,
|
||||||
* see the LICENSE file.
|
* see the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lambdaurora.lambdacontrols.client.ring;
|
package eu.midnightdust.midnightcontrols.client.ring;
|
||||||
|
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.text.TranslatableText;
|
|
||||||
import org.aperlambda.lambdacommon.utils.Nameable;
|
import org.aperlambda.lambdacommon.utils.Nameable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -21,8 +20,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @version 1.4.0
|
* @version 1.4.0
|
||||||
* @since 1.4.0
|
* @since 1.4.0
|
||||||
*/
|
*/
|
||||||
public enum RingButtonMode implements Nameable
|
public enum RingButtonMode implements Nameable {
|
||||||
{
|
|
||||||
PRESS("press"),
|
PRESS("press"),
|
||||||
HOLD("hold"),
|
HOLD("hold"),
|
||||||
TOGGLE("toggle");
|
TOGGLE("toggle");
|
||||||
@@ -30,20 +28,18 @@ public enum RingButtonMode implements Nameable
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final Text text;
|
private final Text text;
|
||||||
|
|
||||||
RingButtonMode(@NotNull String name)
|
RingButtonMode(@NotNull String name) {
|
||||||
{
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.text = new TranslatableText(this.getTranslationKey());
|
this.text = Text.translatable(this.getTranslationKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next ring button mode available.
|
* Returns the next ring button mode available.
|
||||||
*
|
*
|
||||||
* @return The next ring button mode.
|
* @return the next ring button mode
|
||||||
*/
|
*/
|
||||||
public @NotNull RingButtonMode next()
|
public @NotNull RingButtonMode next() {
|
||||||
{
|
var v = values();
|
||||||
RingButtonMode[] v = values();
|
|
||||||
if (v.length == this.ordinal() + 1)
|
if (v.length == this.ordinal() + 1)
|
||||||
return v[0];
|
return v[0];
|
||||||
return v[this.ordinal() + 1];
|
return v[this.ordinal() + 1];
|
||||||
@@ -52,26 +48,23 @@ public enum RingButtonMode implements Nameable
|
|||||||
/**
|
/**
|
||||||
* Returns the translation key of this ring button mode.
|
* Returns the translation key of this ring button mode.
|
||||||
*
|
*
|
||||||
* @return The translation key of this ring button mode.
|
* @return the translation key of this ring button mode
|
||||||
*/
|
*/
|
||||||
public @NotNull String getTranslationKey()
|
public @NotNull String getTranslationKey() {
|
||||||
{
|
return "midnightcontrols.ring.button_mode." + this.getName();
|
||||||
return "lambdacontrols.ring.button_mode." + this.getName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the translated name of this ring button mode.
|
* Gets the translated name of this ring button mode.
|
||||||
*
|
*
|
||||||
* @return The translated name of this ring button mode.
|
* @return the translated name of this ring button mode
|
||||||
*/
|
*/
|
||||||
public @NotNull Text getTranslatedText()
|
public @NotNull Text getTranslatedText() {
|
||||||
{
|
|
||||||
return this.text;
|
return this.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String getName()
|
public @NotNull String getName() {
|
||||||
{
|
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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 eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a ring page.
|
||||||
|
*
|
||||||
|
* @author LambdAurora
|
||||||
|
* @version 1.5.0
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
public class RingPage {
|
||||||
|
public static final RingPage DEFAULT = new RingPage("Default");
|
||||||
|
|
||||||
|
public final String name;
|
||||||
|
public static int selected = -1;
|
||||||
|
public RingAction[] actions = new RingAction[8];
|
||||||
|
|
||||||
|
public RingPage(@NotNull String name) {
|
||||||
|
this.name = name;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
this.actions[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the ring page.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @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 DrawContext context, @NotNull TextRenderer textRenderer, int width, int height, int mouseX, int mouseY, float tickDelta) {
|
||||||
|
int centerX = width / 2;
|
||||||
|
int centerY = height / 2;
|
||||||
|
if (MidnightControlsClient.ring.getMaxPages() > 1) context.drawCenteredTextWithShadow(textRenderer, name, centerX, 5, 0xffffff);
|
||||||
|
|
||||||
|
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(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
|
||||||
|
x += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
}
|
||||||
|
y += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
x = centerX - offset;
|
||||||
|
for (int i = 3; i < 5; i++) {
|
||||||
|
var ringAction = this.actions[i];
|
||||||
|
if (ringAction != null)
|
||||||
|
ringAction.render(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
|
||||||
|
x += (MidnightRing.ELEMENT_SIZE + 5) * 2;
|
||||||
|
}
|
||||||
|
y += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
x = centerX - offset;
|
||||||
|
for (int i = 5; i < 8; i++) {
|
||||||
|
var ringAction = this.actions[i];
|
||||||
|
if (ringAction != null)
|
||||||
|
ringAction.render(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
|
||||||
|
x += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && selected < 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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 += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
}
|
||||||
|
y += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
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 += (MidnightRing.ELEMENT_SIZE + 5) * 2;
|
||||||
|
}
|
||||||
|
y += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
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 += MidnightRing.ELEMENT_SIZE + 5;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.touch;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
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.EntityHitResult;
|
||||||
|
import net.minecraft.util.hit.HitResult;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsConfig.doMixedInput;
|
||||||
|
|
||||||
|
public class TouchInput {
|
||||||
|
private static final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
public static long clickStartTime;
|
||||||
|
public static HitResult firstHitResult = null;
|
||||||
|
public static boolean isDragging = false;
|
||||||
|
|
||||||
|
public static void tick() {
|
||||||
|
if ((client.currentScreen == null && doMixedInput()) || client.currentScreen instanceof TouchscreenOverlay) {
|
||||||
|
double scaleFactor = client.getWindow().getScaleFactor();
|
||||||
|
if (clickStartTime > 0 && System.currentTimeMillis() - clickStartTime >= MidnightControlsConfig.touchBreakDelay) {
|
||||||
|
mouseHeldDown(client.mouse.getX() / scaleFactor, client.mouse.getY() / scaleFactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void mouseHeldDown(double mouseX, double mouseY) {
|
||||||
|
assert client != null;
|
||||||
|
assert client.player != null;
|
||||||
|
assert client.interactionManager != null;
|
||||||
|
|
||||||
|
if (client.player.getMainHandStack() != null && TouchUtils.hasInWorldUseAction(client.player.getMainHandStack())) {
|
||||||
|
client.interactionManager.interactItem(client.player, client.player.getActiveHand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HitResult result = TouchUtils.getTargetedObject(mouseX, mouseY);
|
||||||
|
if (result == null || firstHitResult == null) {
|
||||||
|
client.interactionManager.cancelBlockBreaking();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result instanceof BlockHitResult blockHit && firstHitResult instanceof BlockHitResult firstBlock && blockHit.getBlockPos().equals(firstBlock.getBlockPos())) {
|
||||||
|
if (MidnightControlsConfig.debug) System.out.println(blockHit.getBlockPos().toString());
|
||||||
|
if (client.interactionManager.updateBlockBreakingProgress(blockHit.getBlockPos(), blockHit.getSide())) {
|
||||||
|
client.particleManager.addBlockBreakingParticles(blockHit.getBlockPos(), blockHit.getSide());
|
||||||
|
client.player.swingHand(Hand.MAIN_HAND);
|
||||||
|
} else client.interactionManager.cancelBlockBreaking();
|
||||||
|
firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
|
||||||
|
}
|
||||||
|
else if (result instanceof EntityHitResult entityHit && firstHitResult instanceof EntityHitResult firstEntity && entityHit.getEntity().getUuid().compareTo(firstEntity.getEntity().getUuid()) == 0) {
|
||||||
|
if (client.interactionManager.interactEntity(client.player, entityHit.getEntity(), client.player.getActiveHand()) == ActionResult.SUCCESS) {
|
||||||
|
client.player.swingHand(Hand.MAIN_HAND);
|
||||||
|
}
|
||||||
|
firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||||
|
isDragging = false;
|
||||||
|
firstHitResult = null;
|
||||||
|
if (client.interactionManager != null) client.interactionManager.cancelBlockBreaking();
|
||||||
|
if ((client.currentScreen == null || !client.currentScreen.mouseReleased(mouseX, mouseY, button)) && System.currentTimeMillis() - clickStartTime < MidnightControlsConfig.touchBreakDelay) {
|
||||||
|
assert client.player != null;
|
||||||
|
assert client.world != null;
|
||||||
|
assert client.interactionManager != null;
|
||||||
|
clickStartTime = -1;
|
||||||
|
|
||||||
|
if (client.player.getMainHandStack() != null && TouchUtils.hasInWorldUseAction(client.player.getMainHandStack())) {
|
||||||
|
client.interactionManager.stopUsingItem(client.player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
HitResult result = TouchUtils.getTargetedObject(mouseX, mouseY);
|
||||||
|
if (result == null) return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (result instanceof BlockHitResult blockHit) {
|
||||||
|
BlockPos blockPos = blockHit.getBlockPos().offset(blockHit.getSide());
|
||||||
|
BlockState state = client.world.getBlockState(blockPos);
|
||||||
|
|
||||||
|
if (client.world.isAir(blockPos) || state.isReplaceable()) {
|
||||||
|
ItemStack stackInHand = client.player.getMainHandStack();
|
||||||
|
int previousStackCount = stackInHand.getCount();
|
||||||
|
var interaction = client.interactionManager.interactBlock(client.player, client.player.getActiveHand(), blockHit);
|
||||||
|
if (interaction.isAccepted()) {
|
||||||
|
//if (interaction.shouldSwingHand()) {
|
||||||
|
client.player.swingHand(client.player.preferredHand);
|
||||||
|
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || client.interactionManager.hasCreativeInventory())) {
|
||||||
|
client.gameRenderer.firstPersonRenderer.resetEquipProgress(client.player.preferredHand);
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result instanceof EntityHitResult entityHit) {
|
||||||
|
client.interactionManager.attackEntity(client.player, entityHit.getEntity());
|
||||||
|
client.player.swingHand(Hand.MAIN_HAND);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clickStartTime = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.touch;
|
||||||
|
|
||||||
|
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.render.Camera;
|
||||||
|
import net.minecraft.entity.projectile.ProjectileUtil;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.item.consume.UseAction;
|
||||||
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
|
import net.minecraft.util.hit.HitResult;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.RaycastContext;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightReacharound.getPlayerRange;
|
||||||
|
|
||||||
|
public class TouchUtils {
|
||||||
|
private static final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
public static final Matrix4f lastWorldSpaceMatrix = new Matrix4f();
|
||||||
|
public static final Matrix4f lastProjMat = new Matrix4f();
|
||||||
|
public static final Matrix4f lastModMat = new Matrix4f();
|
||||||
|
|
||||||
|
public static HitResult getTargetedObject(double mouseX, double mouseY) {
|
||||||
|
if (client.player == null || client.world == null || MidnightControlsConfig.touchMode == TouchMode.CROSSHAIR || PlatformFunctions.isModLoaded("vulkanmod")) {
|
||||||
|
return client.crosshairTarget;
|
||||||
|
}
|
||||||
|
Vec3d near = screenSpaceToWorldSpace(mouseX, mouseY, 0);
|
||||||
|
Vec3d far = screenSpaceToWorldSpace(mouseX, mouseY, 1);
|
||||||
|
|
||||||
|
float playerRange = getPlayerRange(client);
|
||||||
|
EntityHitResult entityCast = ProjectileUtil.raycast(client.player, near, far, Box.from(client.player.getPos()).expand(playerRange), entity -> (!entity.isSpectator() && entity.isAttackable()), playerRange * playerRange);
|
||||||
|
|
||||||
|
if (entityCast != null && entityCast.getType() == HitResult.Type.ENTITY) return entityCast;
|
||||||
|
|
||||||
|
BlockHitResult result = client.world.raycast(new RaycastContext(near, far, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.ANY, client.player));
|
||||||
|
|
||||||
|
if (client.player.getPos().distanceTo(result.getPos()) > playerRange) return null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Taken from https://github.com/0x3C50/Renderer/blob/master/src/main/java/me/x150/renderer/util/RendererUtils.java#L270
|
||||||
|
* Credits to 0x3C50 */
|
||||||
|
public static Vec3d screenSpaceToWorldSpace(double x, double y, double d) {
|
||||||
|
Camera camera = client.getEntityRenderDispatcher().camera;
|
||||||
|
int displayHeight = client.getWindow().getScaledHeight();
|
||||||
|
int displayWidth = client.getWindow().getScaledWidth();
|
||||||
|
int[] viewport = new int[4];
|
||||||
|
GL11.glGetIntegerv(GL11.GL_VIEWPORT, viewport);
|
||||||
|
Vector3f target = new Vector3f();
|
||||||
|
|
||||||
|
Matrix4f matrixProj = new Matrix4f(lastProjMat);
|
||||||
|
Matrix4f matrixModel = new Matrix4f(lastModMat);
|
||||||
|
|
||||||
|
matrixProj.mul(matrixModel)
|
||||||
|
.mul(lastWorldSpaceMatrix)
|
||||||
|
.unproject((float) x / displayWidth * viewport[2],
|
||||||
|
(float) (displayHeight - y) / displayHeight * viewport[3], (float) d, viewport, target);
|
||||||
|
|
||||||
|
return new Vec3d(target.x, target.y, target.z).add(camera.getPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasInWorldUseAction(ItemStack stack) {
|
||||||
|
UseAction action = stack.getUseAction();
|
||||||
|
return action == UseAction.BOW || action == UseAction.BRUSH || action == UseAction.SPEAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.touch.gui;
|
||||||
|
|
||||||
|
import net.minecraft.item.consume.UseAction;
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import net.minecraft.item.ArmorItem;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
public class ItemUseButtonWidget extends SpruceButtonWidget {
|
||||||
|
|
||||||
|
public ItemUseButtonWidget(Position position, int width, int height, Text message, PressAction action) {
|
||||||
|
super(position, width, height, message, action);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void onRelease(double mouseX, double mouseY) {
|
||||||
|
assert client.player != null;
|
||||||
|
assert client.interactionManager != null;
|
||||||
|
UseAction action = client.player.getMainHandStack().getUseAction();
|
||||||
|
if (action == UseAction.SPYGLASS || action == UseAction.TOOT_HORN) client.interactionManager.stopUsingItem(client.player);
|
||||||
|
super.onRelease(mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(boolean visible) {
|
||||||
|
if (visible && client.player != null && client.player.getMainHandStack() != null) {
|
||||||
|
UseAction action = client.player.getMainHandStack().getUseAction();
|
||||||
|
if (action == UseAction.EAT) {
|
||||||
|
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat"));
|
||||||
|
} else if (action == UseAction.DRINK) {
|
||||||
|
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.drink"));
|
||||||
|
} else if (client.player.getMainHandStack().getItem() instanceof ArmorItem) {
|
||||||
|
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.equip"));
|
||||||
|
} else if (!action.equals(UseAction.NONE)) {
|
||||||
|
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.use"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setAlpha(MidnightControlsConfig.touchTransparency / 100f);
|
||||||
|
super.setVisible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.touch.gui;
|
||||||
|
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.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() {}
|
||||||
|
@Override
|
||||||
|
protected void onRelease(double mouseX, double mouseY) {
|
||||||
|
this.setActive(false);
|
||||||
|
super.onClick(mouseX, mouseY);
|
||||||
|
super.onRelease(mouseX, mouseY);
|
||||||
|
this.setActive(true);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onClick(double mouseX, double mouseY) {
|
||||||
|
this.setActive(true);
|
||||||
|
super.onClick(mouseX, mouseY);
|
||||||
|
this.setActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.touch.gui;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.storage.AxisStorage;
|
||||||
|
import net.minecraft.client.gui.screen.ChatScreen;
|
||||||
|
import net.minecraft.client.gui.screen.GameMenuScreen;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.item.ArmorItem;
|
||||||
|
import net.minecraft.item.consume.UseAction;
|
||||||
|
import net.minecraft.util.Arm;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import org.thinkingstudio.obsidianui.Position;
|
||||||
|
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
|
||||||
|
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.compat.EmotecraftCompat;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
|
||||||
|
import net.minecraft.client.gui.widget.TextIconButtonWidget;
|
||||||
|
import net.minecraft.client.option.KeyBinding;
|
||||||
|
import net.minecraft.client.texture.MissingSprite;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.client.util.InputUtil;
|
||||||
|
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.MidnightControls.id;
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.input;
|
||||||
|
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 = id("textures/gui/widgets.png");
|
||||||
|
private SilentTexturedButtonWidget inventoryButton;
|
||||||
|
private SilentTexturedButtonWidget swapHandsButton;
|
||||||
|
private SilentTexturedButtonWidget dropButton;
|
||||||
|
private ItemUseButtonWidget useButton;
|
||||||
|
private SilentTexturedButtonWidget jumpButton;
|
||||||
|
private SilentTexturedButtonWidget flyButton;
|
||||||
|
private SilentTexturedButtonWidget flyUpButton;
|
||||||
|
private SilentTexturedButtonWidget flyDownButton;
|
||||||
|
private SilentTexturedButtonWidget forwardButton;
|
||||||
|
private SilentTexturedButtonWidget forwardLeftButton;
|
||||||
|
private SilentTexturedButtonWidget forwardRightButton;
|
||||||
|
private SilentTexturedButtonWidget leftButton;
|
||||||
|
private SilentTexturedButtonWidget rightButton;
|
||||||
|
private SilentTexturedButtonWidget backButton;
|
||||||
|
private SilentTexturedButtonWidget startSneakButton;
|
||||||
|
private SilentTexturedButtonWidget endSneakButton;
|
||||||
|
private int flyButtonEnableTicks = 0;
|
||||||
|
private int forwardButtonTick = 0;
|
||||||
|
|
||||||
|
public TouchscreenOverlay() {
|
||||||
|
super(Text.literal("Touchscreen Overlay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {}
|
||||||
|
|
||||||
|
private void pauseGame() {
|
||||||
|
assert this.client != null;
|
||||||
|
this.client.setScreen(new GameMenuScreen(true));
|
||||||
|
if (this.client.isIntegratedServerRunning() && !Objects.requireNonNull(this.client.getServer()).isRemote()) {
|
||||||
|
this.client.getSoundManager().pauseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the forward button ticks cooldown.
|
||||||
|
*
|
||||||
|
* @param state The button state.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void updateForwardButtonsState(boolean state) {
|
||||||
|
this.forwardButtonTick = state ? -1 : 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the jump buttons.
|
||||||
|
*/
|
||||||
|
private void updateJumpButtons() {
|
||||||
|
assert this.client != null;
|
||||||
|
assert this.client.player != null;
|
||||||
|
float transparency = MidnightControlsConfig.touchTransparency / 100f;
|
||||||
|
|
||||||
|
if (this.client.player.getAbilities().flying) {
|
||||||
|
boolean oldStateFly = this.flyButton.isVisible();
|
||||||
|
this.jumpButton.setVisible(false);
|
||||||
|
this.flyButton.setVisible(true);
|
||||||
|
this.flyUpButton.setVisible(true);
|
||||||
|
this.flyDownButton.setVisible(true);
|
||||||
|
this.flyButton.setAlpha(transparency);
|
||||||
|
this.flyUpButton.setAlpha(transparency);
|
||||||
|
this.flyDownButton.setAlpha(transparency);
|
||||||
|
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);
|
||||||
|
this.jumpButton.setAlpha(transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the jump button.
|
||||||
|
*
|
||||||
|
* @param btn The pressed button.
|
||||||
|
*/
|
||||||
|
private void handleJump(SpruceButtonWidget btn) {
|
||||||
|
assert this.client != null;
|
||||||
|
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(btn.isActive());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles the jump button.
|
||||||
|
*
|
||||||
|
* @param state The state.
|
||||||
|
*/
|
||||||
|
private void setJump(boolean state) {
|
||||||
|
assert this.client != null;
|
||||||
|
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
assert this.client != null;
|
||||||
|
assert this.client.player != null;
|
||||||
|
assert this.client.interactionManager != null;
|
||||||
|
int scaledWidth = this.client.getWindow().getScaledWidth();
|
||||||
|
int scaledHeight = this.client.getWindow().getScaledHeight();
|
||||||
|
int emoteOffset = 0;
|
||||||
|
if (PlatformFunctions.isModLoaded("emotecraft")) {
|
||||||
|
emoteOffset = 10;
|
||||||
|
TextIconButtonWidget emoteButton = TextIconButtonWidget.builder(Text.empty(), btn -> EmotecraftCompat.openEmotecraftScreen(this), true).width(20).texture(id("touch/emote"), 20, 20).build();
|
||||||
|
emoteButton.setPosition(scaledWidth / 2 - 30, 0);
|
||||||
|
this.addDrawableChild(emoteButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextIconButtonWidget chatButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.client.setScreen(new ChatScreen("")), true).width(20).texture(id("touch/chat"), 20, 20).build();
|
||||||
|
chatButton.setPosition(scaledWidth / 2 - 20 + emoteOffset, 0);
|
||||||
|
this.addDrawableChild(chatButton);
|
||||||
|
TextIconButtonWidget pauseButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.pauseGame(), true).width(20).texture(id("touch/pause"), 20, 20).build();
|
||||||
|
pauseButton.setPosition(scaledWidth / 2 + emoteOffset, 0);
|
||||||
|
this.addDrawableChild(pauseButton);
|
||||||
|
// 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(this.inventoryButton = new SilentTexturedButtonWidget(Position.of(inventoryButtonX, inventoryButtonY), 20, 20, Text.empty(), btn -> {
|
||||||
|
if (this.client.interactionManager.hasRidingInventory()) {
|
||||||
|
this.client.player.openRidingInventory();
|
||||||
|
} else {
|
||||||
|
this.client.getTutorialManager().onInventoryOpened();
|
||||||
|
this.client.setScreen(new InventoryScreen(this.client.player));
|
||||||
|
}
|
||||||
|
}, 20, 0, 20, WIDGETS_LOCATION, 256, 256));
|
||||||
|
;
|
||||||
|
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(this.swapHandsButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY), 20, 20, Text.empty(),
|
||||||
|
button -> {
|
||||||
|
if (button.isActive()) {
|
||||||
|
if (!this.client.player.isSpectator()) {
|
||||||
|
Objects.requireNonNull(this.client.getNetworkHandler()).sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},0, 160, 20, WIDGETS_LOCATION));
|
||||||
|
// Drop
|
||||||
|
this.addDrawableChild(this.dropButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY + 5 + 20), 20, 20, Text.empty(), btn -> {
|
||||||
|
if (btn.isActive() && !client.player.isSpectator() && client.player.dropSelectedItem(false)) {
|
||||||
|
client.player.swingHand(Hand.MAIN_HAND);
|
||||||
|
}
|
||||||
|
}, 20, 160, 20, WIDGETS_LOCATION));
|
||||||
|
// Use
|
||||||
|
this.addDrawableChild(this.useButton = new ItemUseButtonWidget(Position.of(width/2-25, height - 70), 50, 17, Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat"), btn ->
|
||||||
|
client.interactionManager.interactItem(client.player, client.player.getActiveHand())));
|
||||||
|
// 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.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.addDrawableChild(this.forwardButton = 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.addDrawableChild(this.rightButton =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(this.backButton = 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(this.leftButton = 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
|
||||||
|
));
|
||||||
|
initCustomButtons(true);
|
||||||
|
initCustomButtons(false);
|
||||||
|
|
||||||
|
this.setButtonProperties(MidnightControlsConfig.touchTransparency / 100f);
|
||||||
|
}
|
||||||
|
private void initCustomButtons(boolean left) {
|
||||||
|
assert client != null;
|
||||||
|
Identifier emptySprite = id("touch/empty");
|
||||||
|
List<String> list = left ? MidnightControlsConfig.leftTouchBinds : MidnightControlsConfig.rightTouchBinds;
|
||||||
|
Sprite missingSprite = client.getGuiAtlasManager().getSprite(MissingSprite.getMissingSpriteId());
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
String bindName = list.get(i);
|
||||||
|
ButtonBinding binding = InputManager.getBinding(bindName);
|
||||||
|
if (binding == null) continue;
|
||||||
|
boolean hasTexture = client.getGuiAtlasManager().getSprite(id("binding/"+bindName)) != missingSprite;
|
||||||
|
if (MidnightControlsConfig.debug) System.out.println(left +" "+id("binding/"+bindName)+" "+ hasTexture);
|
||||||
|
var button = TextIconButtonWidget.builder(Text.translatable(binding.getTranslationKey()), b -> binding.handle(client, 1, ButtonState.PRESS), hasTexture)
|
||||||
|
.texture(hasTexture ? id("binding/"+bindName) : emptySprite, 20, 20).dimension(20, 20).build();
|
||||||
|
button.setPosition(left ? (3+(i*23)) : this.width-(23+(i*23)), 3);
|
||||||
|
button.setAlpha(MidnightControlsConfig.touchTransparency / 100f);
|
||||||
|
this.addDrawableChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void setButtonProperties(float transparency) {
|
||||||
|
this.inventoryButton.setAlpha(transparency);
|
||||||
|
this.dropButton.setAlpha(transparency);
|
||||||
|
this.swapHandsButton.setAlpha(transparency);
|
||||||
|
this.jumpButton.setAlpha(transparency);
|
||||||
|
this.flyButton.setAlpha(transparency);
|
||||||
|
this.flyUpButton.setAlpha(transparency);
|
||||||
|
this.useButton.setAlpha(Math.min(transparency+0.1f, 1.0f));
|
||||||
|
this.flyDownButton.setAlpha(transparency);
|
||||||
|
this.startSneakButton.setAlpha(transparency);
|
||||||
|
this.endSneakButton.setAlpha(transparency);
|
||||||
|
this.forwardButton.setAlpha(transparency);
|
||||||
|
this.forwardLeftButton.setAlpha(Math.max(0.05f, transparency-0.1f));
|
||||||
|
this.forwardRightButton.setAlpha(Math.max(0.05f, transparency-0.1f));
|
||||||
|
this.leftButton.setAlpha(transparency);
|
||||||
|
this.rightButton.setAlpha(transparency);
|
||||||
|
this.backButton.setAlpha(transparency);
|
||||||
|
this.useButton.setAlpha(Math.min(transparency+0.1f, 1.0f));
|
||||||
|
this.endSneakButton.setVisible(false);
|
||||||
|
this.forwardLeftButton.setVisible(false);
|
||||||
|
this.forwardRightButton.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
assert this.client != null;
|
||||||
|
assert this.client.interactionManager != null;
|
||||||
|
assert this.client.player != null;
|
||||||
|
|
||||||
|
if (this.forwardButtonTick > 0) {
|
||||||
|
--this.forwardButtonTick;
|
||||||
|
} else {
|
||||||
|
this.forwardLeftButton.setVisible(false);
|
||||||
|
this.forwardRightButton.setVisible(false);
|
||||||
|
}
|
||||||
|
this.useButton.setVisible(client.player.getMainHandStack() != null && (client.player.getMainHandStack().getUseAction() != UseAction.NONE || client.player.getMainHandStack().getItem() instanceof ArmorItem) && !TouchUtils.hasInWorldUseAction(client.player.getMainHandStack()));
|
||||||
|
this.updateJumpButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 (TouchInput.isDragging) {
|
||||||
|
if (!MidnightControlsConfig.invertTouch) {
|
||||||
|
deltaX = -deltaX;
|
||||||
|
deltaY = -deltaY;
|
||||||
|
}
|
||||||
|
input.handleTouchscreenLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) deltaY, 0.25d));
|
||||||
|
input.handleTouchscreenLook(AxisStorage.of(GLFW_GAMEPAD_AXIS_RIGHT_X, (float) deltaX, 0.25d));
|
||||||
|
}
|
||||||
|
else TouchInput.isDragging = true;
|
||||||
|
}
|
||||||
|
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||||
|
KeyBinding.onKeyPressed(InputUtil.fromKeyCode(keyCode, scanCode));
|
||||||
|
super.keyPressed(keyCode,scanCode,modifiers);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,124 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import org.aperlambda.lambdacommon.utils.Pair;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.input;
|
||||||
|
|
||||||
|
public class InventoryUtil {
|
||||||
|
// Finds the closest slot in the GUI within 14 pixels.
|
||||||
|
public static Optional<Slot> findClosestSlot(HandledScreen<?> inventory, int direction) {
|
||||||
|
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);
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int targetMouseX = 0;
|
||||||
|
private static int targetMouseY = 0;
|
||||||
|
|
||||||
|
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
|
||||||
|
public static void moveMouseToClosestSlot(@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<net.minecraft.util.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 new net.minecraft.util.Pair<>(slot, distance);
|
||||||
|
}).filter(entry -> entry.getRight() <= 14.0)
|
||||||
|
.min(Comparator.comparingDouble(net.minecraft.util.Pair::getRight));
|
||||||
|
|
||||||
|
if (closestSlot.isPresent() && client.player != null) {
|
||||||
|
var slot = closestSlot.get().getLeft();
|
||||||
|
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) {
|
||||||
|
targetMouseX += (int) (deltaX * 0.75);
|
||||||
|
targetMouseY += (int) (deltaY * 0.75);
|
||||||
|
} else {
|
||||||
|
input.mouseSpeedX *= 0.3F;
|
||||||
|
input.mouseSpeedY *= 0.3F;
|
||||||
|
}
|
||||||
|
input.mouseSpeedX *= .75F;
|
||||||
|
input.mouseSpeedY *= .75F;
|
||||||
|
} else {
|
||||||
|
input.mouseSpeedX *= .1F;
|
||||||
|
input.mouseSpeedY *= .1F;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input.mouseSpeedX *= .3F;
|
||||||
|
input.mouseSpeedY *= .3F;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input.mouseSpeedX = 0.F;
|
||||||
|
input.mouseSpeedY = 0.F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,23 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
|
||||||
|
public class MathUtil {
|
||||||
|
public static class PolarUtil {
|
||||||
|
public float polarX;
|
||||||
|
public float polarY;
|
||||||
|
public PolarUtil() {}
|
||||||
|
|
||||||
|
public void calculate(float x, float y, float speedFactor) {
|
||||||
|
calculate(x, y, speedFactor, 0);
|
||||||
|
}
|
||||||
|
public void calculate(float x, float y, float speedFactor, double deadZone) {
|
||||||
|
double inputR = Math.pow(x, 2) + Math.pow(y, 2);
|
||||||
|
inputR = (Math.abs(speedFactor * MathHelper.clamp(inputR,0.f,1.f)));
|
||||||
|
inputR = inputR < deadZone ? 0f : (inputR-deadZone) / (1f-deadZone);
|
||||||
|
double inputTheta = Math.atan2(y, x);
|
||||||
|
polarX = (float) (inputR *Math.cos(inputTheta));
|
||||||
|
polarY = (float) (inputR *Math.sin(inputTheta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class RainbowColor {
|
||||||
|
public static float hue;
|
||||||
|
public static void tick() {
|
||||||
|
if (hue > 1) hue = 0f;
|
||||||
|
hue = hue + 0.01f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Color radialRainbow(float saturation, float brightness) {
|
||||||
|
return Color.getHSBColor(hue, saturation, brightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
|
||||||
|
|
||||||
|
public class ToggleSneakSprintUtil {
|
||||||
|
public static boolean toggleSneak(ButtonBinding button) {
|
||||||
|
if (client.player == null) return false;
|
||||||
|
boolean isFlying = client.player.getAbilities().flying;
|
||||||
|
var option = client.options.getSneakToggled();
|
||||||
|
|
||||||
|
button.asKeyBinding().ifPresent(binding -> {
|
||||||
|
boolean sneakToggled = option.getValue();
|
||||||
|
if (isFlying && sneakToggled)
|
||||||
|
option.setValue(false);
|
||||||
|
else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled)
|
||||||
|
option.setValue(!sneakToggled);
|
||||||
|
binding.setPressed(button.isPressed());
|
||||||
|
if (isFlying && sneakToggled)
|
||||||
|
option.setValue(true);
|
||||||
|
else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled)
|
||||||
|
option.setValue(sneakToggled);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static boolean toggleSprint(ButtonBinding button) {
|
||||||
|
if (client.player == null) return false;
|
||||||
|
boolean isFlying = client.player.getAbilities().flying;
|
||||||
|
var option = client.options.getSprintToggled();
|
||||||
|
|
||||||
|
button.asKeyBinding().ifPresent(binding -> {
|
||||||
|
boolean sprintToggled = option.getValue();
|
||||||
|
if (isFlying && sprintToggled)
|
||||||
|
option.setValue(false);
|
||||||
|
else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled)
|
||||||
|
option.setValue(!sprintToggled);
|
||||||
|
binding.setPressed(button.isPressed());
|
||||||
|
if (client.player.getAbilities().flying && sprintToggled)
|
||||||
|
option.setValue(true);
|
||||||
|
else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled)
|
||||||
|
option.setValue(sprintToggled);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util.platform;
|
||||||
|
|
||||||
|
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||||
|
import net.minecraft.client.gui.widget.PressableWidget;
|
||||||
|
import net.minecraft.item.ItemGroup;
|
||||||
|
import net.minecraft.item.ItemGroups;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ItemGroupUtil {
|
||||||
|
@ExpectPlatform
|
||||||
|
public static List<ItemGroup> getVisibleGroups(CreativeInventoryScreen screen) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean cyclePage(boolean next, CreativeInventoryScreen screen) {
|
||||||
|
try {
|
||||||
|
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 (next && element.getMessage().getString().equals(">")) {
|
||||||
|
element.onPress();
|
||||||
|
return true;
|
||||||
|
} else if (element.getMessage().getString().equals("<")) {
|
||||||
|
element.onPress();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ItemGroup cycleTab(boolean next, MinecraftClient client) {
|
||||||
|
ItemGroup currentTab = CreativeInventoryScreenAccessor.getSelectedTab();
|
||||||
|
int currentColumn = currentTab.getColumn();
|
||||||
|
ItemGroup.Row currentRow = currentTab.getRow();
|
||||||
|
ItemGroup newTab = null;
|
||||||
|
List<ItemGroup> visibleTabs = ItemGroupUtil.getVisibleGroups((CreativeInventoryScreen) client.currentScreen);
|
||||||
|
for (ItemGroup tab : visibleTabs) {
|
||||||
|
if (tab.getRow().equals(currentRow) && ((newTab == null && ((next && tab.getColumn() > currentColumn) ||
|
||||||
|
(!next && tab.getColumn() < currentColumn))) || (newTab != null && ((next && tab.getColumn() > currentColumn && tab.getColumn() < newTab.getColumn()) ||
|
||||||
|
(!next && tab.getColumn() < currentColumn && tab.getColumn() > newTab.getColumn())))))
|
||||||
|
newTab = tab;
|
||||||
|
}
|
||||||
|
if (newTab == null)
|
||||||
|
for (ItemGroup tab : visibleTabs) {
|
||||||
|
if ((tab.getRow().compareTo(currentRow)) != 0 && ((next && newTab == null || next && newTab.getColumn() > tab.getColumn()) || (!next && newTab == null) || (!next && newTab.getColumn() < tab.getColumn())))
|
||||||
|
newTab = tab;
|
||||||
|
}
|
||||||
|
if (newTab == null) {
|
||||||
|
for (ItemGroup tab : visibleTabs) {
|
||||||
|
if ((next && tab.getRow() == ItemGroup.Row.TOP && tab.getColumn() == 0) ||
|
||||||
|
!next && tab.getRow() == ItemGroup.Row.BOTTOM && (newTab == null || tab.getColumn() > newTab.getColumn()))
|
||||||
|
newTab = tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newTab == null || newTab.equals(currentTab)) newTab = ItemGroups.getDefaultTab();
|
||||||
|
return newTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util.platform;
|
||||||
|
|
||||||
|
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||||
|
import net.minecraft.network.packet.CustomPayload;
|
||||||
|
import net.minecraft.network.packet.Packet;
|
||||||
|
|
||||||
|
public class NetworkUtil {
|
||||||
|
@ExpectPlatform
|
||||||
|
public static void sendPacketC2S(Packet<?> packet) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
@ExpectPlatform
|
||||||
|
public static void sendPayloadC2S(CustomPayload payload) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util.storage;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.MidnightInput.BUTTON_COOLDOWNS;
|
||||||
|
import static eu.midnightdust.midnightcontrols.client.controller.InputManager.STATES;
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER;
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_X;
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y;
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER;
|
||||||
|
|
||||||
|
public class AxisStorage {
|
||||||
|
public final int axis;
|
||||||
|
public float value, absValue;
|
||||||
|
public final double deadZone;
|
||||||
|
public final Polarity polarity;
|
||||||
|
public final boolean isTrigger;
|
||||||
|
public final ButtonState buttonState;
|
||||||
|
|
||||||
|
// Used for joysticks
|
||||||
|
public static AxisStorage of(int axis, float value) {
|
||||||
|
return new AxisStorage(axis, value, isLeftAxis(axis) ? MidnightControlsConfig.leftDeadZone : MidnightControlsConfig.rightDeadZone);
|
||||||
|
}
|
||||||
|
public static AxisStorage of(int axis, float value, double deadZone) {
|
||||||
|
return new AxisStorage(axis, value, deadZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AxisStorage(int axis, float value, double deadZone) {
|
||||||
|
this.axis = axis;
|
||||||
|
this.deadZone = deadZone;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
this.isTrigger = true;
|
||||||
|
if (value < -.5f) {
|
||||||
|
value = 0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Fixes Triggers not working correctly on some controllers
|
||||||
|
if (MidnightControlsConfig.triggerFix) {
|
||||||
|
value = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else isTrigger = false;
|
||||||
|
|
||||||
|
this.value = value;
|
||||||
|
this.buttonState = value > .5f ? ButtonState.PRESS : (value < -.5f ? ButtonState.RELEASE : ButtonState.NONE);
|
||||||
|
this.absValue = Math.abs(value);
|
||||||
|
boolean currentPlusState = value > deadZone;
|
||||||
|
boolean currentMinusState = value < -deadZone;
|
||||||
|
if (isTrigger) currentMinusState = false;
|
||||||
|
else if (!MidnightControlsConfig.analogMovement && isLeftAxis(axis)) {
|
||||||
|
currentPlusState = buttonState == ButtonState.PRESS;
|
||||||
|
currentMinusState = buttonState == ButtonState.RELEASE;
|
||||||
|
}
|
||||||
|
this.polarity = currentPlusState ? AxisStorage.Polarity.PLUS : currentMinusState ? AxisStorage.Polarity.MINUS : AxisStorage.Polarity.ZERO;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the specified axis as a button.
|
||||||
|
*
|
||||||
|
* @param positive true if the axis part is positive, else false
|
||||||
|
* @return the axis as a button
|
||||||
|
*/
|
||||||
|
public int getButtonId(boolean positive) {
|
||||||
|
return ButtonBinding.axisAsButton(axis, positive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupButtonStates() {
|
||||||
|
var posButton = getButtonId(true);
|
||||||
|
var negButton = getButtonId(false);
|
||||||
|
var previousPlusState = STATES.getOrDefault(posButton, ButtonState.NONE);
|
||||||
|
var previousMinusState = STATES.getOrDefault(negButton, ButtonState.NONE);
|
||||||
|
|
||||||
|
if (polarity.isPositive() != previousPlusState.isPressed()) {
|
||||||
|
STATES.put(posButton, polarity.isPositive() ? ButtonState.PRESS : ButtonState.RELEASE);
|
||||||
|
if (polarity.isPositive())
|
||||||
|
BUTTON_COOLDOWNS.put(posButton, 5);
|
||||||
|
} else if (polarity.isPositive()) {
|
||||||
|
STATES.put(posButton, ButtonState.REPEAT);
|
||||||
|
if (BUTTON_COOLDOWNS.getOrDefault(posButton, 0) == 0) {
|
||||||
|
BUTTON_COOLDOWNS.put(posButton, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polarity.isNegative() != previousMinusState.isPressed()) {
|
||||||
|
STATES.put(negButton, polarity.isNegative() ? ButtonState.PRESS : ButtonState.RELEASE);
|
||||||
|
if (polarity.isNegative())
|
||||||
|
BUTTON_COOLDOWNS.put(negButton, 5);
|
||||||
|
} else if (polarity.isNegative()) {
|
||||||
|
STATES.put(negButton, ButtonState.REPEAT);
|
||||||
|
if (BUTTON_COOLDOWNS.getOrDefault(negButton, 0) == 0) {
|
||||||
|
BUTTON_COOLDOWNS.put(negButton, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static boolean isLeftAxis(int axis) {
|
||||||
|
return axis == GLFW_GAMEPAD_AXIS_LEFT_X || axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER;
|
||||||
|
}
|
||||||
|
public static boolean isRightAxis(int axis) {
|
||||||
|
return !isLeftAxis(axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Polarity {
|
||||||
|
MINUS(-1), ZERO(0), PLUS(1);
|
||||||
|
|
||||||
|
public final int multiplier;
|
||||||
|
Polarity(int multiplier) {
|
||||||
|
this.multiplier = multiplier;
|
||||||
|
}
|
||||||
|
public boolean isPositive() {
|
||||||
|
return this == PLUS;
|
||||||
|
}
|
||||||
|
public boolean isNegative() {
|
||||||
|
return this == MINUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.client.util.storage;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT;
|
||||||
|
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP;
|
||||||
|
|
||||||
|
public class ButtonStorage {
|
||||||
|
public final int button;
|
||||||
|
public final ButtonState state;
|
||||||
|
|
||||||
|
public static ButtonStorage of(int button, ButtonState state) {
|
||||||
|
return new ButtonStorage(button, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ButtonStorage(int button, ButtonState state) {
|
||||||
|
this.button = button;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
public boolean isDpad() {
|
||||||
|
return button >= GLFW_GAMEPAD_BUTTON_DPAD_UP && button <= GLFW_GAMEPAD_BUTTON_DPAD_LEFT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package eu.midnightdust.midnightcontrols.packet;
|
||||||
|
|
||||||
|
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
|
||||||
|
import net.minecraft.network.RegistryByteBuf;
|
||||||
|
import net.minecraft.network.codec.PacketCodec;
|
||||||
|
import net.minecraft.network.packet.CustomPayload;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record ControlsModePayload(String controlsMode) implements CustomPayload {
|
||||||
|
public static final Id<ControlsModePayload> PACKET_ID = new Id<>(MidnightControlsConstants.CONTROLS_MODE_CHANNEL);
|
||||||
|
public static final PacketCodec<RegistryByteBuf, ControlsModePayload> codec = PacketCodec.of(ControlsModePayload::write, ControlsModePayload::read);
|
||||||
|
|
||||||
|
public static ControlsModePayload read(RegistryByteBuf buf) {
|
||||||
|
return new ControlsModePayload(buf.readString(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(RegistryByteBuf buf) {
|
||||||
|
Objects.requireNonNull(controlsMode, "Controls mode cannot be null.");
|
||||||
|
buf.writeString(controlsMode, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Id<? extends CustomPayload> getId() {
|
||||||
|
return PACKET_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user