Compare commits
No commits in common. "mkdocs" and "neoforge" have entirely different histories.
50
.forgejo/workflows/build-jar.yaml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
name: Build TurnBasedMC and create Release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-release-exists:
|
||||||
|
runs-on: any_archLinux
|
||||||
|
outputs:
|
||||||
|
status: ${{ steps.release_exists_check.outputs.http_code }}
|
||||||
|
steps:
|
||||||
|
- name: Check if release already exists
|
||||||
|
id: release_exists_check
|
||||||
|
run: |
|
||||||
|
curl -X GET "https://git.seodisparate.com/api/v1/repos/stephenseo/TurnBasedMinecraftMod/releases/tags/${GITHUB_REF_NAME}" \
|
||||||
|
-H 'accept: application/json' -o release_check_resp.json 2>/dev/null \
|
||||||
|
-w '%{http_code}\n' | sed 's/^\([0-9]\+\)/http_code=\1/' >> "$GITHUB_OUTPUT"
|
||||||
|
build-and-create-release:
|
||||||
|
needs: check-release-exists
|
||||||
|
if: ${{ needs.check-release-exists.outputs.status == '404' }}
|
||||||
|
runs-on: highmem_self
|
||||||
|
steps:
|
||||||
|
- run: git clone --depth=1 --no-single-branch https://git.seodisparate.com/stephenseo/TurnBasedMinecraftMod.git TurnBasedMinecraftMod
|
||||||
|
- run: cd TurnBasedMinecraftMod && git checkout ${GITHUB_REF_NAME}
|
||||||
|
- run: cd TurnBasedMinecraftMod && sed -i '/org.gradle.jvmargs/s/Xmx[0-9]\+[mMgG]/Xmx1024m/' gradle.properties && echo 'neogradle.subsystems.decompiler.maxThreads=1' >> gradle.properties
|
||||||
|
- run: cd TurnBasedMinecraftMod && ./gradlew --console=plain build
|
||||||
|
- run: cd TurnBasedMinecraftMod/build/libs && find . -type f -regex '.*all.jar$' -exec sha256sum '{}' ';' -exec bash -c 'sha256sum {} >> sha256sums.txt' ';' && java --version >> javaVersion.txt && javac --version >> javaVersion.txt
|
||||||
|
- name: Create release and attach jar
|
||||||
|
run: |
|
||||||
|
curl --fail-with-body -X 'POST' \
|
||||||
|
"https://git.seodisparate.com/api/v1/repos/stephenseo/TurnBasedMinecraftMod/releases" \
|
||||||
|
-H 'accept: application/json' \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{
|
||||||
|
\"name\": \"TurnBasedMinecraftMod version ${GITHUB_REF_NAME}\",
|
||||||
|
\"body\": \"See the [Changelog](https://git.seodisparate.com/stephenseo/TurnBasedMinecraftMod/src/branch/neoforge/Changelog.md)
|
||||||
|
|
||||||
|
$(java --version | sed -n '1p;2,$s/^/ /p')
|
||||||
|
$(javac --version)
|
||||||
|
$(find TurnBasedMinecraftMod/build/libs -regex '.*all.jar$' -exec sha256sum '{}' ';')\",
|
||||||
|
\"tag_name\": \"${GITHUB_REF_NAME}\"
|
||||||
|
}" > response.json \
|
||||||
|
&& curl --fail-with-body -X 'POST' \
|
||||||
|
"https://git.seodisparate.com/api/v1/repos/stephenseo/TurnBasedMinecraftMod/releases/$(jq .id < response.json)/assets" \
|
||||||
|
-H 'accept: application/json' \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H 'Content-Type: multipart/form-data' \
|
||||||
|
-F "attachment=@$(find TurnBasedMinecraftMod/build/libs -regex '.*all.jar$');type=application/java-archive" > response2.json
|
29
.github/workflows/deploy_gh_pages.yml
vendored
|
@ -1,29 +0,0 @@
|
||||||
name: deploy_gh_pages_mkdocs
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- mkdocs
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
if: github.repository == 'Stephen-Seo/TurnBasedMinecraftMod'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Get Python and Git
|
|
||||||
run: |
|
|
||||||
sudo apt-get update && sudo apt-get install python3 git
|
|
||||||
- name: Get mkdocs
|
|
||||||
run: |
|
|
||||||
sudo pip install mkdocs
|
|
||||||
- name: Clone and Checkout repository
|
|
||||||
run: |
|
|
||||||
git clone --depth=1 --no-single-branch https://github.com/Stephen-Seo/TurnBasedMinecraftMod.git TBMM
|
|
||||||
cd TBMM && git checkout mkdocs
|
|
||||||
- name: Publish with mkdocs
|
|
||||||
run: |
|
|
||||||
cd TBMM/tbmm-docs
|
|
||||||
git config --global user.name 'Stephen Seo'
|
|
||||||
git config --global user.email 'seo.disparate@gmail.com'
|
|
||||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
|
|
||||||
mkdocs gh-deploy
|
|
25
.gitignore
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# eclipse
|
||||||
|
bin
|
||||||
|
*.launch
|
||||||
|
.settings
|
||||||
|
.metadata
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
|
||||||
|
# idea
|
||||||
|
out
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# gradle
|
||||||
|
build
|
||||||
|
.gradle
|
||||||
|
|
||||||
|
# other
|
||||||
|
eclipse
|
||||||
|
run
|
||||||
|
|
||||||
|
logs/
|
||||||
|
runs/
|
560
Changelog.md
Normal file
|
@ -0,0 +1,560 @@
|
||||||
|
# Upcoming changes
|
||||||
|
|
||||||
|
# Version Forge-1.26.5
|
||||||
|
|
||||||
|
Update TBM\_Config.toml to have haste\_speed and slow\_speed for all mob
|
||||||
|
entries.
|
||||||
|
|
||||||
|
Update Config to merge in new changes. This means that the existing config will
|
||||||
|
be overwritten much less frequently (if ever).
|
||||||
|
|
||||||
|
Update to Forge 52.0.26 (MC 1.21.1).
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.5-MC-1.21.1
|
||||||
|
|
||||||
|
Update TBM\_Config.toml to have haste\_speed and slow\_speed for all mob
|
||||||
|
entries.
|
||||||
|
|
||||||
|
Update ClientConfigGui to quit to mod-menu screen in NeoForge (before, it quit
|
||||||
|
to the main screen or current game).
|
||||||
|
|
||||||
|
Update Config to merge in new changes. This means that the existing config will
|
||||||
|
be overwritten much less frequently (if ever).
|
||||||
|
|
||||||
|
Update to NeoForge 21.1.74 (MC 1.21.1).
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.5
|
||||||
|
|
||||||
|
Update TBM\_Config.toml to have haste\_speed and slow\_speed for all mob
|
||||||
|
entries.
|
||||||
|
|
||||||
|
Update ClientConfigGui to quit to mod-menu screen in NeoForge (before, it quit
|
||||||
|
to the main screen or current game).
|
||||||
|
|
||||||
|
Update Config to merge in new changes. This means that the existing config will
|
||||||
|
be overwritten much less frequently (if ever).
|
||||||
|
|
||||||
|
Update to NeoForge 21.3.11-beta (MC 1.21.3).
|
||||||
|
|
||||||
|
# Version Forge-1.26.4
|
||||||
|
|
||||||
|
[Add support for "per-player-stats" in Turn-Based-Battle.](https://stephen-seo.github.io/TurnBasedMinecraftMod/server_config/#per-player-settings)
|
||||||
|
|
||||||
|
Update to Forge 52.0.24 (MC 1.21.1).
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.4-MC-1.21.1
|
||||||
|
|
||||||
|
[Add support for "per-player-stats" in Turn-Based-Battle.](https://stephen-seo.github.io/TurnBasedMinecraftMod/server_config/#per-player-settings)
|
||||||
|
|
||||||
|
Update to Neoforge 21.1.73 (Minecraft 1.21.1).
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.4
|
||||||
|
|
||||||
|
[Add support for "per-player-stats" in Turn-Based-Battle.](https://stephen-seo.github.io/TurnBasedMinecraftMod/server_config/#per-player-settings)
|
||||||
|
|
||||||
|
Update to NeoForge 21.3.6-beta (MC 1.21.3).
|
||||||
|
|
||||||
|
# Version Forge-1.26.3
|
||||||
|
|
||||||
|
Tweak to "Ping" packet to not create client-local Battle instance if it does
|
||||||
|
not exist.
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.3
|
||||||
|
|
||||||
|
Port to NeoForge 21.3.2-beta (MC 1.21.3).
|
||||||
|
|
||||||
|
Note that MC 1.21.1 (NeoForge 21.1.72) will still be supported in a separate
|
||||||
|
branch (neoforge\_mc1.21.1) until MC version 1.22 is released.
|
||||||
|
|
||||||
|
Tweak to "Ping" packet to not create client-local Battle instance if it does
|
||||||
|
not exist.
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.3-MC-1.21.1
|
||||||
|
|
||||||
|
Tweak to "Ping" packet to not create client-local Battle instance if it does
|
||||||
|
not exist.
|
||||||
|
|
||||||
|
# Version Forge-1.26.2
|
||||||
|
|
||||||
|
Show battling Entities next to their attack button in the BattleGUI.
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.2
|
||||||
|
|
||||||
|
Show battling Entities next to their attack button in the BattleGUI.
|
||||||
|
|
||||||
|
# Version Forge-1.26.1
|
||||||
|
|
||||||
|
Minor fixes/refactorings that should make the mod more robust.
|
||||||
|
|
||||||
|
Port to Forge 52.0.22 (Minecraft 1.21.1).
|
||||||
|
|
||||||
|
Allow leaving battle GUI with Escape key (temporarily), and some refactorings
|
||||||
|
to (hopefully) fix that pesky transient client-freeze-bug.
|
||||||
|
|
||||||
|
Minecraft's music should be paused during battle, even if it starts mid-battle.
|
||||||
|
(Minecraft's music may play up to 4 seconds before it is paused by TBMM.)
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.1
|
||||||
|
|
||||||
|
Minor fixes/refactorings that should make the mod more robust.
|
||||||
|
|
||||||
|
Port to NeoForge-21.1.72 (Minecraft 1.21.1).
|
||||||
|
|
||||||
|
Allow leaving battle GUI with Escape key (temporarily), and some refactorings
|
||||||
|
to (hopefully) fix that pesky transient client-freeze-bug.
|
||||||
|
|
||||||
|
Minecraft's music should be paused during battle, even if it starts mid-battle.
|
||||||
|
(Minecraft's music may play up to 4 seconds before it is paused by TBMM.)
|
||||||
|
|
||||||
|
# Version Forge-1.26.0
|
||||||
|
|
||||||
|
Port to Forge 52.0.21 (Minecraft 1.21.1).
|
||||||
|
|
||||||
|
Client-config available via `/tbm-client-edit` (same as NeoForge), but there is
|
||||||
|
no way to access it via the mod-list (unlike NeoForge). Removed from file
|
||||||
|
holding server-side config (same as NeoForge).
|
||||||
|
|
||||||
|
Add option in client-config to set battle/silly music volume, and an option for
|
||||||
|
whether or not battle/silly music volume is affected by global music volume
|
||||||
|
setting and whether or not it is affected by master volume setting.
|
||||||
|
|
||||||
|
Proper volume handling (like in the NeoForge branch).
|
||||||
|
|
||||||
|
Added Armadillo, Bogged, and Breeze to mob list in config.
|
||||||
|
|
||||||
|
# Version NeoForge-1.26.0
|
||||||
|
|
||||||
|
Make it possible to open the client-config from the Mod-list GUI.
|
||||||
|
|
||||||
|
Port to NeoForge 21.1.69 (Minecraft 1.21.1).
|
||||||
|
|
||||||
|
Fix volume handling of battle/silly music. (Previous implementation did not
|
||||||
|
properly reduce volume based on Minecraft's "music volume" setting.)
|
||||||
|
|
||||||
|
Move client-config to NeoForge's configuration.
|
||||||
|
|
||||||
|
Add GUI to edit client-config that can be opened with /tbm-client-edit command.
|
||||||
|
|
||||||
|
Add option in client-config to set battle/silly music volume, and an option for
|
||||||
|
whether or not battle/silly music volume is affected by global music volume
|
||||||
|
setting and whether or not it is affected by master volume setting.
|
||||||
|
|
||||||
|
Added Armadillo, Bogged, and Breeze to mob list in config.
|
||||||
|
|
||||||
|
# Version NeoForge-1.25.2
|
||||||
|
|
||||||
|
Fix invalid use of throwable potions. (Previously, the Player would "drink"
|
||||||
|
splash/lingering potions when used.) Now, if a splash/lingering potion is
|
||||||
|
"Use"d in battle, it will be thrown.
|
||||||
|
|
||||||
|
Add experimental support for "right-click" of arbitrary items on hotbar when
|
||||||
|
"Use" is used in battle.
|
||||||
|
|
||||||
|
# Version Forge-1.25.2
|
||||||
|
|
||||||
|
Fix invalid use of throwable potions. (Previously, the Player would "drink"
|
||||||
|
splash/lingering potions when used.) Now, if a splash/lingering potion is
|
||||||
|
"Use"d in battle, it will be thrown.
|
||||||
|
|
||||||
|
Add experimental support for "right-click" of arbitrary items on hotbar when
|
||||||
|
"Use" is used in battle.
|
||||||
|
|
||||||
|
# Version Forge-1.25.1
|
||||||
|
|
||||||
|
Add icon for mod in Mod list description.
|
||||||
|
|
||||||
|
Update for Forge 49.0.19.
|
||||||
|
Works on Forge Minecraft 1.20.4.
|
||||||
|
|
||||||
|
# Version NeoForge-1.25.1
|
||||||
|
|
||||||
|
Add icon for mod in Mod list description.
|
||||||
|
|
||||||
|
Update for NeoForge 20.4.108-beta.
|
||||||
|
Works on NeoForge Minecraft 1.20.4.
|
||||||
|
|
||||||
|
# Version NeoForge-1.25.0
|
||||||
|
|
||||||
|
Add new dependency `j-ogg-vorbis`.
|
||||||
|
|
||||||
|
Implement playing Vorbis encoded .ogg files for battle/silly music.
|
||||||
|
|
||||||
|
# Version Forge-1.25.0
|
||||||
|
|
||||||
|
Remove usage of "shadow jar" in build.gradle, and use jarJar instead.
|
||||||
|
|
||||||
|
Add new dependency `j-ogg-vorbis`.
|
||||||
|
|
||||||
|
Implement playing Vorbis encoded .ogg files for battle/silly music.
|
||||||
|
|
||||||
|
# Version NeoForge-1.24.0
|
||||||
|
|
||||||
|
Update to NeoForge 1.20.2-20.2.88.
|
||||||
|
|
||||||
|
Allow use of Crossbows in battle (it should behave identically to Bows).
|
||||||
|
|
||||||
|
# Version Forge-1.24.0
|
||||||
|
|
||||||
|
Update to Forge 1.20.2-48.1.0.
|
||||||
|
|
||||||
|
The `master` branch of this repository will track the build for Minecraft Forge.
|
||||||
|
|
||||||
|
The `neoforge` branch of this repo. will track the build for NeoForge.
|
||||||
|
|
||||||
|
Allow use of Crossbows in battle (it should behave identically to Bows).
|
||||||
|
|
||||||
|
# Version 1.23.1
|
||||||
|
|
||||||
|
More robust handling of disallowed Damage Sources in battle (via config).
|
||||||
|
Basically, the mod will load all possible damage sources. Damage sources to be
|
||||||
|
ignored in battle can be modified with "/tbm-server-edit" and clicking on
|
||||||
|
"ignore\_damage\_sources". It can also be manually modified in the server
|
||||||
|
config's "ignore\_damage\_sources" array.
|
||||||
|
|
||||||
|
# Version 1.23.0
|
||||||
|
|
||||||
|
Support reproducible builds. This means that if this mod is compiled, then it
|
||||||
|
should be byte-by-byte exactly the same as another compiled jar (assuming it
|
||||||
|
was compiled with the same version of Java and same mod version.)
|
||||||
|
|
||||||
|
Update to Forge 1.20.1-47.1.0.
|
||||||
|
|
||||||
|
Experimental support for "use item" for unrecognized items. Note that this uses
|
||||||
|
the Minecraft API's `Item.finishUsingItem(...)`.
|
||||||
|
|
||||||
|
# Version 1.22.0
|
||||||
|
|
||||||
|
Update to Forge 1.19.3-44.1.0.
|
||||||
|
|
||||||
|
# Version 1.21.4
|
||||||
|
|
||||||
|
More refactoring of check-if-in-battle lookup code.
|
||||||
|
|
||||||
|
Fix potential bug where clients cannot attack entities if their config didn't
|
||||||
|
exist client-side. They should now always be available to attack regardless of
|
||||||
|
whether or not the client has the config entry for an entity.
|
||||||
|
|
||||||
|
# Version 1.21.3
|
||||||
|
|
||||||
|
Implemented "player-only" battles, which can be enabled in the server-side
|
||||||
|
config or set using `/tbm-server-edit`. (Somewhat untested because I am only 1
|
||||||
|
person.)
|
||||||
|
|
||||||
|
# Version 1.21.2
|
||||||
|
|
||||||
|
Refactored checking-if-in-battle code from `O(n)` to `O(1)` complexity.
|
||||||
|
(In other words, utilizes the HashMap's constant time lookup of a key instead of
|
||||||
|
checking every key's id if the entity's id is the same. This speeds up the
|
||||||
|
lookup from linear to constant time.)
|
||||||
|
|
||||||
|
# Version 1.21.1
|
||||||
|
|
||||||
|
Refactored checking-if-in-battle code to be more efficient.
|
||||||
|
|
||||||
|
# Version 1.21.0
|
||||||
|
|
||||||
|
Updated mod to use forge-1.19.2-43.1.1
|
||||||
|
|
||||||
|
# Version 1.20
|
||||||
|
|
||||||
|
Implemented ignoring specific damage sources while in battle (like lava or
|
||||||
|
drowning). The damage sources can be tweaked with `/tbm-server-edit`.
|
||||||
|
|
||||||
|
Also updated entity entries in the config so that the mod logs much less when an
|
||||||
|
entity does not have specific values (which gets logged if missing).
|
||||||
|
|
||||||
|
Note that the TBM_Config.toml file has been updated. This means that your existing
|
||||||
|
TBM_Config.toml will be renamed and replaced by the newly updated config.
|
||||||
|
If you made changes to this file, you will need to apply them again to the new
|
||||||
|
updated config to keep the changes.
|
||||||
|
|
||||||
|
# Version 1.19
|
||||||
|
|
||||||
|
Updated to work with forge-1.19-41.1.0. (Somehow, this mod's version number
|
||||||
|
ended up being the same as Minecraft's version number. Don't count on this to
|
||||||
|
remain the same.)
|
||||||
|
|
||||||
|
Added Allay, Frog, Tadpole, and Warden entities to the config. (The config
|
||||||
|
version has incremented, so existing config will be replaced. Note that existing
|
||||||
|
config will not be deleted, but renamed.)
|
||||||
|
|
||||||
|
# Version 1.18.7
|
||||||
|
|
||||||
|
Incremented network channel's protocol version to 2, because a packet's format
|
||||||
|
was changed in the previous version.
|
||||||
|
|
||||||
|
# Version 1.18.6
|
||||||
|
|
||||||
|
Add server config option to disable the turn timer (recommended to not disable
|
||||||
|
the turn timer, otherwise a player could hang a battle for forever).
|
||||||
|
|
||||||
|
Fix turn timer not respecting the server's turn timer value. For example, if the
|
||||||
|
server had it set to 5 seconds, but the client had it set to 15 seconds, the
|
||||||
|
turn timer would erronously show 15 seconds at the start of the next turn.
|
||||||
|
|
||||||
|
# Version 1.18.5
|
||||||
|
|
||||||
|
Fix invalid Battle text output when a Player drinks a potion.
|
||||||
|
|
||||||
|
# Version 1.18.4
|
||||||
|
|
||||||
|
Fix attacks not hitting due to "invulnerability frames".
|
||||||
|
|
||||||
|
Change attacks to be applied approx 150ms apart.
|
||||||
|
|
||||||
|
# Version 1.18.3
|
||||||
|
|
||||||
|
[The ability to change server-side config from within the game using
|
||||||
|
"/tbm-server-edit".](https://youtu.be/9xkbHNWkcIY)
|
||||||
|
|
||||||
|
Fix Battle not checking Player "speed/slow" status to apply the
|
||||||
|
"player\_haste\_speed" and "player\_slow\_speed" settings.
|
||||||
|
|
||||||
|
# Version 1.18.2
|
||||||
|
|
||||||
|
The list of targets in the Battle GUI when selecting a target did not display
|
||||||
|
Players' names in their team color. This version now shows Player names with
|
||||||
|
their team color in the target buttons/list.
|
||||||
|
|
||||||
|
# Version 1.18.1
|
||||||
|
|
||||||
|
Fix battle text output such that players in teams will have their name displayed
|
||||||
|
with the team's color (and some refactoring of related battle text output).
|
||||||
|
|
||||||
|
# Version 1.18.0
|
||||||
|
|
||||||
|
Mod now works with Forge-1.18.2-40.1.0 .
|
||||||
|
Note that the mod's version is confusingly similar (1.18.0).
|
||||||
|
|
||||||
|
TBM should allow players to eat any food from any mod (including food items from Pam's HarvestCraft).
|
||||||
|
|
||||||
|
# Version 1.17.2.6
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Implemented getting EntityInfo for CustomNPCs that have the same name as a
|
||||||
|
"custom entry" in the "server\_config.entity" array in the config.
|
||||||
|
|
||||||
|
# Version 1.17.2.5
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Refactored OtherModHandling.java to be more efficient when handling CustomNPCs
|
||||||
|
DamagedEvent.
|
||||||
|
|
||||||
|
# Version 1.17.2.4
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Fix usage of NpcAPI.
|
||||||
|
|
||||||
|
# Version 1.17.2.3
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Fix potential unhandled exception crash bug related to handling CustomNPCs
|
||||||
|
Player hurt events.
|
||||||
|
|
||||||
|
# Version 1.17.2.2
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Fix potential NullPointerException crash bug.
|
||||||
|
|
||||||
|
# Version 1.17.2.1
|
||||||
|
|
||||||
|
(Branched from 1.17.2)
|
||||||
|
|
||||||
|
Attempt to fix CustomNPCs mods not damaging players in TurnBased combat.
|
||||||
|
|
||||||
|
# Version 1.17.2
|
||||||
|
|
||||||
|
(try to) Fix potential freeze bug when an entity leaves battle.
|
||||||
|
|
||||||
|
# Version 1.17.1
|
||||||
|
|
||||||
|
Add experimental support for Pam's Harvestcraft foods.
|
||||||
|
|
||||||
|
# Version 1.17
|
||||||
|
|
||||||
|
Update mod for Forge 1.16.5-36.1.0 .
|
||||||
|
|
||||||
|
# Version 1.16
|
||||||
|
|
||||||
|
Add config options regarding creeper behavior.
|
||||||
|
|
||||||
|
By default, creepers will not explode if they leave battle (maybe due to a
|
||||||
|
player fleeing), until the cooldown time ends.
|
||||||
|
|
||||||
|
By default, creepers that explode will damage anyone, even if they weren't in
|
||||||
|
turn-based battle.
|
||||||
|
|
||||||
|
# Version 1.15
|
||||||
|
|
||||||
|
Add server-side config option that determines on what turn a Creeper will
|
||||||
|
explode in battle.
|
||||||
|
|
||||||
|
# Version 1.14
|
||||||
|
|
||||||
|
Implemented support for Creepers in battle.
|
||||||
|
|
||||||
|
Added an option to the config to prevent config from being overwritten on
|
||||||
|
update.
|
||||||
|
|
||||||
|
Fixed some display text during battle.
|
||||||
|
|
||||||
|
# Version 1.13
|
||||||
|
|
||||||
|
Disabled midi playback due to currently being unable to change its volume.
|
||||||
|
|
||||||
|
Note mp3 playback breaks sometimes. Using an mp3 file without album art seems to
|
||||||
|
work though...
|
||||||
|
|
||||||
|
# Version 1.12
|
||||||
|
|
||||||
|
Fix potential crash if mod is loaded on dedicated server.
|
||||||
|
|
||||||
|
# Version 1.11
|
||||||
|
|
||||||
|
Fixed text display in BattleGUI.
|
||||||
|
|
||||||
|
Updated TBM\_Config.toml with new vanilla mobs.
|
||||||
|
|
||||||
|
Fixed version parsing of TBM\_Config.toml.
|
||||||
|
|
||||||
|
# Version 1.10
|
||||||
|
|
||||||
|
Updated for 1.16.3
|
||||||
|
|
||||||
|
Compiled against forge version "1.16.3-34.1.0"
|
||||||
|
|
||||||
|
However, MP3 playing seems to not work sometimes.
|
||||||
|
|
||||||
|
# Version 1.9
|
||||||
|
|
||||||
|
Updated mod for 1.14.4
|
||||||
|
(Took a long while, and no new features were added due to making sure everything
|
||||||
|
still works).
|
||||||
|
|
||||||
|
Compiled against forge version "1.14.4-28.1.0"
|
||||||
|
|
||||||
|
Entity names have changed in the config, so this newer version will replace the
|
||||||
|
old version. Older existing config should be renamed rather than deleted.
|
||||||
|
|
||||||
|
# Version 1.8
|
||||||
|
|
||||||
|
Update to forge version "1.12.2-14.23.5.2768".
|
||||||
|
|
||||||
|
Fix bug where more than config-set-amount of entities can be in battle.
|
||||||
|
|
||||||
|
Add mp3 support, can now play mp3s.
|
||||||
|
|
||||||
|
Minor improvements.
|
||||||
|
|
||||||
|
# Version 1.7
|
||||||
|
|
||||||
|
Fix bug where after using "/tbm-edit", ignore_battle option is saved in
|
||||||
|
config with the wrong name.
|
||||||
|
|
||||||
|
Add "/tbm-edit custom", which lets an OP add an entity entry for entities with
|
||||||
|
custom names (via name-tags). The entry added into the config file will use
|
||||||
|
"custom_name" instead of "name" to specify if it is a regular entity entry
|
||||||
|
or an entry for a specific custom name.
|
||||||
|
|
||||||
|
Minor fixes and improvements.
|
||||||
|
|
||||||
|
# Version 1.6
|
||||||
|
|
||||||
|
Fix bug where player can start battle with self.
|
||||||
|
|
||||||
|
Change config to use ".toml" instead of ".xml".
|
||||||
|
|
||||||
|
Added command "/tbm-edit" that allows OPs to edit entity entries for TBM.
|
||||||
|
Can be used to add mobs from other mods easily.
|
||||||
|
|
||||||
|
Change how battle info text is displayed.
|
||||||
|
|
||||||
|
# Version 1.5
|
||||||
|
|
||||||
|
Fix proper consumption of food/potion items in battle.
|
||||||
|
|
||||||
|
Added some debug output on internal freeze occurrence (investigation of the
|
||||||
|
freeze bug is still ongoing).
|
||||||
|
|
||||||
|
# Version 1.4
|
||||||
|
|
||||||
|
Fix duplicate "... entered battle" messages.
|
||||||
|
|
||||||
|
Added max-distance config option for how close a monster must be to initiate
|
||||||
|
battle (when triggered by a monster targeting a player or entity in battle).
|
||||||
|
|
||||||
|
Some internal fixes and refactorings.
|
||||||
|
|
||||||
|
# Version 1.3
|
||||||
|
|
||||||
|
Added a battle-cooldown and related config option. Now, when leaving battle, a
|
||||||
|
cooldown timer (default 5 seconds) prevents entities from
|
||||||
|
attacking/being-attacked for the duration of the cooldown. Can be set to a
|
||||||
|
minimum of 1 second and maximum of 10 seconds.
|
||||||
|
|
||||||
|
"/tbm-enable-all" and "/tbm-disable-all" now notifies all players when they are
|
||||||
|
invoked by an OP.
|
||||||
|
|
||||||
|
Battles can now be started/joined by hostile mobs when they target a player or
|
||||||
|
other entity in battle, instead of just entering on attack. Old
|
||||||
|
battle-starting-behavior can be used by setting the related config option.
|
||||||
|
(This change was made to keep zombies from gathering around the player when
|
||||||
|
the config option for freezing entities in battle is enabled.)
|
||||||
|
|
||||||
|
Non-player entities in battle now primarily attack entities they are already
|
||||||
|
targeting (via a call to EntityLiving's "getAttackTarget()").
|
||||||
|
|
||||||
|
Note since config version is now 5, older config will be renamed and the new
|
||||||
|
config will take its place.
|
||||||
|
|
||||||
|
# Version 1.2
|
||||||
|
|
||||||
|
Fixed "/tbm-enable" and "/tbm-disable" not working in singleplayer.
|
||||||
|
|
||||||
|
Added commands:
|
||||||
|
- "/tbm-enable-all"
|
||||||
|
- "/tbm-disable-all"
|
||||||
|
|
||||||
|
Only OPs can use these new commands to enable or disable turn-based-battle for
|
||||||
|
everyone.
|
||||||
|
Note that if "/tbm-disable-all" is invoked, joining players will also have
|
||||||
|
turn-based-battle disabled for them. Invoking "/tbm-enable-all" will change this
|
||||||
|
back to the default, where turn-based-battle is enabled for joining players.
|
||||||
|
|
||||||
|
# Version 1.1
|
||||||
|
|
||||||
|
Added commands to enable/disable turn-based-battle on-demand.
|
||||||
|
|
||||||
|
Commands are:
|
||||||
|
- "/tbm-enable"
|
||||||
|
- "/tbm-disable"
|
||||||
|
- "/tbm-set \<player> \<true/false>"
|
||||||
|
|
||||||
|
There is a config option to allow anyone to use "/tbm-enable" and "/tbm-disable".
|
||||||
|
Only OPs can use "/tbm-set" (permission level 2).
|
||||||
|
|
||||||
|
Note that since config version has been updated, pre-existing config will be
|
||||||
|
renamed and the newer config will take its place.
|
||||||
|
|
||||||
|
# Version 1.0
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Turn based combat with hostile mobs (excluding passive and bosses) by default
|
||||||
|
- Config generated at ".minecraft/config/TurnBasedMinecraft/TBM_Config.xml
|
||||||
|
- Old config is renamed if new config exists with newer mod version
|
||||||
|
- Battle is very configurable per mob and also for all players in config
|
||||||
|
- Can add mobs unique to other mods in config (using full Java Class name of mob)
|
||||||
|
- Can set config on server/singleplayer to freeze mobs in combat
|
||||||
|
- Can add battle/silly music in ".minecraft/config/TurnBasedMinecraft/Music"
|
||||||
|
that activates depending on mob types in battle
|
||||||
|
- What determines battle or silly can be set client-side in config
|
||||||
|
- Unknown types defaults to battle music instead of silly music
|
||||||
|
- Can set max battle combatants in config
|
||||||
|
- Can use bow/arrows in battle (currently different projectile weapons provided
|
||||||
|
by different mods are not supported)
|
||||||
|
- Players in creative-mode will not enter turn-based battle
|
97
FAQ.md
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
## How do I use this mod to have turn-based-battle with any mob (including mobs from other mods)?
|
||||||
|
|
||||||
|
To have turn-based-battle with a mob, it must have a config entry in the server
|
||||||
|
config. This can either be done manually or be [done in-game via a
|
||||||
|
command](https://www.youtube.com/watch?v=MK648OVHddE).
|
||||||
|
|
||||||
|
## Is it possible to have a mob's config applied to any mob with a specific name?
|
||||||
|
|
||||||
|
Yes, [this video explains this feature](https://www.youtube.com/watch?v=9lBETQFMd3A).
|
||||||
|
|
||||||
|
## Can the mod play music while a battle is happening?
|
||||||
|
|
||||||
|
Yes, you have to put the music (`.wav`, `.ogg`, or `.mp3`) in
|
||||||
|
`.minecraft/config/TurnBasedMinecraft/Music/battle` and
|
||||||
|
`.minecraft/config/TurnBasedMinecraft/Music/silly` . Note that `.wav`, `.ogg`,
|
||||||
|
and `.mp3` music files are supported, but `.mid` files are disabled due to lack
|
||||||
|
of volume control with the default Java library api. The config file can be
|
||||||
|
edited to change what categories of entities trigger what type of music, but
|
||||||
|
generally "passive" mobs trigger the "silly" music and everything else triggers
|
||||||
|
"battle" music. Note that the default server config has turn-based-battle
|
||||||
|
disabled for "passive" mobs.
|
||||||
|
|
||||||
|
**Note that while .ogg Vorbis files are supported, .ogg Opus files are NOT
|
||||||
|
supported.**
|
||||||
|
|
||||||
|
**It is recommended to use .ogg Vorbis files instead of .mp3 files.**
|
||||||
|
|
||||||
|
One can use FFmpeg to convert music files into .ogg Vorbis:
|
||||||
|
|
||||||
|
ffmpeg -i <music_file_to_convert> -map a:0 -c:a libvorbis output.ogg
|
||||||
|
|
||||||
|
## Why can't the mod play my mp3 files?
|
||||||
|
|
||||||
|
The third-party-library used to load mp3 files seems to have issues with
|
||||||
|
loading any mp3 file that isn't "barebones". Try removing the metadata of the
|
||||||
|
mp3 file. I've found that using mp3s without album art embedded in it seems to
|
||||||
|
work.
|
||||||
|
|
||||||
|
**It is recommended to use .ogg Vorbis files instead of .mp3 files.**
|
||||||
|
|
||||||
|
## How do I configure battle music?
|
||||||
|
|
||||||
|
There is a way to edit client-config that deals with music settings, like what
|
||||||
|
mob groups trigger battle/silly music and volume. This menu can be opened in two
|
||||||
|
ways.
|
||||||
|
|
||||||
|
- Run the command `/tbm-client-edit` to open the menu.
|
||||||
|
- Open the Mod list from the options screen, click on TBMM on the left column
|
||||||
|
and click on "Config". (Note that this way of opening the client-config-gui
|
||||||
|
is unavailable on Forge, and is only available on NeoForge.)
|
||||||
|
|
||||||
|
Note that "Accept" must be clicked on to save the client-config.
|
||||||
|
|
||||||
|
## Why do passive mobs don't start turn-based battle?
|
||||||
|
|
||||||
|
By default, the `passive` category is set to "ignore turn-based-battle" in the
|
||||||
|
server config. Use `/tbm-server-edit` to change this. (Click on
|
||||||
|
`ignore_battle_types` which should be dark-green. A list of "categories" will
|
||||||
|
appear at the bottom of the text. Click on `passive` to remove the "passive"
|
||||||
|
category.)
|
||||||
|
![Screenshot one of two showing how to unset passive category from ignore battle types list](https://seodisparate.com/static/uploads/TBMM_ignore_battle_types_screenshot0.png)
|
||||||
|
![Screenshot two of two showing how to unset passive category from ignore battle types list](https://seodisparate.com/static/uploads/TBMM_ignore_battle_types_screenshot1.png)
|
||||||
|
|
||||||
|
Alternatively, edit the [server config](https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/ad78063a16c768f660dd086cba857a3be43a84b2/src/main/resources/assets/com_burnedkirby_turnbasedminecraft/TBM_Config.toml#L46)
|
||||||
|
and remove "passive" from the ignore\_battle\_types list.
|
||||||
|
|
||||||
|
## Why is the mod's config file missing?
|
||||||
|
|
||||||
|
The mod needs to be run once to generate the default config file and
|
||||||
|
directories for battle music. After running it once, you can now close
|
||||||
|
Minecraft and edit the config found at
|
||||||
|
`.minecraft/config/TurnBasedMinecraft/TBM_Config.toml` Note that some options
|
||||||
|
only apply to the server and some only to the client, as specified in the
|
||||||
|
config. This means that server config must be changed on the server side for it
|
||||||
|
to take effect (local singleplayer will use all of the local config, but
|
||||||
|
multiplayer setups will require the server config to be changed on the server
|
||||||
|
side). [You can edit the server-side config in game via the "/tbm-server-edit"
|
||||||
|
command](https://youtu.be/9xkbHNWkcIY).
|
||||||
|
|
||||||
|
## I updated the mod, but now my config changes are back to default, what happened?
|
||||||
|
|
||||||
|
*As of Version 1.26.5 and onwards, this should happen less often!*
|
||||||
|
Version 1.26.5 introduces more robust config updating such that entries that
|
||||||
|
exist in the default config, but not in the current config will be appended to
|
||||||
|
the current config.
|
||||||
|
|
||||||
|
Sometimes, I add new mob entries to the config, and increment the version
|
||||||
|
number of the config. When the server/client starts, it checks the default
|
||||||
|
config's version number with the existing config's version number. If the
|
||||||
|
existing config is determined to be outdated, then it is renamed to a different
|
||||||
|
name (which usually includes the date/time of when it was renamed), and the new
|
||||||
|
default config is placed in its place. There is a config option to prevent this
|
||||||
|
from happening, but it is strongly recommended to not disable this since this
|
||||||
|
will cause updates to the config to never be placed in the mod's config
|
||||||
|
directory. If you have changes you want to keep, but the mod renamed the
|
||||||
|
original config, you will have to edit the `TBM_Config.toml` to have the
|
||||||
|
changes you want from the renamed older config file.
|
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018-2024 Stephen Seo
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
107
README.md
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# TurnBasedMinecraftMod
|
||||||
|
|
||||||
|
This mod puts turn-based-combat of RPGs into Minecraft!
|
||||||
|
|
||||||
|
# Links/Downloads
|
||||||
|
|
||||||
|
Precompiled jars are available here:
|
||||||
|
https://seodisparate.com/static/tbm_releases/
|
||||||
|
https://burnedkirby.com/tbmm_downloads/
|
||||||
|
https://www.curseforge.com/minecraft/mc-mods/turnbasedminecraft/files
|
||||||
|
https://modrinth.com/mod/turnbasedmc
|
||||||
|
https://git.seodisparate.com/stephenseo/TurnBasedMinecraftMod/releases
|
||||||
|
|
||||||
|
# Documentation Page
|
||||||
|
|
||||||
|
https://stephen-seo.github.io/TurnBasedMinecraftMod/
|
||||||
|
|
||||||
|
# Forge or NeoForge
|
||||||
|
|
||||||
|
The `forge` branch tracks the version of the mod for Minecraft Forge.
|
||||||
|
|
||||||
|
The `neoforge` branch tracks the version of the mod for Minecraft NeoForge.
|
||||||
|
|
||||||
|
# What changed in what version
|
||||||
|
|
||||||
|
See the [Changelog](https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md)
|
||||||
|
|
||||||
|
# Things you may need to know about this mod
|
||||||
|
|
||||||
|
On first run, this mod will create a config file and some directories in your
|
||||||
|
Minecraft directory. They will typically be located at
|
||||||
|
`.minecraft/config/TurnBasedMinecraft`. (for the server they will be in the
|
||||||
|
`config` directory in the server directory.)
|
||||||
|
|
||||||
|
The config file `.minecraft/config/TurnBasedMinecraft/TBM_Config.toml` is commented
|
||||||
|
with info on what each option does. ~~It will also be moved if a newer version
|
||||||
|
of this mod has a newer version of the config file (usually renamed with a
|
||||||
|
timestamp).~~ ~~I will try my best to not move the previous version config, but rather
|
||||||
|
edit the previous version config to have new options.~~ ~~When a new config version is made,
|
||||||
|
usually because a new entry has been added, the existing config is renamed to a file with
|
||||||
|
a timestamp in the filename of when it was replaced. One can set a config option in the
|
||||||
|
config to prevent it being overwritten if necessary.~~
|
||||||
|
|
||||||
|
*As of version 1.26.5 of this mod, this should happen less frequently!*
|
||||||
|
Version 1.26.5 introduces changes that allow entries that exist in the default
|
||||||
|
config but not in the current config to be appended in the current config.
|
||||||
|
|
||||||
|
Some options in the config file only affect the Server, and ~~some only affect the Client~~.
|
||||||
|
Client config has been moved to a
|
||||||
|
[separate system provided by NeoForge](https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/FAQ.md#how-do-i-configure-battle-music).
|
||||||
|
When playing multiplayer, some configuration of the config on the server may be needed.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- Combat between players and mobs or other players will invoke a turn based battle
|
||||||
|
between them
|
||||||
|
- Supports use of the vanilla Minecraft bow and arrows (have bow selected when
|
||||||
|
in battle)
|
||||||
|
- Supports custom battle music to be played when fighting enemies. (They must
|
||||||
|
be placed in `.minecraft/config/TurnBasedMinecraft/Music/battle` or
|
||||||
|
`.minecraft/config/TurnBasedMinecraft/Music/silly`. Client-side config
|
||||||
|
determines which song plays in battle for the client. only `.wav`,
|
||||||
|
~~`.mid`~~, `.mp3`, and `.ogg` files supported. ~~Only `.mid` files are not
|
||||||
|
affected by volume options (master and music sliders))~~ Midi file playback
|
||||||
|
has been disabled for now due to lack of volume control issues. MP3 file
|
||||||
|
playback sometimes fails, but seems to work better when the file is as
|
||||||
|
"barebones" as possible (no album art metadata in the file).
|
||||||
|
- It is recommended to use `.ogg` files for music.
|
||||||
|
- Note that ogg Vorbis is supported, and NOT ogg Opus.
|
||||||
|
- One can convert to ogg Vorbis with ffmpeg like this: `ffmpeg -i
|
||||||
|
<my_music_file_to_convert> -map a:0 -c:a libvorbis output.ogg`.
|
||||||
|
- Config allows limiting number of combatants in turn-based battle.
|
||||||
|
- Config can be modified (server-side) to add entries of mobs from other mods.
|
||||||
|
(by default an unknown mob cannot enter turn-based battle, so the config must be
|
||||||
|
configured for them.)
|
||||||
|
- [Alternatively, the command "/tbm-edit" can be used in-game to add/edit
|
||||||
|
entities for the mod.](https://www.youtube.com/watch?v=MK648OVHddE)
|
||||||
|
- [Also, one can make entries for specific custom names](https://youtu.be/9lBETQFMd3A)
|
||||||
|
- [Server-side config can be edited in-game with the "/tbm-server-edit" command](https://youtu.be/9xkbHNWkcIY)
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
Simply invoke `./gradlew build` in the mod directory and after some time the
|
||||||
|
finished jar will be saved at
|
||||||
|
`build/libs/TurnBasedMinecraft-NeoForge-1.26.5-all.jar`
|
||||||
|
|
||||||
|
# Reproducibility
|
||||||
|
|
||||||
|
This mod should support reproducible builds. See `Reproducibility.md` to see
|
||||||
|
more details.
|
||||||
|
|
||||||
|
# Other notes
|
||||||
|
|
||||||
|
This mod uses [j-ogg-vorbis](https://github.com/stephengold/j-ogg-all)
|
||||||
|
available from [http://www.j-ogg.de](http://www.j-ogg.de) and copyrighted by
|
||||||
|
Tor-Einar Jarnbjo.
|
||||||
|
|
||||||
|
This mod also uses [JavaMP3](https://github.com/kevinstadler/JavaMP3)
|
||||||
|
which is licensed under the [MIT License](https://github.com/kevinstadler/JavaMP3/blob/master/LICENSE).
|
||||||
|
|
||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
[See the FAQ page.](https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/FAQ.md)
|
||||||
|
|
||||||
|
# Related Videos
|
||||||
|
|
||||||
|
[See related videos here](https://burnedkirby.com/posts/tbmm/)
|
304
Reproducibility.md
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
# Reproducibility
|
||||||
|
|
||||||
|
Starting with version 1.24.0 of this mod, this file will list what version of
|
||||||
|
Java was used to compile the jars. In theory, using the same version of Java
|
||||||
|
should result in an identical jar due to reproducible builds.
|
||||||
|
|
||||||
|
## NeoForge 1.26.5
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.5-all.jar
|
||||||
|
b02d0abf6f2fbc5c3b718b548309efacb159ec8f86c7d2d653fc0b73234e761a build/libs/TurnBasedMinecraft-NeoForge-1.26.5-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.5-MC-1.21.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.5-MC-1.21.1-all.jar
|
||||||
|
c529ebe3dd48608afd27e3393b201036ce84d3be0a850cdf48039fbc4820629e build/libs/TurnBasedMinecraft-NeoForge-1.26.5-MC-1.21.1-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.5
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.5-all.jar
|
||||||
|
c8ed6e2e9a433c40901d41ec604bc6260fc5b231f5d3859832ecbe76b0f5a9e2 build/libs/TurnBasedMinecraft-Forge-1.26.5-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.4
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.4-all.jar
|
||||||
|
ddab3e58638ba70c7b10f84f4aa7ac81e8e5a63cb47d0ebf7e7aa4bcf3c0a1ba build/libs/TurnBasedMinecraft-NeoForge-1.26.4-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.4-MC-1.21.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.4-MC-1.21.1-all.jar
|
||||||
|
e49665c67452cae8fab8f356d187b860893885afbe6dab1e3a869331a12f1cf5 build/libs/TurnBasedMinecraft-NeoForge-1.26.4-MC-1.21.1-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.4
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.4-all.jar
|
||||||
|
2052b1e8f6a49374b6a9bbc0c0547c1972d5454ea9afa5f0455c534285d6cada build/libs/TurnBasedMinecraft-Forge-1.26.4-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.3
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.3-all.jar
|
||||||
|
2c8f17499a475f22493244e16f499bed46ea6a32a20f6bd2be5b3151464b2225 build/libs/TurnBasedMinecraft-NeoForge-1.26.3-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.3-MC-1.21.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.3-MC-1.21.1-all.jar
|
||||||
|
311018353109da4d9a49379d9ebc29dbac7e2aef3331ec177bd0edc300d15b89 /home/public/TurnBasedMC/TurnBasedMinecraft-NeoForge-1.26.3-MC-1.21.1-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.3
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.3-all.jar
|
||||||
|
5fdaffd14f75c2340a410c37811a5f7644ade3c6852db4b982bf3161bab1aae7 build/libs/TurnBasedMinecraft-Forge-1.26.3-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.2
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.2-all.jar
|
||||||
|
d55f516a2166d266c0d60e881b170cb734372ac01c8a25cf12e2f593f7b87004 build/libs/TurnBasedMinecraft-NeoForge-1.26.2-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.2
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.2-all.jar
|
||||||
|
d06f3cc8e050aa4086dce187ffce2cc5049c67c401a0cd4608138880b0868e89 build/libs/TurnBasedMinecraft-Forge-1.26.2-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.1-all.jar
|
||||||
|
0fc0f1ea49c726b06b7a353fee4e59eaadd608a4074245477d1ccd957467305c build/libs/TurnBasedMinecraft-Forge-1.26.1-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.5 2024-10-15
|
||||||
|
OpenJDK Runtime Environment (build 21.0.5+11)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.5+11, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.5
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.1-all.jar
|
||||||
|
ac3005191d9c23ad823e4ae33b750a0ac17518460fedc91242d031a8f1365101 build/libs/TurnBasedMinecraft-NeoForge-1.26.1-all.jar
|
||||||
|
|
||||||
|
## Forge 1.26.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.4 2024-07-16
|
||||||
|
OpenJDK Runtime Environment (build 21.0.4+7)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.4+7, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.4
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.26.0-all.jar
|
||||||
|
9a3bb24fef9348e620ab5bcc120d83e11c5289a046561fb4ef91d0ccade9b271 build/libs/TurnBasedMinecraft-Forge-1.26.0-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.26.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 21.0.4 2024-07-16
|
||||||
|
OpenJDK Runtime Environment (build 21.0.4+7)
|
||||||
|
OpenJDK 64-Bit Server VM (build 21.0.4+7, mixed mode, sharing)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 21.0.4
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.26.0-all.jar
|
||||||
|
a13e93df640eb3ce5577521421e760aa5d808d34d5cef9c5415ae7a699173ea9 build/libs/TurnBasedMinecraft-NeoForge-1.26.0-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.25.2
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.10 2024-01-16
|
||||||
|
OpenJDK Runtime Environment (build 17.0.10+7)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.10+7, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.10
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.25.2-all.jar
|
||||||
|
c59533059eb322a616f38ab40ccbc7d4d6c1667a651328a4c6eb187fe16d7a6f build/libs/TurnBasedMinecraft-NeoForge-1.25.2-all.jar
|
||||||
|
|
||||||
|
## Forge 1.25.2
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.10 2024-01-16
|
||||||
|
OpenJDK Runtime Environment (build 17.0.10+7)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.10+7, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.10
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.25.2-all.jar
|
||||||
|
00bc7958431e161b0a512ce32b41c1a97516b00e109195294ee18d4abf58dc26 build/libs/TurnBasedMinecraft-Forge-1.25.2-all.jar
|
||||||
|
|
||||||
|
## Forge 1.25.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.25.1-all.jar
|
||||||
|
33711947eed8b24fa7fd65d36ecdb6ed78e144af8d7fff6e1bfa304cfe4a1d3d build/libs/TurnBasedMinecraft-Forge-1.25.1-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.25.1
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.25.1-all.jar
|
||||||
|
b0ec086c356c4d3662dcea4bdd9aeb2b0786e4ef40e061599e81b529746e01ea build/libs/TurnBasedMinecraft-NeoForge-1.25.1-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.25.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.25.0-all.jar
|
||||||
|
0e5eacc8aefd3b1a1c8e6c9657108172934fae2e727547ca7c12f9ff79ce4e8e build/libs/TurnBasedMinecraft-NeoForge-1.25.0-all.jar
|
||||||
|
|
||||||
|
## Forge 1.25.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.25.0-all.jar
|
||||||
|
51ef854552b180df68969f4cec6fdc8716ef519b947948b9e5f4ce9953d00162 build/libs/TurnBasedMinecraft-Forge-1.25.0-all.jar
|
||||||
|
|
||||||
|
## NeoForge 1.24.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-NeoForge-1.24.0-all.jar
|
||||||
|
584935b6e928ad141a55e4d1a21944cebff5152396782085d145bbe34c29286c build/libs/TurnBasedMinecraft-NeoForge-1.24.0-all.jar
|
||||||
|
|
||||||
|
## Forge 1.24.0
|
||||||
|
|
||||||
|
$ java --version
|
||||||
|
openjdk 17.0.9 2023-10-17
|
||||||
|
OpenJDK Runtime Environment (build 17.0.9+8)
|
||||||
|
OpenJDK 64-Bit Server VM (build 17.0.9+8, mixed mode)
|
||||||
|
|
||||||
|
$ javac --version
|
||||||
|
javac 17.0.9
|
||||||
|
|
||||||
|
$ sha256sum build/libs/TurnBasedMinecraft-Forge-1.24.0.jar
|
||||||
|
e17c370cdf347b053c7f55091afed77564dcd8f419615bd6ca87babe10329c07 build/libs/TurnBasedMinecraft-Forge-1.24.0.jar
|
225
build.gradle
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-library'
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'net.neoforged.moddev' version '1.0.21'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('wrapper', Wrapper).configure {
|
||||||
|
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
|
||||||
|
// Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with
|
||||||
|
// documentation attached on cursor hover of gradle classes and methods. However, this comes with increased
|
||||||
|
// file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards.
|
||||||
|
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
|
||||||
|
distributionType = Wrapper.DistributionType.BIN
|
||||||
|
}
|
||||||
|
|
||||||
|
version = mod_version
|
||||||
|
group = mod_group_id
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
|
||||||
|
flatDir {
|
||||||
|
dir 'libs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base {
|
||||||
|
archivesName = "TurnBasedMinecraft-NeoForge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mojang ships Java 21 to end users starting in 1.20.5, so mods should target Java 21.
|
||||||
|
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
|
||||||
|
|
||||||
|
neoForge {
|
||||||
|
// Specify the version of NeoForge to use.
|
||||||
|
version = project.neo_version
|
||||||
|
|
||||||
|
parchment {
|
||||||
|
mappingsVersion = project.parchment_mappings_version
|
||||||
|
minecraftVersion = project.parchment_minecraft_version
|
||||||
|
}
|
||||||
|
|
||||||
|
// This line is optional. Access Transformers are automatically detected
|
||||||
|
// accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg')
|
||||||
|
|
||||||
|
// Default run configurations.
|
||||||
|
// These can be tweaked, removed, or duplicated as needed.
|
||||||
|
runs {
|
||||||
|
client {
|
||||||
|
client()
|
||||||
|
|
||||||
|
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||||
|
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server()
|
||||||
|
programArgument '--nogui'
|
||||||
|
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
||||||
|
// By default, the server will crash when no gametests are provided.
|
||||||
|
// The gametest system is also enabled by default for other run configs under the /test command.
|
||||||
|
gameTestServer {
|
||||||
|
type = "gameTestServer"
|
||||||
|
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
data {
|
||||||
|
data()
|
||||||
|
|
||||||
|
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
||||||
|
// gameDirectory = project.file('run-data')
|
||||||
|
|
||||||
|
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||||
|
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
// applies to all the run configs above
|
||||||
|
configureEach {
|
||||||
|
// Recommended logging data for a userdev environment
|
||||||
|
// The markers can be added/remove as needed separated by commas.
|
||||||
|
// "SCAN": For mods scan.
|
||||||
|
// "REGISTRIES": For firing of registry events.
|
||||||
|
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||||
|
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
||||||
|
|
||||||
|
// Recommended logging level for the console
|
||||||
|
// You can set various levels here.
|
||||||
|
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||||
|
logLevel = org.slf4j.event.Level.DEBUG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mods {
|
||||||
|
// define mod <-> source bindings
|
||||||
|
// these are used to tell the game which sources are for which mod
|
||||||
|
// mostly optional in a single mod project
|
||||||
|
// but multi mod projects should define one per mod
|
||||||
|
"${mod_id}" {
|
||||||
|
sourceSet(sourceSets.main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include resources generated by data generators.
|
||||||
|
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||||
|
|
||||||
|
// Sets up a dependency configuration called 'localRuntime'.
|
||||||
|
// This configuration should be used instead of 'runtimeOnly' to declare
|
||||||
|
// a dependency that will be present for runtime testing but that is
|
||||||
|
// "optional", meaning it will not be pulled by dependents of this mod.
|
||||||
|
configurations {
|
||||||
|
runtimeClasspath.extendsFrom localRuntime
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Example optional mod dependency with JEI
|
||||||
|
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
||||||
|
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
|
||||||
|
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
|
||||||
|
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
|
||||||
|
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
|
||||||
|
|
||||||
|
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
||||||
|
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
||||||
|
// The group id is ignored when searching -- in this case, it is "blank"
|
||||||
|
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
|
||||||
|
|
||||||
|
// Example mod dependency using a file as dependency
|
||||||
|
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
|
||||||
|
|
||||||
|
// Example project dependency using a sister or child project:
|
||||||
|
// implementation project(":myproject")
|
||||||
|
|
||||||
|
// For more info:
|
||||||
|
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||||
|
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||||
|
|
||||||
|
|
||||||
|
// implementation "net.neoforged:neoforge:${neo_version}"
|
||||||
|
|
||||||
|
// implementation files('libs/javamp3-1.0.3.jar')
|
||||||
|
implementation 'fr.delthas:javamp3:1.0.3'
|
||||||
|
|
||||||
|
implementation 'com.github.stephengold:j-ogg-vorbis:1.0.4'
|
||||||
|
|
||||||
|
jarJar(implementation("fr.delthas:javamp3")) {
|
||||||
|
version {
|
||||||
|
strictly '[1.0.0,2.0.0)'
|
||||||
|
prefer '1.0.3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jarJar(implementation("com.github.stephengold:j-ogg-vorbis")) {
|
||||||
|
version {
|
||||||
|
strictly '[1.0.4,2.0.0)'
|
||||||
|
prefer '1.0.4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This block of code expands all declared replace properties in the specified resource targets.
|
||||||
|
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
|
||||||
|
var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) {
|
||||||
|
var replaceProperties = [
|
||||||
|
minecraft_version : minecraft_version,
|
||||||
|
minecraft_version_range: minecraft_version_range,
|
||||||
|
neo_version : neo_version,
|
||||||
|
neo_version_range : neo_version_range,
|
||||||
|
loader_version_range : loader_version_range,
|
||||||
|
mod_id : mod_id,
|
||||||
|
mod_name : mod_name,
|
||||||
|
mod_license : mod_license,
|
||||||
|
mod_version : mod_version,
|
||||||
|
mod_authors : mod_authors,
|
||||||
|
mod_description : mod_description
|
||||||
|
]
|
||||||
|
inputs.properties replaceProperties
|
||||||
|
expand replaceProperties
|
||||||
|
from "src/main/templates"
|
||||||
|
into "build/generated/sources/modMetadata"
|
||||||
|
}
|
||||||
|
// Include the output of "generateModMetadata" as an input directory for the build
|
||||||
|
// this works with both building through Gradle and the IDE.
|
||||||
|
sourceSets.main.resources.srcDir generateModMetadata
|
||||||
|
// To avoid having to run "generateModMetadata" manually, make it run on every project reload
|
||||||
|
neoForge.ideSyncTask generateModMetadata
|
||||||
|
|
||||||
|
// Example for how to get properties into the manifest for reading by the runtime..
|
||||||
|
jar {
|
||||||
|
archiveClassifier = 'all'
|
||||||
|
manifest {
|
||||||
|
attributes([
|
||||||
|
"Specification-Title": "TurnBasedMinecraftMod",
|
||||||
|
"Specification-Vendor": "TurnBasedMinecraftMod_BK",
|
||||||
|
"Specification-Version": "1", // We are version 1 of ourselves
|
||||||
|
"Implementation-Title": "TurnBasedMinecraftMod",
|
||||||
|
"Implementation-Version": "${version}",
|
||||||
|
"Implementation-Vendor" :"TurnBasedMinecraftMod_BK",
|
||||||
|
// Do not place timestamp for the sake of reproducible builds
|
||||||
|
// "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reproducible Builds
|
||||||
|
tasks.withType(AbstractArchiveTask).configureEach {
|
||||||
|
preserveFileTimestamps = false
|
||||||
|
reproducibleFileOrder = true
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadSources = true
|
||||||
|
downloadJavadoc = true
|
||||||
|
}
|
||||||
|
}
|
48
gradle.properties
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||||
|
org.gradle.jvmargs=-Xmx1G
|
||||||
|
org.gradle.daemon=false
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
|
||||||
|
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
|
||||||
|
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
|
||||||
|
parchment_minecraft_version=1.21
|
||||||
|
parchment_mappings_version=2024.07.28
|
||||||
|
|
||||||
|
# Environment Properties
|
||||||
|
# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge
|
||||||
|
# The Minecraft version must agree with the Neo version to get a valid artifact
|
||||||
|
minecraft_version=1.21.3
|
||||||
|
|
||||||
|
# The Minecraft version range can use any release version of Minecraft as bounds.
|
||||||
|
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
|
||||||
|
# as they do not follow standard versioning conventions.
|
||||||
|
minecraft_version_range=[1.21.3, 1.22)
|
||||||
|
|
||||||
|
# The Neo version must agree with the Minecraft version to get a valid artifact
|
||||||
|
neo_version=21.3.11-beta
|
||||||
|
# The Neo version range can use any version of Neo as bounds
|
||||||
|
neo_version_range=[21.3.0,)
|
||||||
|
# The loader version range can only use the major version of FML as bounds
|
||||||
|
loader_version_range=[4,)
|
||||||
|
|
||||||
|
## Mod Properties
|
||||||
|
|
||||||
|
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}
|
||||||
|
# Must match the String constant located in the main mod class annotated with @Mod.
|
||||||
|
mod_id=com_burnedkirby_turnbasedminecraft
|
||||||
|
# The human-readable display name for the mod.
|
||||||
|
mod_name=TurnBasedMinecraftMod
|
||||||
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
|
mod_license=MIT
|
||||||
|
# The mod version. See https://semver.org/
|
||||||
|
mod_version=1.26.5
|
||||||
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
|
# This should match the base package used for the mod sources.
|
||||||
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
mod_group_id=com.burnedkirby.TurnBasedMinecraft
|
||||||
|
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
|
||||||
|
mod_authors=BurnedKirby a.k.a. Stephen Seo
|
||||||
|
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
||||||
|
mod_description=Implements turn-based-battle in Minecraft.
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
252
gradlew
vendored
Executable file
|
@ -0,0 +1,252 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
94
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
BIN
libs/javamp3-1.0.3.jar
Normal file
15
settings.gradle
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven {
|
||||||
|
url = 'https://maven.neoforged.net/releases'
|
||||||
|
}
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||||
|
}
|
|
@ -0,0 +1,439 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Battle;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Combatant;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Config;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketBattleDecision;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.AbstractButton;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class BattleGui extends Screen {
|
||||||
|
private AtomicInteger timeRemaining;
|
||||||
|
|
||||||
|
private int timerMax;
|
||||||
|
private boolean turnTimerEnabled;
|
||||||
|
private long lastInstant;
|
||||||
|
private long elapsedTime;
|
||||||
|
private MenuState state;
|
||||||
|
private boolean stateChanged;
|
||||||
|
private String info;
|
||||||
|
private Long waitMissingBattleTicks;
|
||||||
|
private boolean showingEntities;
|
||||||
|
|
||||||
|
private enum MenuState {
|
||||||
|
MAIN_MENU(0), ATTACK_TARGET(1), ITEM_ACTION(2), WAITING(3), SWITCH_ITEM(4), USE_ITEM(5);
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
MenuState(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, MenuState> map;
|
||||||
|
|
||||||
|
static {
|
||||||
|
map = new HashMap<Integer, MenuState>();
|
||||||
|
for (MenuState state : MenuState.values()) {
|
||||||
|
map.put(state.getValue(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MenuState valueOf(int value) {
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ButtonAction {
|
||||||
|
ATTACK(0), DEFEND(1), ITEM(2), FLEE(3), ATTACK_TARGET(4), SWITCH_HELD_ITEM(5), DECIDE_USE_ITEM(6), CANCEL(7),
|
||||||
|
DO_ITEM_SWITCH(8), DO_USE_ITEM(9);
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
ButtonAction(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, ButtonAction> map;
|
||||||
|
static {
|
||||||
|
map = new HashMap<Integer, ButtonAction>();
|
||||||
|
for (ButtonAction action : ButtonAction.values()) {
|
||||||
|
map.put(action.getValue(), action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ButtonAction valueOf(int value) {
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BattleGui() {
|
||||||
|
super(Component.literal("Battle Gui"));
|
||||||
|
timeRemaining = new AtomicInteger((int) (Config.BATTLE_DECISION_DURATION_NANO_DEFAULT / 1000000000L));
|
||||||
|
timerMax = timeRemaining.get();
|
||||||
|
lastInstant = System.nanoTime();
|
||||||
|
elapsedTime = 0;
|
||||||
|
state = MenuState.MAIN_MENU;
|
||||||
|
stateChanged = true;
|
||||||
|
waitMissingBattleTicks = null;
|
||||||
|
showingEntities = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setState(MenuState state) {
|
||||||
|
this.state = state;
|
||||||
|
stateChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnBegin() {
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getLocalBattle() != null) {
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().setState(Battle.State.ACTION);
|
||||||
|
}
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnEnd() {
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getLocalBattle() != null) {
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().setState(Battle.State.DECISION);
|
||||||
|
}
|
||||||
|
timeRemaining.set(timerMax);
|
||||||
|
elapsedTime = 0;
|
||||||
|
lastInstant = System.nanoTime();
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void battleChanged() {
|
||||||
|
stateChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState() {
|
||||||
|
if (!stateChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateChanged = false;
|
||||||
|
showingEntities = false;
|
||||||
|
clearWidgets();
|
||||||
|
switch (state) {
|
||||||
|
case MAIN_MENU:
|
||||||
|
info = "What will you do?";
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Attack"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.ATTACK);
|
||||||
|
}).bounds(width * 3 / 7 - 25, 40, 50, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Defend"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.DEFEND);
|
||||||
|
}).bounds(width * 4 / 7 - 25, 40, 50, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Item"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.ITEM);
|
||||||
|
}).bounds(width * 3 / 7 - 25, 60, 50, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Flee"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.FLEE);
|
||||||
|
}).bounds(width * 4 / 7 - 25, 60, 50, 20).build());
|
||||||
|
break;
|
||||||
|
case ATTACK_TARGET:
|
||||||
|
info = "Who will you attack?";
|
||||||
|
int y = 30;
|
||||||
|
showingEntities = true;
|
||||||
|
try {
|
||||||
|
for (Map.Entry<Integer, Combatant> e : TurnBasedMinecraftMod.proxy.getLocalBattle()
|
||||||
|
.getSideAEntrySet()) {
|
||||||
|
if (e.getValue().entity != null) {
|
||||||
|
addRenderableWidget(new EntitySelectionButton(width / 4 - 60, y, 120, 20, e.getValue().entity.getDisplayName(), e.getKey(), true, (button) -> {
|
||||||
|
entityButtonActionEvent(button, ButtonAction.ATTACK_TARGET);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
addRenderableWidget(new EntitySelectionButton(width / 4 - 60, y, 120, 20, "Unknown", e.getKey(), true, (button) -> {
|
||||||
|
entityButtonActionEvent(button, ButtonAction.ATTACK_TARGET);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
y += 20;
|
||||||
|
}
|
||||||
|
} catch (ConcurrentModificationException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
y = 30;
|
||||||
|
try {
|
||||||
|
for (Map.Entry<Integer, Combatant> e : TurnBasedMinecraftMod.proxy.getLocalBattle()
|
||||||
|
.getSideBEntrySet()) {
|
||||||
|
if (e.getValue().entity != null) {
|
||||||
|
addRenderableWidget(new EntitySelectionButton(width * 3 / 4 - 60, y, 120, 20, e.getValue().entity.getDisplayName(), e.getKey(), false, (button) -> {
|
||||||
|
entityButtonActionEvent(button, ButtonAction.ATTACK_TARGET);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
addRenderableWidget(new EntitySelectionButton(width * 3 / 4 - 60, y, 120, 20, "Unknown", e.getKey(), false, (button) -> {
|
||||||
|
entityButtonActionEvent(button, ButtonAction.ATTACK_TARGET);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
y += 20;
|
||||||
|
}
|
||||||
|
} catch (ConcurrentModificationException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Cancel"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.CANCEL);
|
||||||
|
}).bounds(width / 2 - 30, height - 120, 60, 20).build());
|
||||||
|
break;
|
||||||
|
case ITEM_ACTION:
|
||||||
|
info = "What will you do with an item?";
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Switch Held"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.SWITCH_HELD_ITEM);
|
||||||
|
}).bounds(width / 4 - 40, height - 120, 80, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Use"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.DECIDE_USE_ITEM);
|
||||||
|
}).bounds(width * 2 / 4 - 40, height - 120, 80, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Cancel"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.CANCEL);
|
||||||
|
}).bounds(width * 3 / 4 - 40, height - 120, 80, 20).build());
|
||||||
|
break;
|
||||||
|
case WAITING:
|
||||||
|
info = "Waiting...";
|
||||||
|
break;
|
||||||
|
case SWITCH_ITEM:
|
||||||
|
info = "To which item will you switch to?";
|
||||||
|
for (int i = 0; i < 9; ++i) {
|
||||||
|
addRenderableWidget(new ItemSelectionButton(width / 2 - 88 + i * 20, height - 19, 16, 16, i, (button) -> {
|
||||||
|
itemButtonActionEvent(button, ButtonAction.DO_ITEM_SWITCH);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Cancel"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.CANCEL);
|
||||||
|
}).bounds(width / 2 - 40, height - 120, 80, 20).build());
|
||||||
|
break;
|
||||||
|
case USE_ITEM:
|
||||||
|
info = "Which item will you use?";
|
||||||
|
for (int i = 0; i < 9; ++i) {
|
||||||
|
addRenderableWidget(new ItemSelectionButton(width / 2 - 88 + i * 20, height - 19, 16, 16, i, (button) -> {
|
||||||
|
itemButtonActionEvent(button, ButtonAction.DO_USE_ITEM);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Cancel"), (button) -> {
|
||||||
|
buttonActionEvent(button, ButtonAction.CANCEL);
|
||||||
|
}).bounds(width / 2 - 40, height - 120, 80, 20).build());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int colorFromTicks(final Long ticks) {
|
||||||
|
if (ticks < 20 * 10) {
|
||||||
|
double value = (20 * 10 - ticks.intValue()) / (20.0 * 10.0);
|
||||||
|
return 0xFF0000FF | (((int)(value * 255.0)) << 8) | (((int)(value * 255.0)) << 16);
|
||||||
|
} else {
|
||||||
|
return 0xFF0000FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getLocalBattle() == null) {
|
||||||
|
if (waitMissingBattleTicks == null) {
|
||||||
|
waitMissingBattleTicks = 0L;
|
||||||
|
} else {
|
||||||
|
waitMissingBattleTicks += 1L;
|
||||||
|
}
|
||||||
|
// drawHoveringText("Waiting...", width / 2 - 50, height / 2);
|
||||||
|
drawString(guiGraphics, "Waiting...", width / 2 - 50, height / 2, colorFromTicks(waitMissingBattleTicks));
|
||||||
|
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
waitMissingBattleTicks = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getLocalBattle().getState() == Battle.State.DECISION
|
||||||
|
&& timeRemaining.get() > 0) {
|
||||||
|
long nextInstant = System.nanoTime();
|
||||||
|
elapsedTime += nextInstant - lastInstant;
|
||||||
|
lastInstant = nextInstant;
|
||||||
|
while (elapsedTime > 1000000000) {
|
||||||
|
elapsedTime -= 1000000000;
|
||||||
|
timeRemaining.decrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
if (showingEntities) {
|
||||||
|
int y = 30;
|
||||||
|
try {
|
||||||
|
for (Map.Entry<Integer, Combatant> e : TurnBasedMinecraftMod.proxy.getLocalBattle().getSideAEntrySet()) {
|
||||||
|
if (e.getValue().entity instanceof LivingEntity lEntity) {
|
||||||
|
InventoryScreen.renderEntityInInventoryFollowsMouse(guiGraphics, width / 4 - 60 - 20, y, width / 4 - 60, y + 20, 7, 0.0F, mouseX, mouseY, lEntity);
|
||||||
|
}
|
||||||
|
y += 20;
|
||||||
|
}
|
||||||
|
} catch(ConcurrentModificationException e) {}
|
||||||
|
y = 30;
|
||||||
|
try {
|
||||||
|
for (Map.Entry<Integer, Combatant> e : TurnBasedMinecraftMod.proxy.getLocalBattle().getSideBEntrySet()) {
|
||||||
|
if (e.getValue().entity instanceof LivingEntity lEntity) {
|
||||||
|
InventoryScreen.renderEntityInInventoryFollowsMouse(guiGraphics, width * 3 / 4 - 60 + 120, y, width * 3 / 4 - 60 + 140, y + 20, 7, 0.0F, mouseX, mouseY, lEntity);
|
||||||
|
}
|
||||||
|
y += 20;
|
||||||
|
}
|
||||||
|
} catch(ConcurrentModificationException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
|
String timeRemainingString = "Time remaining: ";
|
||||||
|
int timeRemainingInt = timeRemaining.get();
|
||||||
|
if (timeRemainingInt > 8 || !turnTimerEnabled) {
|
||||||
|
timeRemainingString += "\u00A7a";
|
||||||
|
} else if (timeRemainingInt > 4) {
|
||||||
|
timeRemainingString += "\u00A7e";
|
||||||
|
} else {
|
||||||
|
timeRemainingString += "\u00A7c";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!turnTimerEnabled) {
|
||||||
|
timeRemainingString += "Infinity";
|
||||||
|
} else {
|
||||||
|
timeRemainingString += Integer.toString(timeRemainingInt);
|
||||||
|
}
|
||||||
|
int stringWidth = font.width(timeRemainingString);
|
||||||
|
guiGraphics.fill(width / 2 - stringWidth / 2, 5, width / 2 + stringWidth / 2, 15, 0x70000000);
|
||||||
|
drawString(guiGraphics, timeRemainingString, width / 2 - stringWidth / 2, 5, 0xFFFFFFFF);
|
||||||
|
stringWidth = font.width(info);
|
||||||
|
guiGraphics.fill(width / 2 - stringWidth / 2, 20, width / 2 + stringWidth / 2, 30, 0x70000000);
|
||||||
|
drawString(guiGraphics, info, width / 2 - stringWidth / 2, 20, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void buttonActionEvent(AbstractButton button, ButtonAction action) {
|
||||||
|
switch (action) {
|
||||||
|
case ATTACK:
|
||||||
|
setState(MenuState.ATTACK_TARGET);
|
||||||
|
break;
|
||||||
|
case DEFEND:
|
||||||
|
PacketDistributor.sendToServer(new PacketBattleDecision(
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().getId(), Battle.Decision.DEFEND.getValue(), 0));
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
break;
|
||||||
|
case ITEM:
|
||||||
|
setState(MenuState.ITEM_ACTION);
|
||||||
|
break;
|
||||||
|
case FLEE:
|
||||||
|
PacketDistributor.sendToServer(new PacketBattleDecision(
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().getId(), Battle.Decision.FLEE.getValue(), 0));
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
break;
|
||||||
|
case ATTACK_TARGET:
|
||||||
|
// Invalid, but set menu to main menu anyways.
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
break;
|
||||||
|
case SWITCH_HELD_ITEM:
|
||||||
|
setState(MenuState.SWITCH_ITEM);
|
||||||
|
break;
|
||||||
|
case DECIDE_USE_ITEM:
|
||||||
|
setState(MenuState.USE_ITEM);
|
||||||
|
break;
|
||||||
|
case CANCEL:
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
break;
|
||||||
|
case DO_ITEM_SWITCH:
|
||||||
|
// Invalid, but set menu to main menu anyways.
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
break;
|
||||||
|
case DO_USE_ITEM:
|
||||||
|
// Invalid, but set menu to main menu anyways.
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void entityButtonActionEvent(EntitySelectionButton button, ButtonAction action) {
|
||||||
|
if (action.equals(ButtonAction.ATTACK_TARGET)) {
|
||||||
|
PacketDistributor.sendToServer(
|
||||||
|
new PacketBattleDecision(TurnBasedMinecraftMod.proxy.getLocalBattle().getId(),
|
||||||
|
Battle.Decision.ATTACK.getValue(), ((EntitySelectionButton) button).getID()));
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
} else {
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void itemButtonActionEvent(ItemSelectionButton button, ButtonAction action) {
|
||||||
|
switch (action) {
|
||||||
|
case DO_ITEM_SWITCH:
|
||||||
|
PacketDistributor.sendToServer(
|
||||||
|
new PacketBattleDecision(TurnBasedMinecraftMod.proxy.getLocalBattle().getId(),
|
||||||
|
Battle.Decision.SWITCH_ITEM.getValue(), button.getID()));
|
||||||
|
if (button.getID() >= 0 && button.getID() < 9) {
|
||||||
|
Minecraft.getInstance().player.getInventory().selected = button.getID();
|
||||||
|
}
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
break;
|
||||||
|
case DO_USE_ITEM:
|
||||||
|
PacketDistributor.sendToServer(
|
||||||
|
new PacketBattleDecision(TurnBasedMinecraftMod.proxy.getLocalBattle().getId(),
|
||||||
|
Battle.Decision.USE_ITEM.getValue(), button.getID()));
|
||||||
|
setState(MenuState.WAITING);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setState(MenuState.MAIN_MENU);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPauseScreen() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int keyCode, int b, int c) {
|
||||||
|
if (getMinecraft().player.isCreative()) {
|
||||||
|
return super.keyPressed(keyCode, b, c);
|
||||||
|
} else if (keyCode == 256) {
|
||||||
|
getMinecraft().setScreen(null);
|
||||||
|
TurnBasedMinecraftMod.proxy.displayString("Leaving GUI, but the battle continues!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // TODO verify return value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyReleased(int a, int b, int c) {
|
||||||
|
if (getMinecraft().player.isCreative()) {
|
||||||
|
return super.keyReleased(a, b, c);
|
||||||
|
}
|
||||||
|
return false; // TODO verify return value
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeRemaining(int remaining) {
|
||||||
|
timeRemaining.set(remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawString(GuiGraphics guiGraphics, String string, int x, int y, int color) {
|
||||||
|
guiGraphics.drawString(font, string, x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTurnTimerEnabled(boolean enabled) {
|
||||||
|
turnTimerEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTurnTimerMax(int timerMax) {
|
||||||
|
this.timerMax = timerMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderBackground(GuiGraphics p_283688_, int p_296369_, int p_296477_, float p_294317_) {
|
||||||
|
// Prevent graying of background.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,597 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import de.jarnbjo.vorbis.VorbisAudioFileReader;
|
||||||
|
import fr.delthas.javamp3.Sound;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.sound.midi.MidiChannel;
|
||||||
|
import javax.sound.midi.MidiSystem;
|
||||||
|
import javax.sound.midi.MidiUnavailableException;
|
||||||
|
import javax.sound.midi.Sequencer;
|
||||||
|
import javax.sound.sampled.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class BattleMusic
|
||||||
|
{
|
||||||
|
private Logger logger;
|
||||||
|
private ArrayList<File> battleMusic;
|
||||||
|
private ArrayList<File> sillyMusic;
|
||||||
|
private boolean initialized;
|
||||||
|
private File nextBattle;
|
||||||
|
private File nextSilly;
|
||||||
|
private Sequencer sequencer;
|
||||||
|
private AudioInputStream wavInputStream;
|
||||||
|
private Clip clip;
|
||||||
|
private boolean playingIsSilly;
|
||||||
|
private boolean isPlaying;
|
||||||
|
private Thread mp3StreamThread;
|
||||||
|
private Thread oggVorbisStreamThread;
|
||||||
|
private MP3Streamer mp3StreamRunnable;
|
||||||
|
private OGGVorbisStreamer oggVorbisStreamRunnable;
|
||||||
|
|
||||||
|
public BattleMusic(Logger logger)
|
||||||
|
{
|
||||||
|
initialized = false;
|
||||||
|
this.logger = logger;
|
||||||
|
battleMusic = new ArrayList<File>();
|
||||||
|
sillyMusic = new ArrayList<File>();
|
||||||
|
isPlaying = false;
|
||||||
|
mp3StreamThread = null;
|
||||||
|
mp3StreamRunnable = null;
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// sequencer = MidiSystem.getSequencer();
|
||||||
|
// sequencer.open();
|
||||||
|
// } catch (Throwable t) {
|
||||||
|
// logger.error("Failed to load midi sequencer");
|
||||||
|
// t.printStackTrace();
|
||||||
|
// sequencer = null;
|
||||||
|
// }
|
||||||
|
sequencer = null; // midi disabled
|
||||||
|
|
||||||
|
File battleMusicFolder = new File(TurnBasedMinecraftMod.MUSIC_BATTLE);
|
||||||
|
File sillyMusicFolder = new File(TurnBasedMinecraftMod.MUSIC_SILLY);
|
||||||
|
|
||||||
|
if(!battleMusicFolder.exists())
|
||||||
|
{
|
||||||
|
if(!battleMusicFolder.mkdirs())
|
||||||
|
{
|
||||||
|
logger.error("Failed to create " + TurnBasedMinecraftMod.MUSIC_BATTLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!sillyMusicFolder.exists())
|
||||||
|
{
|
||||||
|
if(!sillyMusicFolder.mkdirs())
|
||||||
|
{
|
||||||
|
logger.error("Failed to create " + TurnBasedMinecraftMod.MUSIC_SILLY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] battleFiles = battleMusicFolder.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name)
|
||||||
|
{
|
||||||
|
int extIndex = name.lastIndexOf(".");
|
||||||
|
if(extIndex == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String ext = name.substring(extIndex + 1).toLowerCase();
|
||||||
|
// return ext.equals("mid") || ext.equals("wav") || ext.equals("mp3");
|
||||||
|
return ext.equals("wav") || ext.equals("mp3") || ext.equals("ogg"); // midi disabled
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(File f : battleFiles)
|
||||||
|
{
|
||||||
|
battleMusic.add(f);
|
||||||
|
}
|
||||||
|
logger.info("Got " + battleMusic.size() + " battle music files");
|
||||||
|
|
||||||
|
File[] sillyFiles = sillyMusicFolder.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name)
|
||||||
|
{
|
||||||
|
int extIndex = name.lastIndexOf(".");
|
||||||
|
if(extIndex == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String ext = name.substring(extIndex + 1).toLowerCase();
|
||||||
|
// return ext.equals("mid") || ext.equals("wav") || ext.equals("mp3");
|
||||||
|
return ext.equals("wav") || ext.equals("mp3") || ext.equals("ogg"); // midi disabled
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(File f : sillyFiles)
|
||||||
|
{
|
||||||
|
sillyMusic.add(f);
|
||||||
|
}
|
||||||
|
logger.info("Got " + sillyMusic.size() + " silly music files");
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
pickNextBattle();
|
||||||
|
pickNextSilly();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickNextBattle()
|
||||||
|
{
|
||||||
|
if(!initialized || battleMusic.isEmpty())
|
||||||
|
{
|
||||||
|
nextBattle = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextBattle = battleMusic.get((int)(Math.random() * battleMusic.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickNextSilly()
|
||||||
|
{
|
||||||
|
if(!initialized || sillyMusic.isEmpty())
|
||||||
|
{
|
||||||
|
nextSilly = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextSilly = sillyMusic.get((int)(Math.random() * sillyMusic.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playBattle(float volume)
|
||||||
|
{
|
||||||
|
if(!initialized || volume <= 0.0f || battleMusic.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(volume > 1.0f)
|
||||||
|
{
|
||||||
|
volume = 1.0f;
|
||||||
|
}
|
||||||
|
play(nextBattle, volume, true);
|
||||||
|
pickNextBattle();
|
||||||
|
playingIsSilly = false;
|
||||||
|
isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playSilly(float volume)
|
||||||
|
{
|
||||||
|
if(!initialized || volume <= 0.0f || sillyMusic.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(volume > 1.0f)
|
||||||
|
{
|
||||||
|
volume = 1.0f;
|
||||||
|
}
|
||||||
|
play(nextSilly, volume, false);
|
||||||
|
pickNextSilly();
|
||||||
|
playingIsSilly = true;
|
||||||
|
isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void play(File next, float volume, boolean isBattleType)
|
||||||
|
{
|
||||||
|
if(initialized && next != null)
|
||||||
|
{
|
||||||
|
logger.debug("play called with file " + next.getName() + " and vol " + volume);
|
||||||
|
TurnBasedMinecraftMod.proxy.pauseMCMusic();
|
||||||
|
String suffix = next.getName().substring(next.getName().length() - 3).toLowerCase();
|
||||||
|
if(suffix.equals("mid") && sequencer != null)
|
||||||
|
{
|
||||||
|
if(sequencer.isRunning())
|
||||||
|
{
|
||||||
|
sequencer.stop();
|
||||||
|
}
|
||||||
|
if(clip != null && clip.isActive())
|
||||||
|
{
|
||||||
|
clip.stop();
|
||||||
|
clip.close();
|
||||||
|
}
|
||||||
|
if(mp3StreamThread != null && mp3StreamThread.isAlive())
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setKeepPlaying(false);
|
||||||
|
try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sequencer.setSequence(new BufferedInputStream(new FileInputStream(next)));
|
||||||
|
} catch (Throwable t)
|
||||||
|
{
|
||||||
|
logger.error("Failed to play battle music (midi)");
|
||||||
|
t.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (MidiChannel channel : MidiSystem.getSynthesizer().getChannels()) {
|
||||||
|
channel.controlChange(7, (int)(volume * 127));
|
||||||
|
}
|
||||||
|
} catch (MidiUnavailableException e) {
|
||||||
|
logger.error("Failed to set Midi volume");
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
|
||||||
|
sequencer.start();
|
||||||
|
|
||||||
|
logger.info("Played music (midi) " + next.getName());
|
||||||
|
}
|
||||||
|
else if(suffix.equals("wav"))
|
||||||
|
{
|
||||||
|
if(sequencer != null && sequencer.isRunning())
|
||||||
|
{
|
||||||
|
sequencer.stop();
|
||||||
|
}
|
||||||
|
if(clip != null && clip.isActive())
|
||||||
|
{
|
||||||
|
clip.stop();
|
||||||
|
clip.close();
|
||||||
|
}
|
||||||
|
if(mp3StreamThread != null && mp3StreamThread.isAlive())
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setKeepPlaying(false);
|
||||||
|
try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(wavInputStream != null) {
|
||||||
|
wavInputStream.close();
|
||||||
|
}
|
||||||
|
wavInputStream = AudioSystem.getAudioInputStream(next);
|
||||||
|
AudioFormat format = wavInputStream.getFormat();
|
||||||
|
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||||
|
clip = (Clip) AudioSystem.getLine(info);
|
||||||
|
clip.open(wavInputStream);
|
||||||
|
} catch(Throwable t)
|
||||||
|
{
|
||||||
|
logger.error("Failed to play battle music (wav)");
|
||||||
|
t.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set volume
|
||||||
|
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
gainControl.setValue(BattleMusic.percentageToDecibels(volume));
|
||||||
|
|
||||||
|
clip.loop(Clip.LOOP_CONTINUOUSLY);
|
||||||
|
clip.start();
|
||||||
|
|
||||||
|
logger.info("Playing music (wav) " + next.getName());
|
||||||
|
}
|
||||||
|
else if(suffix.equals("mp3"))
|
||||||
|
{
|
||||||
|
if(sequencer != null && sequencer.isRunning())
|
||||||
|
{
|
||||||
|
sequencer.stop();
|
||||||
|
}
|
||||||
|
if(clip != null && clip.isActive())
|
||||||
|
{
|
||||||
|
clip.stop();
|
||||||
|
clip.close();
|
||||||
|
}
|
||||||
|
if(mp3StreamThread != null && mp3StreamThread.isAlive())
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setKeepPlaying(false);
|
||||||
|
try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(mp3StreamRunnable == null)
|
||||||
|
{
|
||||||
|
mp3StreamRunnable = new MP3Streamer(next, logger, volume);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setMp3File(next);
|
||||||
|
mp3StreamRunnable.setVolume(volume);
|
||||||
|
}
|
||||||
|
mp3StreamThread = new Thread(mp3StreamRunnable);
|
||||||
|
mp3StreamThread.start();
|
||||||
|
|
||||||
|
logger.info("Started playing mp3 " + next.getName());
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
logger.error("Failed to play battle music (mp3)");
|
||||||
|
t.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (suffix.equals("ogg")) {
|
||||||
|
if(sequencer != null && sequencer.isRunning())
|
||||||
|
{
|
||||||
|
sequencer.stop();
|
||||||
|
}
|
||||||
|
if(clip != null && clip.isActive())
|
||||||
|
{
|
||||||
|
clip.stop();
|
||||||
|
clip.close();
|
||||||
|
}
|
||||||
|
if(mp3StreamThread != null && mp3StreamThread.isAlive())
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setKeepPlaying(false);
|
||||||
|
try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (oggVorbisStreamRunnable == null) {
|
||||||
|
oggVorbisStreamRunnable = new OGGVorbisStreamer(next, logger, volume);
|
||||||
|
} else {
|
||||||
|
oggVorbisStreamRunnable.setOggVorbisFile(next);
|
||||||
|
oggVorbisStreamRunnable.setVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
oggVorbisStreamThread = new Thread(oggVorbisStreamRunnable);
|
||||||
|
oggVorbisStreamThread.start();
|
||||||
|
logger.info("Started playing OggVorbis " + next.getName());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.error("Failed to play battle music (ogg)");
|
||||||
|
t.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopMusic(boolean resumeMCSounds)
|
||||||
|
{
|
||||||
|
if(sequencer != null) {
|
||||||
|
sequencer.stop();
|
||||||
|
}
|
||||||
|
if(clip != null) {
|
||||||
|
clip.stop();
|
||||||
|
clip.close();
|
||||||
|
}
|
||||||
|
if(mp3StreamThread != null && mp3StreamThread.isAlive())
|
||||||
|
{
|
||||||
|
mp3StreamRunnable.setKeepPlaying(false);
|
||||||
|
try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
if (oggVorbisStreamThread != null && oggVorbisStreamThread.isAlive()) {
|
||||||
|
oggVorbisStreamRunnable.setKeepPlaying(false);
|
||||||
|
try { oggVorbisStreamThread.join(); } catch (Throwable t) { /* ignored */ }
|
||||||
|
}
|
||||||
|
if(resumeMCSounds)
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.resumeMCMusic();
|
||||||
|
}
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayingSilly()
|
||||||
|
{
|
||||||
|
return playingIsSilly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlaying()
|
||||||
|
{
|
||||||
|
return isPlaying || (sequencer != null && sequencer.isRunning()) || (clip != null && clip.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBattleMusic()
|
||||||
|
{
|
||||||
|
return !battleMusic.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSillyMusic()
|
||||||
|
{
|
||||||
|
return !sillyMusic.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Percentage must be between 0 and 1.
|
||||||
|
public static float percentageToDecibels(float percentage) {
|
||||||
|
if (percentage > 1.0F) {
|
||||||
|
return 0.0F;
|
||||||
|
} else if (percentage <= 0.0F) {
|
||||||
|
return Float.NEGATIVE_INFINITY;
|
||||||
|
} else {
|
||||||
|
return (float) (Math.log10(percentage) * 20.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MP3Streamer implements Runnable
|
||||||
|
{
|
||||||
|
private AtomicBoolean keepPlaying;
|
||||||
|
private File mp3File;
|
||||||
|
private Logger logger;
|
||||||
|
private float volume;
|
||||||
|
|
||||||
|
public MP3Streamer(File mp3File, Logger logger, float volume)
|
||||||
|
{
|
||||||
|
keepPlaying = new AtomicBoolean(true);
|
||||||
|
this.mp3File = mp3File;
|
||||||
|
this.logger = logger;
|
||||||
|
this.volume = volume;
|
||||||
|
if(this.volume > 1.0f)
|
||||||
|
{
|
||||||
|
this.volume = 1.0f;
|
||||||
|
}
|
||||||
|
else if(this.volume < 0.0f)
|
||||||
|
{
|
||||||
|
this.volume = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeepPlaying(boolean playing)
|
||||||
|
{
|
||||||
|
keepPlaying.set(playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMp3File(File mp3File)
|
||||||
|
{
|
||||||
|
this.mp3File = mp3File;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolume(float volume)
|
||||||
|
{
|
||||||
|
this.volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
keepPlaying.set(true);
|
||||||
|
SourceDataLine sdl = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Sound mp3Sound = new Sound(new FileInputStream(mp3File));
|
||||||
|
AudioFormat audioFormat = mp3Sound.getAudioFormat();
|
||||||
|
sdl = AudioSystem.getSourceDataLine(audioFormat);
|
||||||
|
sdl.open(audioFormat);
|
||||||
|
{
|
||||||
|
FloatControl volumeControl = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
volumeControl.setValue(BattleMusic.percentageToDecibels(volume));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
byte[] cached = null;
|
||||||
|
int cachedOffset = 0;
|
||||||
|
int cachedSize = 0;
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
sdl.start();
|
||||||
|
int read = mp3Sound.read(buf, 0, 4096);
|
||||||
|
while(keepPlaying.get())
|
||||||
|
{
|
||||||
|
if(baos != null)
|
||||||
|
{
|
||||||
|
if(read != -1)
|
||||||
|
{
|
||||||
|
sdl.write(buf, 0, read);
|
||||||
|
baos.write(buf, 0, read);
|
||||||
|
read = mp3Sound.read(buf, 0, 4096);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mp3Sound.close();
|
||||||
|
mp3Sound = null;
|
||||||
|
cached = baos.toByteArray();
|
||||||
|
baos = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cachedSize = cached.length - cachedOffset;
|
||||||
|
if(cachedSize > 4096)
|
||||||
|
{
|
||||||
|
cachedSize = 4096;
|
||||||
|
}
|
||||||
|
sdl.write(cached, cachedOffset, cachedSize);
|
||||||
|
cachedOffset += cachedSize;
|
||||||
|
if(cachedOffset >= cached.length)
|
||||||
|
{
|
||||||
|
cachedOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
logger.error("Stream play mp3", t);
|
||||||
|
}
|
||||||
|
if(sdl != null)
|
||||||
|
{
|
||||||
|
sdl.stop();
|
||||||
|
sdl.flush();
|
||||||
|
sdl.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OGGVorbisStreamer implements Runnable {
|
||||||
|
private AtomicBoolean keepPlaying;
|
||||||
|
private File oggVorbisFile;
|
||||||
|
private Logger logger;
|
||||||
|
private float volume;
|
||||||
|
|
||||||
|
public OGGVorbisStreamer(File oggVorbisFile, Logger logger, float volume) {
|
||||||
|
keepPlaying = new AtomicBoolean(true);
|
||||||
|
this.oggVorbisFile = oggVorbisFile;
|
||||||
|
this.logger = logger;
|
||||||
|
this.volume = volume;
|
||||||
|
if (this.volume > 1.0F) {
|
||||||
|
this.volume = 1.0F;
|
||||||
|
} else if (this.volume < 0.0F) {
|
||||||
|
this.volume = 0.0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeepPlaying(boolean playing) {
|
||||||
|
keepPlaying.set(playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOggVorbisFile(File oggVorbisFile) {
|
||||||
|
this.oggVorbisFile = oggVorbisFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolume(float volume) {
|
||||||
|
this.volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
keepPlaying.set(true);
|
||||||
|
SourceDataLine sdl = null;
|
||||||
|
try {
|
||||||
|
VorbisAudioFileReader reader = new VorbisAudioFileReader();
|
||||||
|
AudioFormat audioFormat = reader.getAudioFileFormat(oggVorbisFile).getFormat();
|
||||||
|
sdl = AudioSystem.getSourceDataLine(audioFormat);
|
||||||
|
sdl.open(audioFormat);
|
||||||
|
{
|
||||||
|
FloatControl volumeControl = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
volumeControl.setValue(BattleMusic.percentageToDecibels(volume));
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputStream ais = reader.getAudioInputStream(oggVorbisFile);
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
byte[] cached = null;
|
||||||
|
int cachedOffset = 0;
|
||||||
|
int cachedSize = 0;
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
sdl.start();
|
||||||
|
int read = ais.read(buf);
|
||||||
|
while (keepPlaying.get()) {
|
||||||
|
if (baos != null) {
|
||||||
|
if (read != -1) {
|
||||||
|
sdl.write(buf, 0, read);
|
||||||
|
baos.write(buf, 0, read);
|
||||||
|
read = ais.read(buf);
|
||||||
|
} else {
|
||||||
|
ais.close();
|
||||||
|
ais = null;
|
||||||
|
cached = baos.toByteArray();
|
||||||
|
baos = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cachedSize = cached.length - cachedOffset;
|
||||||
|
if (cachedSize > 4096) {
|
||||||
|
cachedSize = 4096;
|
||||||
|
}
|
||||||
|
sdl.write(cached, cachedOffset, cachedSize);
|
||||||
|
cachedOffset += cachedSize;
|
||||||
|
if (cachedOffset >= cached.length) {
|
||||||
|
cachedOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.error("Stream play oggVorbis", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdl != null) {
|
||||||
|
sdl.stop();
|
||||||
|
sdl.flush();
|
||||||
|
sdl.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import net.neoforged.neoforge.common.ModConfigSpec;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClientConfig {
|
||||||
|
public static final ClientConfig CLIENT;
|
||||||
|
public static final ModConfigSpec CLIENT_SPEC;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Pair<ClientConfig, ModConfigSpec> pair =
|
||||||
|
new ModConfigSpec.Builder().configure(ClientConfig::new);
|
||||||
|
CLIENT = pair.getKey();
|
||||||
|
CLIENT_SPEC = pair.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ModConfigSpec.ConfigValue<List<? extends String>> battleMusicList;
|
||||||
|
public final ModConfigSpec.ConfigValue<List<? extends String>> sillyMusicList;
|
||||||
|
public final ModConfigSpec.DoubleValue sillyMusicThreshold;
|
||||||
|
public final ModConfigSpec.BooleanValue volumeAffectedByMasterVolume;
|
||||||
|
public final ModConfigSpec.BooleanValue volumeAffectedByMusicVolume;
|
||||||
|
public final ModConfigSpec.DoubleValue musicVolume;
|
||||||
|
|
||||||
|
ClientConfig(ModConfigSpec.Builder builder) {
|
||||||
|
//builder.push("music");
|
||||||
|
|
||||||
|
List<String> battleMusicList = new ArrayList<String>(8);
|
||||||
|
battleMusicList.add("monster");
|
||||||
|
battleMusicList.add("animal");
|
||||||
|
battleMusicList.add("boss");
|
||||||
|
battleMusicList.add("player");
|
||||||
|
this.battleMusicList = builder.comment("What categories of mobs that play \"battle\" music")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.battle_music_list")
|
||||||
|
.defineList("battleMusicList", battleMusicList, (v) -> v instanceof String);
|
||||||
|
|
||||||
|
List<String> sillyMusicList = new ArrayList<String>(4);
|
||||||
|
sillyMusicList.add("passive");
|
||||||
|
this.sillyMusicList = builder.comment("What categories of mobs that play \"silly\" music")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.silly_music_list")
|
||||||
|
.defineList("sillyMusicList", sillyMusicList, (v) -> true);
|
||||||
|
|
||||||
|
this.sillyMusicThreshold =
|
||||||
|
builder.comment("Minimum percentage of silly entities in battle to use silly music")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.silly_percentage")
|
||||||
|
.defineInRange("sillyMusicThreshold", 0.4, 0.0, 1.0);
|
||||||
|
|
||||||
|
this.volumeAffectedByMasterVolume = builder.comment(
|
||||||
|
"If \"true\", music volume will be affected by global Master volume setting")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.volume_affected_by_master")
|
||||||
|
.define("volumeAffectedByMasterVolume", true);
|
||||||
|
|
||||||
|
this.volumeAffectedByMusicVolume = builder.comment(
|
||||||
|
"If \"true\", music volume will be affected by global Music volume setting")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.volume_affected_by_volume")
|
||||||
|
.define("volumeAffectedByMusicVolume", true);
|
||||||
|
|
||||||
|
this.musicVolume =
|
||||||
|
builder.comment("Volume of battle/silly music as a percentage between 0.0 and 1.0")
|
||||||
|
.translation(TurnBasedMinecraftMod.MODID + ".clientconfig.music_volume")
|
||||||
|
.defineInRange("musicVolume", 0.7, 0.0, 1.0);
|
||||||
|
|
||||||
|
//builder.pop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.*;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.neoforged.fml.ModContainer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClientConfigGui extends net.minecraft.client.gui.screens.Screen {
|
||||||
|
private final int widget_height = 20;
|
||||||
|
private boolean dirtyFlag;
|
||||||
|
private boolean accepted;
|
||||||
|
private EditBox battleListEditBox = null;
|
||||||
|
private EditBox sillyListEditBox = null;
|
||||||
|
private SliderPercentage sillyMusicThresholdSlider = null;
|
||||||
|
private Checkbox affectedByMasterVolCheckbox = null;
|
||||||
|
private Checkbox affectedByMusicVolCheckbox = null;
|
||||||
|
private SliderPercentage volumeSlider = null;
|
||||||
|
private Screen parentScreen = null;
|
||||||
|
|
||||||
|
public ClientConfigGui(ModContainer container, Screen parent) {
|
||||||
|
super(Component.literal("TurnBasedMC Client Config"));
|
||||||
|
|
||||||
|
dirtyFlag = true;
|
||||||
|
|
||||||
|
accepted = false;
|
||||||
|
|
||||||
|
this.parentScreen = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDirty() {
|
||||||
|
clearWidgets();
|
||||||
|
|
||||||
|
// Initialize GUI elements.
|
||||||
|
int widget_x_offset = 5;
|
||||||
|
int widget_width = this.width / 2 - widget_x_offset * 2;
|
||||||
|
int top_offset = 5;
|
||||||
|
|
||||||
|
addRenderableWidget(
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Battle Music Categories"),
|
||||||
|
font));
|
||||||
|
if (battleListEditBox == null) {
|
||||||
|
battleListEditBox =
|
||||||
|
new EditBox(font, this.width / 2 + widget_x_offset, top_offset, widget_width,
|
||||||
|
widget_height, Component.literal("Battle Music Categories Edit Box"));
|
||||||
|
} else {
|
||||||
|
battleListEditBox.setPosition(this.width / 2 + widget_x_offset, top_offset);
|
||||||
|
battleListEditBox.setSize(widget_width, widget_height);
|
||||||
|
}
|
||||||
|
String tempString = "";
|
||||||
|
for (String category : ClientConfig.CLIENT.battleMusicList.get()) {
|
||||||
|
if (tempString.isEmpty()) {
|
||||||
|
tempString = category;
|
||||||
|
} else {
|
||||||
|
tempString += "," + category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
battleListEditBox.setMaxLength(128);
|
||||||
|
battleListEditBox.setValue(tempString);
|
||||||
|
addRenderableWidget(battleListEditBox);
|
||||||
|
|
||||||
|
top_offset += widget_height;
|
||||||
|
|
||||||
|
addRenderableWidget(
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Silly Music Categories"),
|
||||||
|
font));
|
||||||
|
if (sillyListEditBox == null) {
|
||||||
|
sillyListEditBox =
|
||||||
|
new EditBox(font, this.width / 2 + widget_x_offset, top_offset, widget_width,
|
||||||
|
widget_height, Component.literal("Silly Music Categories Edit Box"));
|
||||||
|
} else {
|
||||||
|
sillyListEditBox.setPosition(this.width / 2 + widget_x_offset, top_offset);
|
||||||
|
sillyListEditBox.setSize(widget_width, widget_height);
|
||||||
|
}
|
||||||
|
tempString = "";
|
||||||
|
for (String category : ClientConfig.CLIENT.sillyMusicList.get()) {
|
||||||
|
if (tempString.isEmpty()) {
|
||||||
|
tempString = category;
|
||||||
|
} else {
|
||||||
|
tempString += "," + category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sillyListEditBox.setMaxLength(128);
|
||||||
|
sillyListEditBox.setValue(tempString);
|
||||||
|
addRenderableWidget(sillyListEditBox);
|
||||||
|
|
||||||
|
top_offset += widget_height;
|
||||||
|
|
||||||
|
StringWidget stringWidget =
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Silly Music Threshold"), font);
|
||||||
|
stringWidget.setTooltip(Tooltip.create(
|
||||||
|
Component.literal("Ratio of minimum of silly mobs in battle to play silly music")));
|
||||||
|
addRenderableWidget(stringWidget);
|
||||||
|
if (sillyMusicThresholdSlider == null) {
|
||||||
|
sillyMusicThresholdSlider =
|
||||||
|
new SliderPercentage(this.width / 2 + widget_x_offset, top_offset, widget_width,
|
||||||
|
widget_height, Component.literal("Silly Music Threshold: " +
|
||||||
|
String.format("%.1f%%", ClientConfig.CLIENT.sillyMusicThreshold.get() * 100.0)),
|
||||||
|
ClientConfig.CLIENT.sillyMusicThreshold.get(), "Silly Music Threshold: ");
|
||||||
|
} else {
|
||||||
|
sillyMusicThresholdSlider.setPosition(this.width / 2 + widget_x_offset, top_offset);
|
||||||
|
sillyMusicThresholdSlider.setSize(widget_width, widget_height);
|
||||||
|
}
|
||||||
|
addRenderableWidget(sillyMusicThresholdSlider);
|
||||||
|
|
||||||
|
top_offset += widget_height;
|
||||||
|
|
||||||
|
stringWidget =
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Affected by Master Vol."),
|
||||||
|
font);
|
||||||
|
stringWidget.setTooltip(Tooltip.create(
|
||||||
|
Component.literal("If enabled, volume is affected by global master volume.")));
|
||||||
|
addRenderableWidget(stringWidget);
|
||||||
|
if (affectedByMasterVolCheckbox == null) {
|
||||||
|
affectedByMasterVolCheckbox = Checkbox.builder(Component.literal(""), font)
|
||||||
|
.pos(this.width / 2 + widget_x_offset, top_offset).build();
|
||||||
|
} else {
|
||||||
|
affectedByMasterVolCheckbox.setPosition(this.width / 2 + widget_x_offset,
|
||||||
|
top_offset);
|
||||||
|
}
|
||||||
|
if ((ClientConfig.CLIENT.volumeAffectedByMasterVolume.get() &&
|
||||||
|
!affectedByMasterVolCheckbox.selected()) ||
|
||||||
|
(!ClientConfig.CLIENT.volumeAffectedByMasterVolume.get() &&
|
||||||
|
affectedByMasterVolCheckbox.selected())) {
|
||||||
|
affectedByMasterVolCheckbox.onPress();
|
||||||
|
}
|
||||||
|
addRenderableWidget(affectedByMasterVolCheckbox);
|
||||||
|
|
||||||
|
top_offset += widget_height;
|
||||||
|
|
||||||
|
stringWidget =
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Affected by Music Vol."), font);
|
||||||
|
stringWidget.setTooltip(Tooltip.create(
|
||||||
|
Component.literal("If enabled, volume is affected by global music volume.")));
|
||||||
|
addRenderableWidget(stringWidget);
|
||||||
|
if (affectedByMusicVolCheckbox == null) {
|
||||||
|
affectedByMusicVolCheckbox = Checkbox.builder(Component.literal(""), font)
|
||||||
|
.pos(this.width / 2 + widget_x_offset, top_offset).build();
|
||||||
|
} else {
|
||||||
|
affectedByMusicVolCheckbox.setPosition(this.width / 2 + widget_x_offset,
|
||||||
|
top_offset);
|
||||||
|
}
|
||||||
|
if ((ClientConfig.CLIENT.volumeAffectedByMusicVolume.get() &&
|
||||||
|
!affectedByMusicVolCheckbox.selected()) ||
|
||||||
|
(!ClientConfig.CLIENT.volumeAffectedByMusicVolume.get() &&
|
||||||
|
affectedByMusicVolCheckbox.selected())) {
|
||||||
|
affectedByMusicVolCheckbox.onPress();
|
||||||
|
}
|
||||||
|
addRenderableWidget(affectedByMusicVolCheckbox);
|
||||||
|
|
||||||
|
top_offset += widget_height;
|
||||||
|
|
||||||
|
stringWidget =
|
||||||
|
new StringWidget(this.width / 2 - widget_width + widget_x_offset, top_offset,
|
||||||
|
widget_width, widget_height, Component.literal("Music Volume"), font);
|
||||||
|
stringWidget.setTooltip(
|
||||||
|
Tooltip.create(Component.literal("Volume of battle/silly music")));
|
||||||
|
addRenderableWidget(stringWidget);
|
||||||
|
if (volumeSlider == null) {
|
||||||
|
volumeSlider =
|
||||||
|
new SliderPercentage(this.width / 2 + widget_x_offset, top_offset, widget_width,
|
||||||
|
widget_height, Component.literal(
|
||||||
|
"Volume: " + String.format("%.1f%%", ClientConfig.CLIENT.musicVolume.get() * 100.0)),
|
||||||
|
ClientConfig.CLIENT.musicVolume.get(), "Volume: ");
|
||||||
|
} else {
|
||||||
|
volumeSlider.setPosition(this.width / 2 + widget_x_offset, top_offset);
|
||||||
|
volumeSlider.setSize(widget_width, widget_height);
|
||||||
|
}
|
||||||
|
addRenderableWidget(volumeSlider);
|
||||||
|
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Cancel"),
|
||||||
|
(b) -> Minecraft.getInstance().setScreen(this.parentScreen))
|
||||||
|
.bounds(this.width / 2 - widget_width + widget_x_offset,
|
||||||
|
this.height - widget_height, widget_width, widget_height).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Accept"), (b) -> {
|
||||||
|
accepted = true;
|
||||||
|
}).bounds(this.width / 2 + widget_x_offset, this.height - widget_height, widget_width,
|
||||||
|
widget_height).build());
|
||||||
|
|
||||||
|
dirtyFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAccepted() {
|
||||||
|
String temp = battleListEditBox.getValue();
|
||||||
|
{
|
||||||
|
List<String> battleList = new ArrayList<String>();
|
||||||
|
for (String category : temp.split(",")) {
|
||||||
|
battleList.add(category.strip());
|
||||||
|
}
|
||||||
|
ClientConfig.CLIENT.battleMusicList.set(battleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = sillyListEditBox.getValue();
|
||||||
|
{
|
||||||
|
List<String> sillyList = new ArrayList<String>();
|
||||||
|
for (String category : temp.split(",")) {
|
||||||
|
sillyList.add(category.strip());
|
||||||
|
}
|
||||||
|
ClientConfig.CLIENT.sillyMusicList.set(sillyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientConfig.CLIENT.sillyMusicThreshold.set(sillyMusicThresholdSlider.percentage);
|
||||||
|
|
||||||
|
ClientConfig.CLIENT.volumeAffectedByMasterVolume.set(affectedByMasterVolCheckbox.selected());
|
||||||
|
|
||||||
|
ClientConfig.CLIENT.volumeAffectedByMusicVolume.set(affectedByMusicVolCheckbox.selected());
|
||||||
|
|
||||||
|
ClientConfig.CLIENT.musicVolume.set(volumeSlider.percentage);
|
||||||
|
|
||||||
|
ClientConfig.CLIENT_SPEC.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPauseScreen() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics pGuiGraphics, int pMouseX, int pMouseY, float pPartialTick) {
|
||||||
|
if (accepted) {
|
||||||
|
doAccepted();
|
||||||
|
Minecraft.getInstance().setScreen(this.parentScreen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dirtyFlag) {
|
||||||
|
onDirty();
|
||||||
|
}
|
||||||
|
super.render(pGuiGraphics, pMouseX, pMouseY, pPartialTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resize(Minecraft pMinecraft, int pWidth, int pHeight) {
|
||||||
|
dirtyFlag = true;
|
||||||
|
super.resize(pMinecraft, pWidth, pHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SliderPercentage extends AbstractSliderButton {
|
||||||
|
private final String messagePrefix;
|
||||||
|
private double percentage;
|
||||||
|
|
||||||
|
public SliderPercentage(int x, int y, int width, int height, Component message, double percentage, String messagePrefix) {
|
||||||
|
super(x, y, width, height, message, percentage);
|
||||||
|
this.percentage = percentage;
|
||||||
|
this.messagePrefix = messagePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateMessage() {
|
||||||
|
setMessage(
|
||||||
|
Component.literal(messagePrefix + String.format("%.1f%%", percentage * 100.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyValue() {
|
||||||
|
percentage = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.Renderable;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
|
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||||
|
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
|
||||||
|
public class EntitySelectionButton implements Renderable, GuiEventListener, NarratableEntry {
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
private boolean focused;
|
||||||
|
private Button nestedButton;
|
||||||
|
TBMEntityButtonPress onPress;
|
||||||
|
private int entityID;
|
||||||
|
private boolean isSideA;
|
||||||
|
|
||||||
|
public EntitySelectionButton(int x, int y, int widthIn, int heightIn, String buttonText, int entityID, boolean isSideA, TBMEntityButtonPress onPress) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = widthIn;
|
||||||
|
this.height = heightIn;
|
||||||
|
this.onPress = onPress;
|
||||||
|
this.entityID = entityID;
|
||||||
|
this.isSideA = isSideA;
|
||||||
|
this.nestedButton = Button.builder(Component.literal(buttonText), (unused) -> {}).pos(x, y).size(widthIn, heightIn).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySelectionButton(int x, int y, int widthIn, int heightIn, Component buttonTextComponent, int entityID, boolean isSideA, TBMEntityButtonPress onPress) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = widthIn;
|
||||||
|
this.height = heightIn;
|
||||||
|
this.onPress = onPress;
|
||||||
|
this.entityID = entityID;
|
||||||
|
this.isSideA = isSideA;
|
||||||
|
this.nestedButton = Button.builder(buttonTextComponent, (unused) -> {}).pos(x, y).size(widthIn, heightIn).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getID() {
|
||||||
|
return entityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsSideA() {
|
||||||
|
return isSideA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
this.nestedButton.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
|
Entity e = Minecraft.getInstance().level.getEntity(entityID);
|
||||||
|
if (e != null && e instanceof LivingEntity && ((LivingEntity) e).isAlive()) {
|
||||||
|
int health = (int) (((LivingEntity) e).getHealth() + 0.5f);
|
||||||
|
int xpos = this.x;
|
||||||
|
int xoffset;
|
||||||
|
if (isSideA) {
|
||||||
|
xpos += this.width + 4;
|
||||||
|
xoffset = 4;
|
||||||
|
} else {
|
||||||
|
xpos -= 6;
|
||||||
|
xoffset = -4;
|
||||||
|
}
|
||||||
|
if (health > 200) {
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 4 / 5, xpos + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 3 / 5, xpos + 2, this.y + this.height * 4 / 5, 0xFFFFFF00);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 2 / 5, xpos + 2, this.y + this.height * 3 / 5, 0xFF00FF00);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height / 5, xpos + 2, this.y + this.height * 2 / 5, 0xFF00FFFF);
|
||||||
|
guiGraphics.fill(xpos, this.y, xpos + 2, this.y + this.height / 5, 0xFF0000FF);
|
||||||
|
int healthHeight = ((health - 200) * this.height / 100);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFFFFFFFF);
|
||||||
|
} else if (health > 100) {
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 4 / 5, xpos + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 3 / 5, xpos + 2, this.y + this.height * 4 / 5, 0xFFFFFF00);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 2 / 5, xpos + 2, this.y + this.height * 3 / 5, 0xFF00FF00);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height / 5, xpos + 2, this.y + this.height * 2 / 5, 0xFF00FFFF);
|
||||||
|
int healthHeight = ((health - 100) * this.height / 100);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFF0000FF);
|
||||||
|
} else if (health > 50) {
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 4 / 5, xpos + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 3 / 5, xpos + 2, this.y + this.height * 4 / 5, 0xFFFFFF00);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 2 / 5, xpos + 2, this.y + this.height * 3 / 5, 0xFF00FF00);
|
||||||
|
int healthHeight = ((health - 50) * this.height / 50);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFF00FFFF);
|
||||||
|
} else if (health > 20) {
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 4 / 5, xpos + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 3 / 5, xpos + 2, this.y + this.height * 4 / 5, 0xFFFFFF00);
|
||||||
|
int healthHeight = ((health - 20) * this.height / 30);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFF00FF00);
|
||||||
|
} else if (health > 10) {
|
||||||
|
guiGraphics.fill(xpos, this.y + this.height * 4 / 5, xpos + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
int healthHeight = ((health - 10) * this.height / 10);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFFFFFF00);
|
||||||
|
} else {
|
||||||
|
int healthHeight = (health * this.height / 10);
|
||||||
|
guiGraphics.fill(xpos + xoffset, this.y + this.height - healthHeight, xpos + xoffset + 2, this.y + this.height, 0xFFFF0000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPress() {
|
||||||
|
onPress.onPress(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean b) {
|
||||||
|
this.focused = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NarrationPriority narrationPriority() {
|
||||||
|
return NarrationPriority.FOCUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNarration(NarrationElementOutput narrationElementOutput) {
|
||||||
|
narrationElementOutput.add(NarratedElementType.HINT, TurnBasedMinecraftMod.proxy.getEntity(entityID, Minecraft.getInstance().level.dimension()).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseClicked(double x, double y, int unknown) {
|
||||||
|
if (unknown == 0 && x >= this.x && y >= this.y && x <= (double)(this.x + this.width) && y <= (double)(this.y + this.height)) {
|
||||||
|
onPress();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Renderable;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
|
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||||
|
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||||
|
|
||||||
|
public class ItemSelectionButton implements Renderable, GuiEventListener, NarratableEntry {
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
TBMItemButtonPress onPress;
|
||||||
|
private int itemStackID;
|
||||||
|
private boolean focused;
|
||||||
|
|
||||||
|
public ItemSelectionButton(int x, int y, int widthIn, int heightIn, int itemStackID, TBMItemButtonPress onPress) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = widthIn;
|
||||||
|
this.height = heightIn;
|
||||||
|
this.onPress = onPress;
|
||||||
|
this.itemStackID = itemStackID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getID() {
|
||||||
|
return itemStackID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float unk) {
|
||||||
|
boolean hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
|
||||||
|
if (hovered) {
|
||||||
|
guiGraphics.fill(this.x, this.y, this.x + this.width, this.y + this.height, 0x80FFFFFF);
|
||||||
|
} else {
|
||||||
|
guiGraphics.fill(this.x, this.y, this.x + this.width, this.y + this.height, 0x20707070);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPress() {
|
||||||
|
onPress.onPress(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean b) {
|
||||||
|
focused = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NarrationPriority narrationPriority() {
|
||||||
|
return NarrationPriority.FOCUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNarration(NarrationElementOutput narrationElementOutput) {
|
||||||
|
narrationElementOutput.add(NarratedElementType.HINT, "Item " + this.itemStackID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseClicked(double x, double y, int unknown) {
|
||||||
|
if (unknown == 0 && x >= this.x && y >= this.y && x <= (double)(this.x + this.width) && y <= (double)(this.y + this.height)) {
|
||||||
|
onPress();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.components.AbstractButton;
|
||||||
|
|
||||||
|
public interface TBMButtonPress {
|
||||||
|
void onPress(AbstractButton button);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
public interface TBMEntityButtonPress {
|
||||||
|
void onPress(EntitySelectionButton button);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.client;
|
||||||
|
|
||||||
|
public interface TBMItemButtonPress {
|
||||||
|
void onPress(ItemSelectionButton button);
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketBattleMessage;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketEditingMessage;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketGeneralMessage;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.damagesource.DamageTypes;
|
||||||
|
import net.minecraft.world.entity.monster.Creeper;
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingChangeTargetEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class AttackEventHandler
|
||||||
|
{
|
||||||
|
private boolean isAttackerValid(LivingIncomingDamageEvent event)
|
||||||
|
{
|
||||||
|
if(event.getSource().getEntity() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(event.getSource().getEntity().equals(TurnBasedMinecraftMod.proxy.getAttackingEntity()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final long now = System.nanoTime();
|
||||||
|
boolean isValid = false;
|
||||||
|
synchronized(TurnBasedMinecraftMod.proxy.getAttackerViaBowSet())
|
||||||
|
{
|
||||||
|
for(Iterator<AttackerViaBow> iter = TurnBasedMinecraftMod.proxy.getAttackerViaBowSet().iterator(); iter.hasNext();)
|
||||||
|
{
|
||||||
|
AttackerViaBow attacker = iter.next();
|
||||||
|
if(now - attacker.attackTime >= AttackerViaBow.ATTACK_TIMEOUT)
|
||||||
|
{
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
else if(event.getSource().getEntity().equals(attacker.entity) && event.getSource().is(DamageTypes.ARROW))
|
||||||
|
{
|
||||||
|
iter.remove();
|
||||||
|
if(!isValid)
|
||||||
|
{
|
||||||
|
Battle b = TurnBasedMinecraftMod.proxy.getBattleManager().getBattleByID(attacker.battleID);
|
||||||
|
if(b != null)
|
||||||
|
{
|
||||||
|
b.sendMessageToAllPlayers(PacketBattleMessage.MessageType.ARROW_HIT, attacker.entity.getId(), event.getEntity().getId(), 0);
|
||||||
|
}
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void entityAttacked(LivingIncomingDamageEvent event)
|
||||||
|
{
|
||||||
|
if(event.getEntity().level().isClientSide)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CommonProxy proxy = TurnBasedMinecraftMod.proxy;
|
||||||
|
Config config = proxy.getConfig();
|
||||||
|
BattleManager battleManager = proxy.getBattleManager();
|
||||||
|
// handle edit entity, pick entity via attack
|
||||||
|
{
|
||||||
|
if(event.getSource().getEntity() != null && event.getEntity() != null)
|
||||||
|
{
|
||||||
|
final EditingInfo editingInfo = proxy.getEditingInfo(event.getSource().getEntity().getId());
|
||||||
|
if(editingInfo != null && editingInfo.isPendingEntitySelection)
|
||||||
|
{
|
||||||
|
editingInfo.isPendingEntitySelection = false;
|
||||||
|
event.setCanceled(true);
|
||||||
|
if(editingInfo.isEditingCustomName)
|
||||||
|
{
|
||||||
|
if(!event.getEntity().hasCustomName())
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.logger.error("Cannot edit custom name from entity without custom name");
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)editingInfo.editor, new PacketGeneralMessage("Cannot edit custom name from entity without custom name"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editingInfo.entityInfo = config.getCustomEntityInfo(event.getEntity().getCustomName().getString());
|
||||||
|
if(editingInfo.entityInfo == null)
|
||||||
|
{
|
||||||
|
editingInfo.entityInfo = new EntityInfo();
|
||||||
|
editingInfo.entityInfo.customName = event.getEntity().getCustomName().getString();
|
||||||
|
}
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)editingInfo.editor, new PacketGeneralMessage("Editing custom name \"" + event.getEntity().getCustomName().getString() + "\""));
|
||||||
|
TurnBasedMinecraftMod.logger.info("Begin editing custom \"" + event.getEntity().getCustomName().getString() + "\"");
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)editingInfo.editor, new PacketEditingMessage(PacketEditingMessage.Type.PICK_EDIT, editingInfo.entityInfo));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editingInfo.entityInfo = config.getMatchingEntityInfo(event.getEntity());
|
||||||
|
if(editingInfo.entityInfo == null)
|
||||||
|
{
|
||||||
|
editingInfo.entityInfo = new EntityInfo();
|
||||||
|
editingInfo.entityInfo.classType = event.getEntity().getClass();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editingInfo.entityInfo = editingInfo.entityInfo.clone();
|
||||||
|
}
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)editingInfo.editor, new PacketGeneralMessage("Editing entity \"" + editingInfo.entityInfo.classType.getName() + "\""));
|
||||||
|
TurnBasedMinecraftMod.logger.info("Begin editing \"" + editingInfo.entityInfo.classType.getName() + "\"");
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)editingInfo.editor, new PacketEditingMessage(PacketEditingMessage.Type.PICK_EDIT, editingInfo.entityInfo));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(event.getEntity() != null && event.getSource().getEntity() != null && (battleManager.isRecentlyLeftBattle(event.getEntity().getId()) || battleManager.isRecentlyLeftBattle(event.getSource().getEntity().getId())))
|
||||||
|
{
|
||||||
|
if(event.getSource().getEntity() instanceof Creeper && TurnBasedMinecraftMod.proxy.getConfig().getCreeperAlwaysAllowDamage()) {
|
||||||
|
event.setCanceled(false);
|
||||||
|
} else {
|
||||||
|
// TurnBasedMinecraftMod.logger.debug("Canceled attack");
|
||||||
|
event.setCanceled(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(!isAttackerValid(event)
|
||||||
|
&& event.getEntity() != null
|
||||||
|
&& event.getSource().getEntity() != null
|
||||||
|
&& event.getEntity() != event.getSource().getEntity()
|
||||||
|
&& !config.getBattleIgnoringPlayers().contains(event.getSource().getEntity().getId())
|
||||||
|
&& !config.getBattleIgnoringPlayers().contains(event.getEntity().getId())
|
||||||
|
&& event.getEntity().level().dimension().equals(event.getSource().getEntity().level().dimension())
|
||||||
|
&& battleManager.checkAttack(event))
|
||||||
|
{
|
||||||
|
// TurnBasedMinecraftMod.logger.debug("Canceled LivingAttackEvent between " + TurnBasedMinecraftMod.proxy.getAttackingEntity() + " and " + event.getEntity());
|
||||||
|
event.setCanceled(true);
|
||||||
|
} else {
|
||||||
|
// TurnBasedMinecraftMod.logger.debug("Did not cancel attack");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(TurnBasedMinecraftMod.proxy.getAttackingDamage() < (int) event.getAmount())
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.setAttackingDamage((int) event.getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void entityTargeted(LivingChangeTargetEvent event)
|
||||||
|
{
|
||||||
|
Config config = TurnBasedMinecraftMod.proxy.getConfig();
|
||||||
|
BattleManager battleManager = TurnBasedMinecraftMod.proxy.getBattleManager();
|
||||||
|
if(event.getEntity().level().isClientSide
|
||||||
|
|| config.isOldBattleBehaviorEnabled()
|
||||||
|
|| (event.getEntity() != null && battleManager.isRecentlyLeftBattle(event.getEntity().getId()))
|
||||||
|
|| (event.getNewAboutToBeSetTarget() != null && battleManager.isRecentlyLeftBattle(event.getNewAboutToBeSetTarget().getId()))
|
||||||
|
|| (event.getEntity() != null && event.getNewAboutToBeSetTarget() != null && Utility.distanceBetweenEntities(event.getEntity(), event.getNewAboutToBeSetTarget()) > (double)config.getAggroStartBattleDistance()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(event.getEntity() != null
|
||||||
|
&& event.getNewAboutToBeSetTarget() != null
|
||||||
|
&& !config.getBattleIgnoringPlayers().contains(event.getEntity().getId())
|
||||||
|
&& !config.getBattleIgnoringPlayers().contains(event.getNewAboutToBeSetTarget().getId())
|
||||||
|
&& event.getEntity().level().dimension().equals(event.getNewAboutToBeSetTarget().level().dimension()))
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.getBattleManager().checkTargeted(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
|
public class AttackerViaBow
|
||||||
|
{
|
||||||
|
public static long ATTACK_TIMEOUT = 10000000000L;
|
||||||
|
|
||||||
|
public Entity entity;
|
||||||
|
public long attackTime;
|
||||||
|
public int battleID;
|
||||||
|
|
||||||
|
public AttackerViaBow()
|
||||||
|
{
|
||||||
|
entity = null;
|
||||||
|
attackTime = 0;
|
||||||
|
battleID = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackerViaBow(Entity entity, int battleID)
|
||||||
|
{
|
||||||
|
this.entity = entity;
|
||||||
|
attackTime = System.nanoTime();
|
||||||
|
this.battleID = battleID;
|
||||||
|
}
|
||||||
|
}
|
1415
src/main/java/com/burnedkirby/TurnBasedMinecraft/common/Battle.java
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketGeneralMessage;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.monster.Creeper;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.neoforged.neoforge.common.NeoForge;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingChangeTargetEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class BattleManager
|
||||||
|
{
|
||||||
|
private int IDCounter = 0;
|
||||||
|
protected Map<Integer, Battle> battleMap;
|
||||||
|
private Logger logger;
|
||||||
|
private Map<Integer, Combatant> recentlyLeftBattle;
|
||||||
|
private BattleUpdater battleUpdater;
|
||||||
|
private Map<EntityIDDimPair, Integer> entityToBattleMap;
|
||||||
|
private EntityIDDimPair tempIDPair;
|
||||||
|
|
||||||
|
public BattleManager(Logger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
battleMap = new HashMap<Integer, Battle>();
|
||||||
|
recentlyLeftBattle = new HashMap<Integer, Combatant>();
|
||||||
|
battleUpdater = new BattleUpdater(this);
|
||||||
|
entityToBattleMap = new HashMap<EntityIDDimPair, Integer>();
|
||||||
|
NeoForge.EVENT_BUS.register(battleUpdater);
|
||||||
|
tempIDPair = new EntityIDDimPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Either creates a new Battle, adds a combatant to an existing Battle, or does
|
||||||
|
* nothing, depending on if a player is involved and/or an entity is currently
|
||||||
|
* in battle.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @return True if event should be canceled
|
||||||
|
*/
|
||||||
|
public boolean checkAttack(final LivingIncomingDamageEvent event)
|
||||||
|
{
|
||||||
|
Config config = TurnBasedMinecraftMod.proxy.getConfig();
|
||||||
|
String receiverClassName = event.getEntity().getClass().getName();
|
||||||
|
String receiverCustomName;
|
||||||
|
|
||||||
|
try {
|
||||||
|
receiverCustomName = event.getEntity().getCustomName().getString();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
receiverCustomName = null;
|
||||||
|
}
|
||||||
|
String attackerClassName;
|
||||||
|
try {
|
||||||
|
attackerClassName = event.getSource().getEntity().getClass().getName();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
attackerClassName = null;
|
||||||
|
}
|
||||||
|
String attackerCustomName;
|
||||||
|
try {
|
||||||
|
attackerCustomName = event.getSource().getEntity().getCustomName().getString();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
attackerCustomName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that both entities are EntityPlayer and not in creative or has a corresponding EntityInfo.
|
||||||
|
// Also check if "player_only_battles" is enabled and both entities are players.
|
||||||
|
if(!((event.getEntity() instanceof Player && !((Player)event.getEntity()).isCreative())
|
||||||
|
|| (config.getEntityInfoReference(receiverClassName) != null || config.getCustomEntityInfoReference(receiverCustomName) != null))
|
||||||
|
|| !((event.getSource().getEntity() instanceof Player && !((Player)event.getSource().getEntity()).isCreative())
|
||||||
|
|| (config.getEntityInfoReference(attackerClassName) != null || config.getCustomEntityInfoReference(attackerCustomName) != null))
|
||||||
|
|| (TurnBasedMinecraftMod.proxy.getConfig().isPlayerOnlyBattlesEnabled() &&
|
||||||
|
(!(event.getEntity() instanceof Player) || !(event.getSource().getEntity() instanceof Player))))
|
||||||
|
{
|
||||||
|
// logger.debug("BattleManager: Failed first check, attacker is \"" + attackerClassName + "\", defender is \"" + receiverClassName + "\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if ignore battle in config
|
||||||
|
EntityInfo entityInfo = config.getCustomEntityInfoReference(receiverCustomName);
|
||||||
|
if(entityInfo == null)
|
||||||
|
{
|
||||||
|
entityInfo = config.getMatchingEntityInfo(event.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entityInfo != null && (config.isIgnoreBattleType(entityInfo.category) || entityInfo.ignoreBattle))
|
||||||
|
{
|
||||||
|
// attacked entity ignores battle
|
||||||
|
Battle battle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getSource().getEntity())));
|
||||||
|
if(battle != null && battle.hasCombatant(event.getSource().getEntity().getId())) {
|
||||||
|
logger.debug("Attack Canceled: attacked ignores battle but attacker in battle");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.debug("Attack Not Canceled: attacked ignores battle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entityInfo = config.getCustomEntityInfoReference(attackerCustomName);
|
||||||
|
if(entityInfo == null)
|
||||||
|
{
|
||||||
|
entityInfo = config.getMatchingEntityInfo(event.getSource().getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entityInfo != null && (config.isIgnoreBattleType(entityInfo.category) || entityInfo.ignoreBattle))
|
||||||
|
{
|
||||||
|
// attacker entity ignores battle
|
||||||
|
Battle battle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getEntity())));
|
||||||
|
if(battle != null && battle.hasCombatant(event.getEntity().getId())) {
|
||||||
|
logger.debug("Attack Canceled: attacker ignores battle but attacked in battle");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.debug("Attack Not Canceled: attacker ignores battle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if one is in battle
|
||||||
|
Battle attackerBattle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getSource().getEntity())));
|
||||||
|
if(attackerBattle != null && !attackerBattle.hasCombatant(event.getSource().getEntity().getId())) {
|
||||||
|
attackerBattle = null;
|
||||||
|
}
|
||||||
|
Battle defenderBattle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getEntity())));
|
||||||
|
if(defenderBattle != null && !defenderBattle.hasCombatant(event.getEntity().getId())) {
|
||||||
|
defenderBattle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attackerBattle != null && defenderBattle != null) {
|
||||||
|
// both in battle, attack canceled
|
||||||
|
return true;
|
||||||
|
} else if(attackerBattle == null && defenderBattle == null) {
|
||||||
|
// neither entity is in battle
|
||||||
|
if(event.getEntity() instanceof Player || event.getSource().getEntity() instanceof Player)
|
||||||
|
{
|
||||||
|
// at least one of the entities is a player, create Battle
|
||||||
|
Collection<Entity> sideA = new ArrayList<Entity>(1);
|
||||||
|
Collection<Entity> sideB = new ArrayList<Entity>(1);
|
||||||
|
sideA.add(event.getEntity());
|
||||||
|
sideB.add(event.getSource().getEntity());
|
||||||
|
createBattle(sideA, sideB, event.getEntity().level().dimension());
|
||||||
|
logger.debug("Attack Not Canceled: new battle created");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("Attack Not Canceled: neither are in battle or players");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// at this point only one entity is in battle, so add entity to other side
|
||||||
|
if(attackerBattle != null) {
|
||||||
|
if (attackerBattle.getSize() >= config.getMaxInBattle()) {
|
||||||
|
// battle limit reached, cannot add to battle
|
||||||
|
return true;
|
||||||
|
} else if (attackerBattle.hasCombatantInSideA(event.getSource().getEntity().getId())) {
|
||||||
|
attackerBattle.addCombatantToSideB(event.getEntity());
|
||||||
|
} else {
|
||||||
|
attackerBattle.addCombatantToSideA(event.getEntity());
|
||||||
|
}
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(event.getEntity()), attackerBattle.getId());
|
||||||
|
} else {
|
||||||
|
if (defenderBattle.getSize() >= config.getMaxInBattle()) {
|
||||||
|
// battle limit reached, cannot add to battle
|
||||||
|
return true;
|
||||||
|
} else if (defenderBattle.hasCombatantInSideA(event.getEntity().getId())) {
|
||||||
|
defenderBattle.addCombatantToSideB(event.getSource().getEntity());
|
||||||
|
} else {
|
||||||
|
defenderBattle.addCombatantToSideA(event.getSource().getEntity());
|
||||||
|
}
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(event.getSource().getEntity()), defenderBattle.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Attack Canceled: one is in battle");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkTargeted(LivingChangeTargetEvent event)
|
||||||
|
{
|
||||||
|
// Check if "player_only_battles" is enabled and if both entities are players.
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getConfig().isPlayerOnlyBattlesEnabled() &&
|
||||||
|
(!(event.getEntity() instanceof Player) || !(event.getNewAboutToBeSetTarget() instanceof Player))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetedCustomName;
|
||||||
|
try {
|
||||||
|
targetedCustomName = event.getNewAboutToBeSetTarget().getCustomName().getString();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
targetedCustomName = null;
|
||||||
|
}
|
||||||
|
String attackerCustomName;
|
||||||
|
try {
|
||||||
|
attackerCustomName = event.getEntity().getCustomName().getString();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
attackerCustomName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityInfo attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(attackerCustomName);
|
||||||
|
if(attackerInfo == null)
|
||||||
|
{
|
||||||
|
attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityInfo targetedInfo;
|
||||||
|
if(event.getNewAboutToBeSetTarget() instanceof Player)
|
||||||
|
{
|
||||||
|
targetedInfo = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(targetedCustomName);
|
||||||
|
if(targetedInfo == null)
|
||||||
|
{
|
||||||
|
targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getNewAboutToBeSetTarget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((event.getNewAboutToBeSetTarget() instanceof Player && ((Player)event.getNewAboutToBeSetTarget()).isCreative())
|
||||||
|
|| attackerInfo == null
|
||||||
|
|| attackerInfo.ignoreBattle
|
||||||
|
|| TurnBasedMinecraftMod.proxy.getConfig().isIgnoreBattleType(attackerInfo.category)
|
||||||
|
|| (targetedInfo != null
|
||||||
|
&& (targetedInfo.ignoreBattle
|
||||||
|
|| TurnBasedMinecraftMod.proxy.getConfig().isIgnoreBattleType(targetedInfo.category))))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if one is in battle
|
||||||
|
Battle attackerBattle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getEntity())));
|
||||||
|
if(attackerBattle != null && !attackerBattle.hasCombatant(event.getEntity().getId())) {
|
||||||
|
attackerBattle = null;
|
||||||
|
}
|
||||||
|
Battle defenderBattle = battleMap.get(entityToBattleMap.get(new EntityIDDimPair(event.getNewAboutToBeSetTarget())));
|
||||||
|
if(defenderBattle != null && !defenderBattle.hasCombatant(event.getNewAboutToBeSetTarget().getId())) {
|
||||||
|
defenderBattle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attackerBattle != null && defenderBattle != null) {
|
||||||
|
return;
|
||||||
|
} else if(attackerBattle == null && defenderBattle == null) {
|
||||||
|
// neither in battle
|
||||||
|
if(event.getEntity() instanceof Player || event.getNewAboutToBeSetTarget() instanceof Player)
|
||||||
|
{
|
||||||
|
// at least one is a player, create battle
|
||||||
|
Collection<Entity> sideA = new ArrayList<Entity>(1);
|
||||||
|
Collection<Entity> sideB = new ArrayList<Entity>(1);
|
||||||
|
sideA.add(event.getEntity());
|
||||||
|
sideB.add(event.getNewAboutToBeSetTarget());
|
||||||
|
createBattle(sideA, sideB, event.getEntity().level().dimension());
|
||||||
|
logger.debug("neither in battle, at least one is player, creating new battle");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// add entity to battle
|
||||||
|
if(attackerBattle != null) {
|
||||||
|
if (attackerBattle.getSize() >= TurnBasedMinecraftMod.proxy.getConfig().getMaxInBattle()) {
|
||||||
|
// battle max reached, cannot add to battle
|
||||||
|
return;
|
||||||
|
} else if (attackerBattle.hasCombatantInSideA(event.getEntity().getId())) {
|
||||||
|
attackerBattle.addCombatantToSideB(event.getNewAboutToBeSetTarget());
|
||||||
|
} else {
|
||||||
|
attackerBattle.addCombatantToSideA(event.getNewAboutToBeSetTarget());
|
||||||
|
}
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(event.getNewAboutToBeSetTarget()), attackerBattle.getId());
|
||||||
|
} else {
|
||||||
|
if (defenderBattle.getSize() >= TurnBasedMinecraftMod.proxy.getConfig().getMaxInBattle()) {
|
||||||
|
// battle max reached, cannot add to battle
|
||||||
|
return;
|
||||||
|
} else if (defenderBattle.hasCombatantInSideA(event.getNewAboutToBeSetTarget().getId())) {
|
||||||
|
defenderBattle.addCombatantToSideB(event.getEntity());
|
||||||
|
} else {
|
||||||
|
defenderBattle.addCombatantToSideA(event.getEntity());
|
||||||
|
}
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(event.getEntity()), defenderBattle.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Battle createBattle(Collection<Entity> sideA, Collection<Entity> sideB, ResourceKey<Level> dimension)
|
||||||
|
{
|
||||||
|
Battle newBattle = null;
|
||||||
|
while(battleMap.containsKey(IDCounter))
|
||||||
|
{
|
||||||
|
++IDCounter;
|
||||||
|
}
|
||||||
|
newBattle = new Battle(this, IDCounter, sideA, sideB, true, dimension);
|
||||||
|
battleMap.put(IDCounter, newBattle);
|
||||||
|
for(Entity e : sideA) {
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(e), newBattle.getId());
|
||||||
|
}
|
||||||
|
for(Entity e : sideB) {
|
||||||
|
entityToBattleMap.put(new EntityIDDimPair(e), newBattle.getId());
|
||||||
|
}
|
||||||
|
newBattle.notifyPlayersBattleInfo();
|
||||||
|
return newBattle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battle getBattleByID(int id)
|
||||||
|
{
|
||||||
|
return battleMap.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup()
|
||||||
|
{
|
||||||
|
battleUpdater.setRunning(false);
|
||||||
|
NeoForge.EVENT_BUS.unregister(battleUpdater);
|
||||||
|
battleMap.clear();
|
||||||
|
battleUpdater = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addRecentlyLeftBattle(Combatant c)
|
||||||
|
{
|
||||||
|
c.time = System.nanoTime();
|
||||||
|
Config config = TurnBasedMinecraftMod.proxy.getConfig();
|
||||||
|
if(c.entity instanceof ServerPlayer) {
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)c.entity, new PacketGeneralMessage("You just left battle! " + config.getLeaveBattleCooldownSeconds() + " seconds until you can attack/be-attacked again!"));
|
||||||
|
}
|
||||||
|
recentlyLeftBattle.put(c.entity.getId(), c);
|
||||||
|
entityToBattleMap.remove(new EntityIDDimPair(c.entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateRecentlyLeftBattle()
|
||||||
|
{
|
||||||
|
long current = System.nanoTime();
|
||||||
|
for(Iterator<Map.Entry<Integer, Combatant>> iter = recentlyLeftBattle.entrySet().iterator(); iter.hasNext();)
|
||||||
|
{
|
||||||
|
Map.Entry<Integer, Combatant> entry = iter.next();
|
||||||
|
if(entry.getValue().entity instanceof Creeper && TurnBasedMinecraftMod.proxy.getConfig().getCreeperStopExplodeOnLeaveBattle()) {
|
||||||
|
((Creeper)entry.getValue().entity).setSwellDir(-10);
|
||||||
|
}
|
||||||
|
if(current - entry.getValue().time > TurnBasedMinecraftMod.proxy.getConfig().getLeaveBattleCooldownNanos())
|
||||||
|
{
|
||||||
|
iter.remove();
|
||||||
|
if(entry.getValue().entity instanceof ServerPlayer)
|
||||||
|
{
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)entry.getValue().entity, new PacketGeneralMessage("Timer ended, you can now attack/be-attacked again."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRecentlyLeftBattle(int entityID)
|
||||||
|
{
|
||||||
|
return recentlyLeftBattle.containsKey(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean forceLeaveBattle(EntityIDDimPair entityInfo) {
|
||||||
|
boolean result = false;
|
||||||
|
Integer battleID = entityToBattleMap.get(entityInfo);
|
||||||
|
if(battleID != null) {
|
||||||
|
Battle battle = battleMap.get(battleID);
|
||||||
|
if (battle != null && battle.hasCombatant(entityInfo.id)) {
|
||||||
|
battle.forceRemoveCombatant(entityInfo);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
entityToBattleMap.remove(entityInfo);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInBattle(Entity entity) {
|
||||||
|
synchronized(tempIDPair) {
|
||||||
|
tempIDPair.id = entity.getId();
|
||||||
|
tempIDPair.dim = entity.level().dimension();
|
||||||
|
return entityToBattleMap.keySet().contains(tempIDPair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class BattleUpdater
|
||||||
|
{
|
||||||
|
private BattleManager manager;
|
||||||
|
private AtomicBoolean isRunning;
|
||||||
|
private int tick;
|
||||||
|
private final int tickLimit = 3;
|
||||||
|
|
||||||
|
public BattleUpdater(BattleManager manager)
|
||||||
|
{
|
||||||
|
this.manager = manager;
|
||||||
|
isRunning = new AtomicBoolean(true);
|
||||||
|
tick = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRunning(boolean isRunning) {
|
||||||
|
this.isRunning.set(isRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void update(ServerTickEvent.Post tickEvent) {
|
||||||
|
//if(tickEvent.phase != TickEvent.Phase.START && isRunning.get() && ++tick > tickLimit) {
|
||||||
|
if(isRunning.get() && ++tick > tickLimit && tickEvent.hasTime()) {
|
||||||
|
tick = 0;
|
||||||
|
manager.battleMap.entrySet().removeIf(entry -> entry.getValue().update());
|
||||||
|
manager.updateRecentlyLeftBattle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class Combatant
|
||||||
|
{
|
||||||
|
public Entity entity;
|
||||||
|
public Battle.Decision decision;
|
||||||
|
public int itemToUse;
|
||||||
|
public EntityInfo entityInfo;
|
||||||
|
public boolean recalcSpeedOnCompare;
|
||||||
|
public int targetEntityID;
|
||||||
|
public boolean isSideA;
|
||||||
|
public int remainingDefenses;
|
||||||
|
public int battleID;
|
||||||
|
public double x;
|
||||||
|
//public double y; // y is ignored to prevent perpetual fall damage when FreezeBattleCombatants is enabled
|
||||||
|
public double z;
|
||||||
|
public float yaw;
|
||||||
|
public float pitch;
|
||||||
|
public long time;
|
||||||
|
public int creeperTurns;
|
||||||
|
|
||||||
|
public Combatant()
|
||||||
|
{
|
||||||
|
decision = Battle.Decision.UNDECIDED;
|
||||||
|
recalcSpeedOnCompare = false;
|
||||||
|
remainingDefenses = 0;
|
||||||
|
creeperTurns = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Combatant(Entity e, EntityInfo entityInfo)
|
||||||
|
{
|
||||||
|
entity = e;
|
||||||
|
decision = Battle.Decision.UNDECIDED;
|
||||||
|
this.entityInfo = entityInfo;
|
||||||
|
recalcSpeedOnCompare = false;
|
||||||
|
remainingDefenses = 0;
|
||||||
|
creeperTurns = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided in reverse order of speed because PriorityQueue has least first.
|
||||||
|
*/
|
||||||
|
public static class CombatantComparator implements Comparator<Combatant>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int compare(Combatant c0, Combatant c1)
|
||||||
|
{
|
||||||
|
if(c0.entity instanceof Player && c0.recalcSpeedOnCompare)
|
||||||
|
{
|
||||||
|
LivingEntity c0Entity = (LivingEntity)c0.entity;
|
||||||
|
boolean isHaste = false;
|
||||||
|
boolean isSlow = false;
|
||||||
|
for(MobEffectInstance e : c0Entity.getActiveEffects())
|
||||||
|
{
|
||||||
|
if(e.getEffect().equals(MobEffects.MOVEMENT_SPEED) || e.getEffect().equals(MobEffects.DIG_SPEED))
|
||||||
|
{
|
||||||
|
isHaste = true;
|
||||||
|
}
|
||||||
|
else if(e.getEffect().equals(MobEffects.MOVEMENT_SLOWDOWN) || e.getEffect().equals(MobEffects.DIG_SLOWDOWN))
|
||||||
|
{
|
||||||
|
isSlow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(c0.entityInfo == null)
|
||||||
|
{
|
||||||
|
c0.entityInfo = new EntityInfo();
|
||||||
|
}
|
||||||
|
if(isHaste && !isSlow)
|
||||||
|
{
|
||||||
|
c0.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerHasteSpeed();
|
||||||
|
}
|
||||||
|
else if(isSlow && !isHaste)
|
||||||
|
{
|
||||||
|
c0.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerSlowSpeed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c0.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c1.entity instanceof Player && c1.recalcSpeedOnCompare)
|
||||||
|
{
|
||||||
|
LivingEntity c1Entity = (LivingEntity)c1.entity;
|
||||||
|
boolean isHaste = false;
|
||||||
|
boolean isSlow = false;
|
||||||
|
for(MobEffectInstance e : c1Entity.getActiveEffects())
|
||||||
|
{
|
||||||
|
if(e.getEffect().equals(MobEffects.MOVEMENT_SPEED))
|
||||||
|
{
|
||||||
|
isHaste = true;
|
||||||
|
}
|
||||||
|
else if(e.getEffect().equals(MobEffects.MOVEMENT_SLOWDOWN))
|
||||||
|
{
|
||||||
|
isSlow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(c1.entityInfo == null)
|
||||||
|
{
|
||||||
|
c1.entityInfo = new EntityInfo();
|
||||||
|
}
|
||||||
|
if(isHaste && !isSlow)
|
||||||
|
{
|
||||||
|
c1.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerHasteSpeed();
|
||||||
|
}
|
||||||
|
else if(isSlow && !isHaste)
|
||||||
|
{
|
||||||
|
c1.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerSlowSpeed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c1.entityInfo.speed = TurnBasedMinecraftMod.proxy.getConfig().getPlayerSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c0.entityInfo.speed > c1.entityInfo.speed)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if(c0.entityInfo.speed < c1.entityInfo.speed)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CommonProxy
|
||||||
|
{
|
||||||
|
private Set<AttackerViaBow> attackerViaBow = null;
|
||||||
|
private BattleManager battleManager = null;
|
||||||
|
private Entity attackingEntity = null;
|
||||||
|
private int attackingDamage = 0;
|
||||||
|
private Config config = null;
|
||||||
|
protected Logger logger = null;
|
||||||
|
private Map<Integer, EditingInfo> editingPlayers;
|
||||||
|
|
||||||
|
public final void initialize()
|
||||||
|
{
|
||||||
|
attackerViaBow = new HashSet<AttackerViaBow>();
|
||||||
|
editingPlayers = new Hashtable<Integer, EditingInfo>();
|
||||||
|
initializeClient();
|
||||||
|
logger.debug("Init proxy for com_burnedkirby_turnbasedminecraft");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeClient() {}
|
||||||
|
|
||||||
|
public final boolean initializeBattleManager()
|
||||||
|
{
|
||||||
|
if(battleManager == null)
|
||||||
|
{
|
||||||
|
battleManager = new BattleManager(TurnBasedMinecraftMod.logger);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean cleanupBattleManager ()
|
||||||
|
{
|
||||||
|
if(battleManager != null)
|
||||||
|
{
|
||||||
|
battleManager.cleanup();
|
||||||
|
battleManager = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBattleGuiTime(int timeRemaining) {}
|
||||||
|
|
||||||
|
public void setBattleGuiBattleChanged() {}
|
||||||
|
|
||||||
|
public void setBattleGuiAsGui() {}
|
||||||
|
|
||||||
|
public void setBattleGuiTurnTimerEnabled(boolean enabled) {}
|
||||||
|
|
||||||
|
public void setBattleGuiTurnTimerMax(int timeMax) {}
|
||||||
|
|
||||||
|
public void battleGuiTurnBegin() {}
|
||||||
|
|
||||||
|
public void battleGuiTurnEnd() {}
|
||||||
|
|
||||||
|
public void battleStarted() {}
|
||||||
|
|
||||||
|
public void battleEnded() {}
|
||||||
|
|
||||||
|
public final void postInit()
|
||||||
|
{
|
||||||
|
config = new Config(logger);
|
||||||
|
postInitClient();
|
||||||
|
logger.debug("postInit proxy for com_burnedkirby_turnbasedminecraft");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void postInitClient() {}
|
||||||
|
|
||||||
|
public final void setLogger(Logger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playBattleMusic() {}
|
||||||
|
|
||||||
|
public void playSillyMusic() {}
|
||||||
|
|
||||||
|
public void stopMusic(boolean resumeMCSounds) {}
|
||||||
|
|
||||||
|
public void typeEnteredBattle(String type) {}
|
||||||
|
|
||||||
|
public void typeLeftBattle(String type) {}
|
||||||
|
|
||||||
|
public void displayString(String message) {}
|
||||||
|
|
||||||
|
public void displayComponent(Component textComponent) {}
|
||||||
|
|
||||||
|
public final boolean isServerRunning()
|
||||||
|
{
|
||||||
|
return battleManager != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battle getLocalBattle()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createLocalBattle(int id) {}
|
||||||
|
|
||||||
|
public final Set<AttackerViaBow> getAttackerViaBowSet()
|
||||||
|
{
|
||||||
|
return attackerViaBow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final BattleManager getBattleManager()
|
||||||
|
{
|
||||||
|
return battleManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setAttackingEntity(Entity entity)
|
||||||
|
{
|
||||||
|
attackingEntity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Entity getAttackingEntity()
|
||||||
|
{
|
||||||
|
return attackingEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setAttackingDamage(int damage)
|
||||||
|
{
|
||||||
|
attackingDamage = damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int getAttackingDamage()
|
||||||
|
{
|
||||||
|
return attackingDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Logger getLogger()
|
||||||
|
{
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Config getConfig()
|
||||||
|
{
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final EditingInfo getEditingInfo(int id)
|
||||||
|
{
|
||||||
|
return editingPlayers.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final EditingInfo setEditingPlayer(Player player)
|
||||||
|
{
|
||||||
|
return editingPlayers.put(player.getId(), new EditingInfo(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final EditingInfo removeEditingInfo(int id)
|
||||||
|
{
|
||||||
|
return editingPlayers.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getEntity(int id, ResourceKey<Level> dim) {
|
||||||
|
return ServerLifecycleHooks.getCurrentServer().getLevel(dim).getEntity(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <MSG> void handlePacket(final MSG msg, final IPayloadContext ctx) {}
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, Collection<Integer>> COLLECTION_INT_CODEC = ByteBufCodecs.INT.apply(ByteBufCodecs.collection(ArrayList::new));
|
||||||
|
|
||||||
|
public void showClientConfigGui() {}
|
||||||
|
|
||||||
|
public void pauseMCMusic() {}
|
||||||
|
public void resumeMCMusic() {}
|
||||||
|
}
|
1956
src/main/java/com/burnedkirby/TurnBasedMinecraft/common/Config.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.networking.PacketGeneralMessage;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.EntityTravelToDimensionEvent;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
public class DimensionChangedHandler {
|
||||||
|
@SubscribeEvent
|
||||||
|
public void dimensionChanged(EntityTravelToDimensionEvent event) {
|
||||||
|
if(event.getEntity().level().isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(TurnBasedMinecraftMod.proxy.getBattleManager().forceLeaveBattle(new EntityIDDimPair(event.getEntity()))
|
||||||
|
&& event.getEntity() instanceof ServerPlayer) {
|
||||||
|
PacketDistributor.sendToPlayer((ServerPlayer)event.getEntity(), new PacketGeneralMessage("Left battle due to moving to a different dimension"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
public class EditingInfo
|
||||||
|
{
|
||||||
|
public Player editor;
|
||||||
|
public EntityInfo entityInfo;
|
||||||
|
public boolean isPendingEntitySelection;
|
||||||
|
public boolean isEditingCustomName;
|
||||||
|
public boolean isEditingPlayer;
|
||||||
|
|
||||||
|
public EditingInfo()
|
||||||
|
{
|
||||||
|
editor = null;
|
||||||
|
entityInfo = null;
|
||||||
|
isPendingEntitySelection = true;
|
||||||
|
isEditingCustomName = false;
|
||||||
|
isEditingPlayer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditingInfo(Player player)
|
||||||
|
{
|
||||||
|
editor = player;
|
||||||
|
entityInfo = null;
|
||||||
|
isPendingEntitySelection = true;
|
||||||
|
isEditingCustomName = false;
|
||||||
|
isEditingPlayer = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class EntityIDDimPair {
|
||||||
|
public int id;
|
||||||
|
public ResourceKey<Level> dim;
|
||||||
|
|
||||||
|
EntityIDDimPair() {
|
||||||
|
id = 0;
|
||||||
|
// dim = Minecraft.getInstance().world.dimension();
|
||||||
|
dim = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityIDDimPair(int id, ResourceKey<Level> dim) {
|
||||||
|
this.id = id;
|
||||||
|
this.dim = dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityIDDimPair(Entity entity) {
|
||||||
|
id = entity.getId();
|
||||||
|
dim = entity.level().dimension();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getEntity() {
|
||||||
|
return TurnBasedMinecraftMod.proxy.getEntity(id, dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (id + dim.toString()).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if(other instanceof EntityIDDimPair) {
|
||||||
|
EntityIDDimPair otherPair = (EntityIDDimPair) other;
|
||||||
|
return otherPair.id == id && otherPair.dim == dim;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,543 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class EntityInfo
|
||||||
|
{
|
||||||
|
public Class classType;
|
||||||
|
public boolean ignoreBattle;
|
||||||
|
public int attackPower;
|
||||||
|
public int attackProbability;
|
||||||
|
public int attackVariance;
|
||||||
|
public Effect attackEffect;
|
||||||
|
public int attackEffectProbability;
|
||||||
|
public int defenseDamage;
|
||||||
|
public int defenseDamageProbability;
|
||||||
|
public int evasion;
|
||||||
|
public int speed;
|
||||||
|
public int hasteSpeed;
|
||||||
|
public int slowSpeed;
|
||||||
|
public String category;
|
||||||
|
public int decisionAttack;
|
||||||
|
public int decisionDefend;
|
||||||
|
public int decisionFlee;
|
||||||
|
public String customName;
|
||||||
|
public String playerName;
|
||||||
|
|
||||||
|
public enum Effect
|
||||||
|
{
|
||||||
|
SPEED,
|
||||||
|
SLOW,
|
||||||
|
HASTE,
|
||||||
|
MINING_FATIGUE,
|
||||||
|
STRENGTH,
|
||||||
|
JUMP_BOOST,
|
||||||
|
NAUSEA,
|
||||||
|
REGENERATION,
|
||||||
|
RESISTANCE,
|
||||||
|
FIRE_RESISTANCE,
|
||||||
|
WATER_BREATHING,
|
||||||
|
INVISIBILITY,
|
||||||
|
BLINDNESS,
|
||||||
|
NIGHT_VISION,
|
||||||
|
HUNGER,
|
||||||
|
WEAKNESS,
|
||||||
|
POISON,
|
||||||
|
WITHER,
|
||||||
|
HEALTH_BOOST,
|
||||||
|
ABSORPTION,
|
||||||
|
SATURATION,
|
||||||
|
GLOWING,
|
||||||
|
LEVITATION,
|
||||||
|
LUCK,
|
||||||
|
UNLUCK,
|
||||||
|
SLOW_FALLING,
|
||||||
|
CONDUIT_POWER,
|
||||||
|
DOLPHINS_GRACE,
|
||||||
|
BAD_OMEN,
|
||||||
|
FIRE,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
|
public static Effect fromString(String c)
|
||||||
|
{
|
||||||
|
c = c.toLowerCase();
|
||||||
|
if(c.equals("speed")) {
|
||||||
|
return SPEED;
|
||||||
|
} else if(c.equals("slow")) {
|
||||||
|
return SLOW;
|
||||||
|
} else if(c.equals("haste")) {
|
||||||
|
return HASTE;
|
||||||
|
} else if(c.equals("mining_fatigue") || c.equals("fatigue")) {
|
||||||
|
return MINING_FATIGUE;
|
||||||
|
} else if(c.equals("strength")) {
|
||||||
|
return STRENGTH;
|
||||||
|
} else if(c.equals("jump_boost")) {
|
||||||
|
return JUMP_BOOST;
|
||||||
|
} else if(c.equals("nausea")) {
|
||||||
|
return NAUSEA;
|
||||||
|
} else if(c.equals("regeneration")) {
|
||||||
|
return REGENERATION;
|
||||||
|
} else if(c.equals("resistance")) {
|
||||||
|
return RESISTANCE;
|
||||||
|
} else if(c.equals("fire_resistance")) {
|
||||||
|
return FIRE_RESISTANCE;
|
||||||
|
} else if(c.equals("water_breathing")) {
|
||||||
|
return WATER_BREATHING;
|
||||||
|
} else if(c.equals("invisibility")) {
|
||||||
|
return INVISIBILITY;
|
||||||
|
} else if(c.equals("blindness") || c.equals("blind")) {
|
||||||
|
return BLINDNESS;
|
||||||
|
} else if(c.equals("night_vision")) {
|
||||||
|
return NIGHT_VISION;
|
||||||
|
} else if(c.equals("hunger")) {
|
||||||
|
return HUNGER;
|
||||||
|
} else if(c.equals("weakness")) {
|
||||||
|
return WEAKNESS;
|
||||||
|
} else if(c.equals("poison")) {
|
||||||
|
return POISON;
|
||||||
|
} else if(c.equals("wither")) {
|
||||||
|
return WITHER;
|
||||||
|
} else if(c.equals("health_boost")) {
|
||||||
|
return HEALTH_BOOST;
|
||||||
|
} else if(c.equals("absorption")) {
|
||||||
|
return ABSORPTION;
|
||||||
|
} else if(c.equals("saturation")) {
|
||||||
|
return SATURATION;
|
||||||
|
} else if(c.equals("glowing")) {
|
||||||
|
return GLOWING;
|
||||||
|
} else if(c.equals("levitation")) {
|
||||||
|
return LEVITATION;
|
||||||
|
} else if(c.equals("luck")) {
|
||||||
|
return LUCK;
|
||||||
|
} else if(c.equals("unluck")) {
|
||||||
|
return UNLUCK;
|
||||||
|
} else if(c.equals("slow_falling")) {
|
||||||
|
return SLOW_FALLING;
|
||||||
|
} else if(c.equals("conduit_power")) {
|
||||||
|
return CONDUIT_POWER;
|
||||||
|
} else if(c.equals("dolphins_grace")) {
|
||||||
|
return DOLPHINS_GRACE;
|
||||||
|
} else if(c.equals("bad_omen")) {
|
||||||
|
return BAD_OMEN;
|
||||||
|
} else if(c.equals("fire")) {
|
||||||
|
return FIRE;
|
||||||
|
} else {
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
switch(this)
|
||||||
|
{
|
||||||
|
case SPEED:
|
||||||
|
return "speed";
|
||||||
|
case SLOW:
|
||||||
|
return "slow";
|
||||||
|
case HASTE:
|
||||||
|
return "haste";
|
||||||
|
case MINING_FATIGUE:
|
||||||
|
return "mining_fatigue";
|
||||||
|
case STRENGTH:
|
||||||
|
return "strength";
|
||||||
|
case JUMP_BOOST:
|
||||||
|
return "jump_boost";
|
||||||
|
case NAUSEA:
|
||||||
|
return "nausea";
|
||||||
|
case REGENERATION:
|
||||||
|
return "regeneration";
|
||||||
|
case RESISTANCE:
|
||||||
|
return "resistance";
|
||||||
|
case FIRE_RESISTANCE:
|
||||||
|
return "fire_resistance";
|
||||||
|
case WATER_BREATHING:
|
||||||
|
return "water_breathing";
|
||||||
|
case INVISIBILITY:
|
||||||
|
return "invisibility";
|
||||||
|
case BLINDNESS:
|
||||||
|
return "blindness";
|
||||||
|
case NIGHT_VISION:
|
||||||
|
return "night_vision";
|
||||||
|
case HUNGER:
|
||||||
|
return "hunger";
|
||||||
|
case WEAKNESS:
|
||||||
|
return "weakness";
|
||||||
|
case POISON:
|
||||||
|
return "poison";
|
||||||
|
case WITHER:
|
||||||
|
return "wither";
|
||||||
|
case HEALTH_BOOST:
|
||||||
|
return "health_boost";
|
||||||
|
case ABSORPTION:
|
||||||
|
return "absorption";
|
||||||
|
case SATURATION:
|
||||||
|
return "saturation";
|
||||||
|
case GLOWING:
|
||||||
|
return "glowing";
|
||||||
|
case LEVITATION:
|
||||||
|
return "levitation";
|
||||||
|
case LUCK:
|
||||||
|
return "luck";
|
||||||
|
case UNLUCK:
|
||||||
|
return "unluck";
|
||||||
|
case SLOW_FALLING:
|
||||||
|
return "slow_falling";
|
||||||
|
case CONDUIT_POWER:
|
||||||
|
return "conduit_power";
|
||||||
|
case DOLPHINS_GRACE:
|
||||||
|
return "dolphins_grace";
|
||||||
|
case BAD_OMEN:
|
||||||
|
return "bad_omen";
|
||||||
|
case FIRE:
|
||||||
|
return "fire";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobEffectInstance getPotionEffect()
|
||||||
|
{
|
||||||
|
return getPotionEffect(20 * 7, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobEffectInstance getPotionEffect(int duration, int amplifier) {
|
||||||
|
switch(this) {
|
||||||
|
case SPEED:
|
||||||
|
return new MobEffectInstance(MobEffects.MOVEMENT_SPEED, duration, amplifier);
|
||||||
|
case SLOW:
|
||||||
|
return new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, duration, amplifier);
|
||||||
|
case HASTE:
|
||||||
|
return new MobEffectInstance(MobEffects.DIG_SPEED, duration, amplifier);
|
||||||
|
case MINING_FATIGUE:
|
||||||
|
return new MobEffectInstance(MobEffects.DIG_SLOWDOWN, duration, amplifier);
|
||||||
|
case STRENGTH:
|
||||||
|
return new MobEffectInstance(MobEffects.DAMAGE_BOOST, duration, amplifier);
|
||||||
|
case JUMP_BOOST:
|
||||||
|
return new MobEffectInstance(MobEffects.JUMP, duration, amplifier);
|
||||||
|
case NAUSEA:
|
||||||
|
return new MobEffectInstance(MobEffects.CONFUSION, duration, amplifier);
|
||||||
|
case REGENERATION:
|
||||||
|
return new MobEffectInstance(MobEffects.REGENERATION, duration, amplifier);
|
||||||
|
case RESISTANCE:
|
||||||
|
return new MobEffectInstance(MobEffects.DAMAGE_RESISTANCE, duration, amplifier);
|
||||||
|
case FIRE_RESISTANCE:
|
||||||
|
return new MobEffectInstance(MobEffects.FIRE_RESISTANCE, duration, amplifier);
|
||||||
|
case WATER_BREATHING:
|
||||||
|
return new MobEffectInstance(MobEffects.WATER_BREATHING, duration, amplifier);
|
||||||
|
case INVISIBILITY:
|
||||||
|
return new MobEffectInstance(MobEffects.INVISIBILITY, duration, amplifier);
|
||||||
|
case BLINDNESS:
|
||||||
|
return new MobEffectInstance(MobEffects.BLINDNESS, duration, amplifier);
|
||||||
|
case NIGHT_VISION:
|
||||||
|
return new MobEffectInstance(MobEffects.NIGHT_VISION, duration, amplifier);
|
||||||
|
case HUNGER:
|
||||||
|
return new MobEffectInstance(MobEffects.HUNGER, duration, amplifier);
|
||||||
|
case WEAKNESS:
|
||||||
|
return new MobEffectInstance(MobEffects.WEAKNESS, duration, amplifier);
|
||||||
|
case POISON:
|
||||||
|
return new MobEffectInstance(MobEffects.POISON, duration, amplifier);
|
||||||
|
case WITHER:
|
||||||
|
return new MobEffectInstance(MobEffects.WITHER, duration, amplifier);
|
||||||
|
case HEALTH_BOOST:
|
||||||
|
return new MobEffectInstance(MobEffects.HEALTH_BOOST, duration, amplifier);
|
||||||
|
case ABSORPTION:
|
||||||
|
return new MobEffectInstance(MobEffects.ABSORPTION, duration, amplifier);
|
||||||
|
case SATURATION:
|
||||||
|
return new MobEffectInstance(MobEffects.SATURATION, duration, amplifier);
|
||||||
|
case GLOWING:
|
||||||
|
return new MobEffectInstance(MobEffects.GLOWING, duration, amplifier);
|
||||||
|
case LEVITATION:
|
||||||
|
return new MobEffectInstance(MobEffects.LEVITATION, duration, amplifier);
|
||||||
|
case LUCK:
|
||||||
|
return new MobEffectInstance(MobEffects.LUCK, duration, amplifier);
|
||||||
|
case UNLUCK:
|
||||||
|
return new MobEffectInstance(MobEffects.UNLUCK, duration, amplifier);
|
||||||
|
case SLOW_FALLING:
|
||||||
|
return new MobEffectInstance(MobEffects.SLOW_FALLING, duration, amplifier);
|
||||||
|
case CONDUIT_POWER:
|
||||||
|
return new MobEffectInstance(MobEffects.CONDUIT_POWER, duration, amplifier);
|
||||||
|
case DOLPHINS_GRACE:
|
||||||
|
return new MobEffectInstance(MobEffects.DOLPHINS_GRACE, duration, amplifier);
|
||||||
|
case BAD_OMEN:
|
||||||
|
return new MobEffectInstance(MobEffects.BAD_OMEN, duration, amplifier);
|
||||||
|
case FIRE:
|
||||||
|
// FIRE is not a PotionEffect and must be applied directly to the Entity
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyEffectToEntity(LivingEntity entity)
|
||||||
|
{
|
||||||
|
applyEffectToEntity(entity, 20 * 12, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyEffectToEntity(LivingEntity entity, int duration, int amplifier)
|
||||||
|
{
|
||||||
|
if(this == FIRE)
|
||||||
|
{
|
||||||
|
entity.setRemainingFireTicks(duration / 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(this != UNKNOWN)
|
||||||
|
{
|
||||||
|
entity.addEffect(getPotionEffect(duration, amplifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAffectedString()
|
||||||
|
{
|
||||||
|
switch(this)
|
||||||
|
{
|
||||||
|
case SPEED:
|
||||||
|
return "made faster";
|
||||||
|
case SLOW:
|
||||||
|
return "made slower";
|
||||||
|
case HASTE:
|
||||||
|
return "made hastier";
|
||||||
|
case MINING_FATIGUE:
|
||||||
|
return "fatigued";
|
||||||
|
case STRENGTH:
|
||||||
|
return "strengthened";
|
||||||
|
case JUMP_BOOST:
|
||||||
|
return "jump boosted";
|
||||||
|
case NAUSEA:
|
||||||
|
return "made nauseous";
|
||||||
|
case REGENERATION:
|
||||||
|
return "given regeneration";
|
||||||
|
case RESISTANCE:
|
||||||
|
return "given resistance";
|
||||||
|
case FIRE_RESISTANCE:
|
||||||
|
return "given fire resistance";
|
||||||
|
case WATER_BREATHING:
|
||||||
|
return "made able to breathe underwater";
|
||||||
|
case INVISIBILITY:
|
||||||
|
return "given invisibility";
|
||||||
|
case BLINDNESS:
|
||||||
|
return "made blind";
|
||||||
|
case NIGHT_VISION:
|
||||||
|
return "given night vision";
|
||||||
|
case HUNGER:
|
||||||
|
return "made hungry";
|
||||||
|
case WEAKNESS:
|
||||||
|
return "made weak";
|
||||||
|
case POISON:
|
||||||
|
return "poisoned";
|
||||||
|
case WITHER:
|
||||||
|
return "withered";
|
||||||
|
case HEALTH_BOOST:
|
||||||
|
return "given more health";
|
||||||
|
case ABSORPTION:
|
||||||
|
return "given absorption";
|
||||||
|
case SATURATION:
|
||||||
|
return "given saturation";
|
||||||
|
case GLOWING:
|
||||||
|
return "made to glow";
|
||||||
|
case LEVITATION:
|
||||||
|
return "made to levitate";
|
||||||
|
case LUCK:
|
||||||
|
return "given luck";
|
||||||
|
case UNLUCK:
|
||||||
|
return "made unlucky";
|
||||||
|
case SLOW_FALLING:
|
||||||
|
return "falls slower";
|
||||||
|
case CONDUIT_POWER:
|
||||||
|
return "made able to live underwater";
|
||||||
|
case BAD_OMEN:
|
||||||
|
return "feels a bad omen";
|
||||||
|
case FIRE:
|
||||||
|
return "set on fire";
|
||||||
|
default:
|
||||||
|
return "given unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityInfo()
|
||||||
|
{
|
||||||
|
classType = null;
|
||||||
|
ignoreBattle = false;
|
||||||
|
attackPower = 0;
|
||||||
|
attackProbability = 70;
|
||||||
|
attackVariance = 0;
|
||||||
|
attackEffect = Effect.UNKNOWN;
|
||||||
|
attackEffectProbability = 50;
|
||||||
|
defenseDamage = 0;
|
||||||
|
defenseDamageProbability = 0;
|
||||||
|
evasion = 15;
|
||||||
|
speed = 50;
|
||||||
|
hasteSpeed = 80;
|
||||||
|
slowSpeed = 20;
|
||||||
|
category = "unknown";
|
||||||
|
decisionAttack = 70;
|
||||||
|
decisionDefend = 20;
|
||||||
|
decisionFlee = 10;
|
||||||
|
customName = new String();
|
||||||
|
playerName = new String();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityInfo(Class classType, boolean ignoreBattle, int attackPower, int attackProbability, int attackVariance,
|
||||||
|
Effect attackEffect, int attackEffectProbability, int defenseDamage, int defenseDamageProbability,
|
||||||
|
int evasion, int speed, int hasteSpeed, int slowSpeed, String category, int decisionAttack, int decisionDefend, int decisionFlee,
|
||||||
|
String customName, String playerName) {
|
||||||
|
this.classType = classType;
|
||||||
|
this.ignoreBattle = ignoreBattle;
|
||||||
|
this.attackPower = attackPower;
|
||||||
|
this.attackProbability = attackProbability;
|
||||||
|
this.attackVariance = attackVariance;
|
||||||
|
this.attackEffect = attackEffect;
|
||||||
|
this.attackEffectProbability = attackEffectProbability;
|
||||||
|
this.defenseDamage = defenseDamage;
|
||||||
|
this.defenseDamageProbability = defenseDamageProbability;
|
||||||
|
this.evasion = evasion;
|
||||||
|
this.speed = speed;
|
||||||
|
this.hasteSpeed = hasteSpeed;
|
||||||
|
this.slowSpeed = slowSpeed;
|
||||||
|
this.category = category;
|
||||||
|
this.decisionAttack = decisionAttack;
|
||||||
|
this.decisionDefend = decisionDefend;
|
||||||
|
this.decisionFlee = decisionFlee;
|
||||||
|
this.customName = customName;
|
||||||
|
this.playerName = playerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityInfo clone()
|
||||||
|
{
|
||||||
|
EntityInfo newEntityInfo = new EntityInfo();
|
||||||
|
newEntityInfo.classType = classType;
|
||||||
|
newEntityInfo.ignoreBattle = ignoreBattle;
|
||||||
|
newEntityInfo.attackPower = attackPower;
|
||||||
|
newEntityInfo.attackProbability = attackProbability;
|
||||||
|
newEntityInfo.attackVariance = attackVariance;
|
||||||
|
newEntityInfo.attackEffect = attackEffect;
|
||||||
|
newEntityInfo.attackEffectProbability = attackEffectProbability;
|
||||||
|
newEntityInfo.defenseDamage = defenseDamage;
|
||||||
|
newEntityInfo.defenseDamageProbability = defenseDamageProbability;
|
||||||
|
newEntityInfo.evasion = evasion;
|
||||||
|
newEntityInfo.speed = speed;
|
||||||
|
newEntityInfo.hasteSpeed = hasteSpeed;
|
||||||
|
newEntityInfo.slowSpeed = slowSpeed;
|
||||||
|
newEntityInfo.category = category;
|
||||||
|
newEntityInfo.decisionAttack = decisionAttack;
|
||||||
|
newEntityInfo.decisionDefend = decisionDefend;
|
||||||
|
newEntityInfo.decisionFlee = decisionFlee;
|
||||||
|
newEntityInfo.customName = customName;
|
||||||
|
newEntityInfo.playerName = playerName;
|
||||||
|
return newEntityInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityInfo(ByteBuf buffer) {
|
||||||
|
int name_bytes_len = buffer.readInt();
|
||||||
|
if (name_bytes_len > 0) {
|
||||||
|
ByteBuf name_bytes = buffer.readBytes(name_bytes_len);
|
||||||
|
try {
|
||||||
|
classType = Class.forName(name_bytes.toString(StandardCharsets.UTF_8));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
TurnBasedMinecraftMod.logger.warn("Failed to decode EntityInfo.classType", e);
|
||||||
|
classType = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
classType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ignoreBattle = buffer.readBoolean();
|
||||||
|
attackPower = buffer.readInt();
|
||||||
|
attackProbability = buffer.readInt();
|
||||||
|
attackVariance = buffer.readInt();
|
||||||
|
|
||||||
|
int effect_len = buffer.readInt();
|
||||||
|
ByteBuf effect_bytes = buffer.readBytes(effect_len);
|
||||||
|
attackEffect = Effect.fromString(effect_bytes.toString(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
attackEffectProbability = buffer.readInt();
|
||||||
|
defenseDamage = buffer.readInt();
|
||||||
|
defenseDamageProbability = buffer.readInt();
|
||||||
|
evasion = buffer.readInt();
|
||||||
|
speed = buffer.readInt();
|
||||||
|
hasteSpeed = buffer.readInt();
|
||||||
|
slowSpeed = buffer.readInt();
|
||||||
|
|
||||||
|
int category_len = buffer.readInt();
|
||||||
|
ByteBuf category_bytes = buffer.readBytes(category_len);
|
||||||
|
category = category_bytes.toString(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
decisionAttack = buffer.readInt();
|
||||||
|
decisionDefend = buffer.readInt();
|
||||||
|
decisionFlee = buffer.readInt();
|
||||||
|
|
||||||
|
int custom_len = buffer.readInt();
|
||||||
|
if (custom_len > 0) {
|
||||||
|
ByteBuf custom_bytes = buffer.readBytes(custom_len);
|
||||||
|
customName = custom_bytes.toString(StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
customName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int player_len = buffer.readInt();
|
||||||
|
if (player_len > 0) {
|
||||||
|
ByteBuf player_bytes = buffer.readBytes(player_len);
|
||||||
|
playerName = player_bytes.toString(StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
playerName = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(ByteBuf buffer) {
|
||||||
|
if (classType == null) {
|
||||||
|
buffer.writeInt(0);
|
||||||
|
} else {
|
||||||
|
String name = classType.getName();
|
||||||
|
byte[] name_bytes = name.getBytes(StandardCharsets.UTF_8);
|
||||||
|
buffer.writeInt(name_bytes.length);
|
||||||
|
buffer.writeBytes(name_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.writeBoolean(ignoreBattle);
|
||||||
|
buffer.writeInt(attackPower);
|
||||||
|
buffer.writeInt(attackProbability);
|
||||||
|
buffer.writeInt(attackVariance);
|
||||||
|
|
||||||
|
String effect_name = attackEffect.toString();
|
||||||
|
byte[] effect_bytes = effect_name.getBytes(StandardCharsets.UTF_8);
|
||||||
|
buffer.writeInt(effect_bytes.length);
|
||||||
|
buffer.writeBytes(effect_bytes);
|
||||||
|
|
||||||
|
buffer.writeInt(attackEffectProbability);
|
||||||
|
buffer.writeInt(defenseDamage);
|
||||||
|
buffer.writeInt(defenseDamageProbability);
|
||||||
|
buffer.writeInt(evasion);
|
||||||
|
buffer.writeInt(speed);
|
||||||
|
buffer.writeInt(hasteSpeed);
|
||||||
|
buffer.writeInt(slowSpeed);
|
||||||
|
|
||||||
|
byte[] category_bytes = category.getBytes(StandardCharsets.UTF_8);
|
||||||
|
buffer.writeInt(category_bytes.length);
|
||||||
|
buffer.writeBytes(category_bytes);
|
||||||
|
|
||||||
|
buffer.writeInt(decisionAttack);
|
||||||
|
buffer.writeInt(decisionDefend);
|
||||||
|
buffer.writeInt(decisionFlee);
|
||||||
|
|
||||||
|
if (customName.isEmpty()) {
|
||||||
|
buffer.writeInt(0);
|
||||||
|
} else {
|
||||||
|
byte[] custom_bytes = customName.getBytes(StandardCharsets.UTF_8);
|
||||||
|
buffer.writeInt(custom_bytes.length);
|
||||||
|
buffer.writeBytes(custom_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerName.isEmpty()) {
|
||||||
|
buffer.writeInt(0);
|
||||||
|
} else {
|
||||||
|
byte[] player_bytes = playerName.getBytes(StandardCharsets.UTF_8);
|
||||||
|
buffer.writeInt(player_bytes.length);
|
||||||
|
buffer.writeBytes(player_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent;
|
||||||
|
|
||||||
|
public class HurtEventHandler {
|
||||||
|
@SubscribeEvent
|
||||||
|
public void handleHurtEvent(LivingIncomingDamageEvent event) {
|
||||||
|
CommonProxy proxy = TurnBasedMinecraftMod.proxy;
|
||||||
|
if (event.getEntity().level().isClientSide || proxy.getBattleManager() == null) {
|
||||||
|
return;
|
||||||
|
} else if (proxy.getConfig().getIgnoreHurtDamageSources().contains(event.getSource().getMsgId()) && proxy.getBattleManager().isInBattle(event.getEntity())) {
|
||||||
|
event.setCanceled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
|
||||||
|
|
||||||
|
public class PlayerJoinEventHandler
|
||||||
|
{
|
||||||
|
@SubscribeEvent
|
||||||
|
public void entityJoinHandler(EntityJoinLevelEvent event)
|
||||||
|
{
|
||||||
|
if(event.getLevel().isClientSide)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(event.getEntity() instanceof Player && TurnBasedMinecraftMod.proxy.getConfig().getBattleDisabledForAll())
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.getConfig().addBattleIgnoringPlayer(event.getEntity().getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.client.ClientConfig;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.client.ClientConfigGui;
|
||||||
|
import net.neoforged.api.distmarker.Dist;
|
||||||
|
import net.neoforged.fml.ModContainer;
|
||||||
|
import net.neoforged.fml.common.Mod;
|
||||||
|
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
||||||
|
|
||||||
|
@Mod(value = TurnBasedMinecraftMod.MODID, dist = Dist.CLIENT)
|
||||||
|
public class TBMM_Client {
|
||||||
|
public TBMM_Client(ModContainer container) {
|
||||||
|
container.registerExtensionPoint(IConfigScreenFactory.class, ClientConfigGui::new);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common;
|
||||||
|
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ArrowItem;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class Utility
|
||||||
|
{
|
||||||
|
public static float yawDirection(double posX, double posZ, double targetX, double targetZ)
|
||||||
|
{
|
||||||
|
double radians = Math.atan2(targetZ - posZ, targetX - posX);
|
||||||
|
radians = (radians - Math.PI / 2.0);
|
||||||
|
if(radians < 0.0)
|
||||||
|
{
|
||||||
|
radians += Math.PI * 2.0;
|
||||||
|
}
|
||||||
|
return (float)(radians * 180.0 / Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float pitchDirection(double posX, double posY, double posZ, double targetX, double targetY, double targetZ)
|
||||||
|
{
|
||||||
|
double diffX = targetX - posX;
|
||||||
|
double diffY = targetY - posY;
|
||||||
|
double diffZ = targetZ - posZ;
|
||||||
|
double distance = Math.sqrt(diffX * diffX + diffZ * diffZ);
|
||||||
|
if(Math.abs(diffY) < 0.1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (float)(-Math.atan(diffY / distance) * 180.0 / Math.PI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean doesPlayerHaveArrows(Player player)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < player.getInventory().getContainerSize(); ++i)
|
||||||
|
{
|
||||||
|
if(player.getInventory().getItem(i).getItem() instanceof ArrowItem)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double distanceBetweenEntities(Entity a, Entity b)
|
||||||
|
{
|
||||||
|
return Math.sqrt(Math.pow(a.getX() - b.getX(), 2.0) + Math.pow(a.getY()- b.getY(), 2.0) + Math.pow(a.getZ()- b.getZ(), 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serializeDimension(ResourceKey<Level> dimObject) {
|
||||||
|
return dimObject.registry().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceKey<Level> deserializeDimension(String dimString) {
|
||||||
|
ResourceLocation dimRes = ResourceLocation.parse(dimString);
|
||||||
|
return ResourceKey.create(Registries.DIMENSION, dimRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isItemEdible(ItemStack itemStack, @Nullable LivingEntity entity) {
|
||||||
|
return itemStack.get(DataComponents.CONSUMABLE) != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Battle;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record PacketBattleDecision(int battleID, int decision, int targetIDorItemID) implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketBattleDecision> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetbattledecision"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketBattleDecision> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleDecision::battleID,
|
||||||
|
ByteBufCodecs.VAR_INT,
|
||||||
|
PacketBattleDecision::decision,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleDecision::targetIDorItemID,
|
||||||
|
PacketBattleDecision::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketBattleDecision> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketBattleDecision pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
Battle b = TurnBasedMinecraftMod.proxy.getBattleManager().getBattleByID(pkt.battleID);
|
||||||
|
if(b != null) {
|
||||||
|
Player player = ctx.player();
|
||||||
|
b.setDecision(player.getId(), Battle.Decision.valueOf(pkt.decision), pkt.targetIDorItemID);
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketBattleDecision! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.CommonProxy;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public record PacketBattleInfo(int battleID, Collection<Integer> sideA, Collection<Integer> sideB, long decisionNanos, long maxDecisionNanos, boolean turnTimerEnabled) implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketBattleInfo> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetbattleinfo"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketBattleInfo> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleInfo::battleID,
|
||||||
|
CommonProxy.COLLECTION_INT_CODEC,
|
||||||
|
PacketBattleInfo::sideA,
|
||||||
|
CommonProxy.COLLECTION_INT_CODEC,
|
||||||
|
PacketBattleInfo::sideB,
|
||||||
|
ByteBufCodecs.VAR_LONG,
|
||||||
|
PacketBattleInfo::decisionNanos,
|
||||||
|
ByteBufCodecs.VAR_LONG,
|
||||||
|
PacketBattleInfo::maxDecisionNanos,
|
||||||
|
ByteBufCodecs.BOOL,
|
||||||
|
PacketBattleInfo::turnTimerEnabled,
|
||||||
|
PacketBattleInfo::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketBattleInfo> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketBattleInfo pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if(TurnBasedMinecraftMod.proxy.getLocalBattle() == null)
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.createLocalBattle(pkt.battleID);
|
||||||
|
}
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().clearCombatants();
|
||||||
|
for(Integer id : pkt.sideA)
|
||||||
|
{
|
||||||
|
Entity e = Minecraft.getInstance().level.getEntity(id);
|
||||||
|
if(e != null)
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().addCombatantToSideA(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(Integer id : pkt.sideB)
|
||||||
|
{
|
||||||
|
Entity e = Minecraft.getInstance().level.getEntity(id);
|
||||||
|
if(e != null)
|
||||||
|
{
|
||||||
|
TurnBasedMinecraftMod.proxy.getLocalBattle().addCombatantToSideB(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiAsGui();
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiTime((int)(pkt.decisionNanos / 1000000000L));
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiBattleChanged();
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiTurnTimerEnabled(pkt.turnTimerEnabled);
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiTurnTimerMax((int)(pkt.maxDecisionNanos / 1000000000L));
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketBattleInfo! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Utility;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.neoforged.fml.loading.FMLEnvironment;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PacketBattleMessage implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketBattleMessage> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetbattlemessage"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketBattleMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.VAR_INT.map(MessageType::valueOf, MessageType::getValue),
|
||||||
|
PacketBattleMessage::getMessageType,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleMessage::getEntityIDFrom,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleMessage::getEntityIDTo,
|
||||||
|
ByteBufCodecs.STRING_UTF8.map(Utility::deserializeDimension, Utility::serializeDimension),
|
||||||
|
PacketBattleMessage::getDimension,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleMessage::getAmount,
|
||||||
|
ByteBufCodecs.STRING_UTF8,
|
||||||
|
PacketBattleMessage::getCustom,
|
||||||
|
PacketBattleMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
ENTERED(0),
|
||||||
|
FLEE(1),
|
||||||
|
DIED(2),
|
||||||
|
ENDED(3),
|
||||||
|
ATTACK(4),
|
||||||
|
DEFEND(5),
|
||||||
|
DEFENSE_DAMAGE(6),
|
||||||
|
MISS(7),
|
||||||
|
DEFENDING(8),
|
||||||
|
DID_NOTHING(9),
|
||||||
|
USED_ITEM(10),
|
||||||
|
TURN_BEGIN(11),
|
||||||
|
TURN_END(12),
|
||||||
|
SWITCHED_ITEM(13),
|
||||||
|
WAS_AFFECTED(14),
|
||||||
|
BECAME_CREATIVE(15),
|
||||||
|
FIRED_ARROW(16),
|
||||||
|
ARROW_HIT(17),
|
||||||
|
BOW_NO_AMMO(18),
|
||||||
|
CREEPER_WAIT(19),
|
||||||
|
CREEPER_WAIT_FINAL(20),
|
||||||
|
CREEPER_EXPLODE(21),
|
||||||
|
CROSSBOW_NO_AMMO(22);
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
private static Map<Integer, MessageType> map = new HashMap<Integer, MessageType>();
|
||||||
|
|
||||||
|
MessageType(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for(MessageType type : MessageType.values())
|
||||||
|
{
|
||||||
|
map.put(type.getValue(), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageType valueOf(int value)
|
||||||
|
{
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UsedItemAction
|
||||||
|
{
|
||||||
|
USED_NOTHING(0),
|
||||||
|
USED_INVALID(1),
|
||||||
|
USED_FOOD(2),
|
||||||
|
USED_POTION(3);
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
private static Map<Integer, UsedItemAction> map = new HashMap<Integer, UsedItemAction>();
|
||||||
|
|
||||||
|
UsedItemAction(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for(UsedItemAction type : UsedItemAction.values())
|
||||||
|
{
|
||||||
|
map.put(type.getValue(), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UsedItemAction valueOf(int value)
|
||||||
|
{
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageType messageType;
|
||||||
|
int entityIDFrom;
|
||||||
|
int entityIDTo;
|
||||||
|
int amount;
|
||||||
|
String custom;
|
||||||
|
ResourceKey<Level> dimension;
|
||||||
|
|
||||||
|
public MessageType getMessageType() {
|
||||||
|
return messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEntityIDFrom() {
|
||||||
|
return entityIDFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEntityIDTo() {
|
||||||
|
return entityIDTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustom() {
|
||||||
|
return custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceKey<Level> getDimension() {
|
||||||
|
return dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDimensionSerialized() {
|
||||||
|
return Utility.serializeDimension(dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketBattleMessage() { custom = new String(); }
|
||||||
|
|
||||||
|
public PacketBattleMessage(MessageType messageType, int entityIDFrom, int entityIDTo, ResourceKey<Level> dimension, int amount)
|
||||||
|
{
|
||||||
|
this.messageType = messageType;
|
||||||
|
this.entityIDFrom = entityIDFrom;
|
||||||
|
this.entityIDTo = entityIDTo;
|
||||||
|
this.dimension = dimension;
|
||||||
|
this.amount = amount;
|
||||||
|
custom = new String();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketBattleMessage(MessageType messageType, int entityIDFrom, int entityIDTo, ResourceKey<Level> dimension, int amount, String custom)
|
||||||
|
{
|
||||||
|
this.messageType = messageType;
|
||||||
|
this.entityIDFrom = entityIDFrom;
|
||||||
|
this.entityIDTo = entityIDTo;
|
||||||
|
this.dimension = dimension;
|
||||||
|
this.amount = amount;
|
||||||
|
this.custom = custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketBattleMessage> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketBattleMessage pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if (FMLEnvironment.dist.isClient()) {
|
||||||
|
TurnBasedMinecraftMod.proxy.handlePacket(pkt, ctx);
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketBattleMessage! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record PacketBattlePing(int battleID, int remainingSeconds) implements CustomPacketPayload {
|
||||||
|
public static final CustomPacketPayload.Type<PacketBattlePing> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetbattleping"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketBattlePing> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattlePing::battleID,
|
||||||
|
ByteBufCodecs.VAR_INT,
|
||||||
|
PacketBattlePing::remainingSeconds,
|
||||||
|
PacketBattlePing::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketBattlePing> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketBattlePing pkt, IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if (TurnBasedMinecraftMod.proxy.getLocalBattle() != null) {
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiAsGui();
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiBattleChanged();
|
||||||
|
TurnBasedMinecraftMod.proxy.setBattleGuiTime(pkt.remainingSeconds);
|
||||||
|
TurnBasedMinecraftMod.proxy.pauseMCMusic();
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketBattlePing! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.Battle;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record PacketBattleRequestInfo(int battleID) implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketBattleRequestInfo> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetbattlerequestinfo"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketBattleRequestInfo> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
PacketBattleRequestInfo::battleID,
|
||||||
|
PacketBattleRequestInfo::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public PacketBattleRequestInfo(int battleID)
|
||||||
|
{
|
||||||
|
this.battleID = battleID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketBattleRequestInfo> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketBattleRequestInfo pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
Battle b = TurnBasedMinecraftMod.proxy.getBattleManager().getBattleByID(pkt.battleID);
|
||||||
|
if(b == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.reply(new PacketBattleInfo(
|
||||||
|
b.getId(),
|
||||||
|
b.getSideAIDs(),
|
||||||
|
b.getSideBIDs(),
|
||||||
|
b.getTimerNanos(),
|
||||||
|
TurnBasedMinecraftMod.proxy.getConfig().getDecisionDurationNanos(),
|
||||||
|
!TurnBasedMinecraftMod.proxy.getConfig().isBattleDecisionDurationForever()));
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketBattleRequestInfo! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.fml.loading.FMLEnvironment;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record PacketClientGUI(int reserved) implements CustomPacketPayload {
|
||||||
|
public static final CustomPacketPayload.Type<PacketClientGUI> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetclientgui"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketClientGUI> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.VAR_INT,
|
||||||
|
PacketClientGUI::reserved,
|
||||||
|
PacketClientGUI::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketClientGUI> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketClientGUI pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if (FMLEnvironment.dist.isClient()) {
|
||||||
|
TurnBasedMinecraftMod.proxy.showClientConfigGui();
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketClientGUI! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.EntityInfo;
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.fml.loading.FMLEnvironment;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PacketEditingMessage implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketEditingMessage> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packeteditingmessage"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketEditingMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.INT.map(Type::valueOf, Type::getValue),
|
||||||
|
PacketEditingMessage::getType,
|
||||||
|
StreamCodec.ofMember(EntityInfo::encode, EntityInfo::new),
|
||||||
|
PacketEditingMessage::getEntityInfo,
|
||||||
|
PacketEditingMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomPacketPayload.Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
ATTACK_ENTITY(0),
|
||||||
|
PICK_EDIT(1),
|
||||||
|
EDIT_IGNORE_BATTLE(2),
|
||||||
|
EDIT_ATTACK_POWER(3),
|
||||||
|
EDIT_ATTACK_PROBABILITY(4),
|
||||||
|
EDIT_ATTACK_VARIANCE(5),
|
||||||
|
EDIT_ATTACK_EFFECT(6),
|
||||||
|
EDIT_ATTACK_EFFECT_PROBABILITY(7),
|
||||||
|
EDIT_DEFENSE_DAMAGE(8),
|
||||||
|
EDIT_DEFENSE_DAMAGE_PROBABILITY(9),
|
||||||
|
EDIT_EVASION(10),
|
||||||
|
EDIT_SPEED(11),
|
||||||
|
EDIT_HASTE_SPEED(18),
|
||||||
|
EDIT_SLOW_SPEED(19),
|
||||||
|
EDIT_CATEGORY(12),
|
||||||
|
EDIT_DECISION_ATTACK(13),
|
||||||
|
EDIT_DECISION_DEFEND(14),
|
||||||
|
EDIT_DECISION_FLEE(15),
|
||||||
|
SERVER_EDIT(16),
|
||||||
|
PICK_PLAYER(17);
|
||||||
|
|
||||||
|
Type(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, Type> map;
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
map = new HashMap<Integer, Type>();
|
||||||
|
for(Type t : values())
|
||||||
|
{
|
||||||
|
map.put(t.value, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type valueOf(int value)
|
||||||
|
{
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type = Type.ATTACK_ENTITY;
|
||||||
|
EntityInfo entityInfo = new EntityInfo();
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityInfo getEntityInfo() {
|
||||||
|
return entityInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketEditingMessage() {}
|
||||||
|
|
||||||
|
public PacketEditingMessage(Type type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketEditingMessage(Type type, EntityInfo entityInfo)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
if(entityInfo != null)
|
||||||
|
{
|
||||||
|
this.entityInfo = entityInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketEditingMessage(final FriendlyByteBuf buf) {
|
||||||
|
this.type = Type.valueOf(buf.readInt());
|
||||||
|
this.entityInfo = new EntityInfo();
|
||||||
|
try {
|
||||||
|
this.entityInfo.classType = this.entityInfo.getClass().getClassLoader().loadClass(buf.readUtf());
|
||||||
|
} catch (ClassNotFoundException e) { /* ignored */ }
|
||||||
|
this.entityInfo.ignoreBattle = buf.readBoolean();
|
||||||
|
this.entityInfo.attackPower = buf.readInt();
|
||||||
|
this.entityInfo.attackProbability = buf.readInt();
|
||||||
|
this.entityInfo.attackVariance = buf.readInt();
|
||||||
|
this.entityInfo.attackEffect = EntityInfo.Effect.fromString(buf.readUtf());
|
||||||
|
this.entityInfo.attackEffectProbability = buf.readInt();
|
||||||
|
this.entityInfo.defenseDamage = buf.readInt();
|
||||||
|
this.entityInfo.defenseDamageProbability = buf.readInt();
|
||||||
|
this.entityInfo.evasion = buf.readInt();
|
||||||
|
this.entityInfo.speed = buf.readInt();
|
||||||
|
this.entityInfo.category = buf.readUtf();
|
||||||
|
this.entityInfo.decisionAttack = buf.readInt();
|
||||||
|
this.entityInfo.decisionDefend = buf.readInt();
|
||||||
|
this.entityInfo.decisionFlee = buf.readInt();
|
||||||
|
this.entityInfo.customName = buf.readUtf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketEditingMessage> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketEditingMessage pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if (FMLEnvironment.dist.isClient()) {
|
||||||
|
TurnBasedMinecraftMod.proxy.handlePacket(pkt, ctx);
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketEditingMessage! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.burnedkirby.TurnBasedMinecraft.common.networking;
|
||||||
|
|
||||||
|
import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.fml.loading.FMLEnvironment;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record PacketGeneralMessage(String message) implements CustomPacketPayload
|
||||||
|
{
|
||||||
|
public static final CustomPacketPayload.Type<PacketGeneralMessage> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(TurnBasedMinecraftMod.MODID, "network_packetgeneralmessage"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PacketGeneralMessage> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
ByteBufCodecs.STRING_UTF8,
|
||||||
|
PacketGeneralMessage::message,
|
||||||
|
PacketGeneralMessage::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketGeneralMessage(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PayloadHandler implements IPayloadHandler<PacketGeneralMessage> {
|
||||||
|
@Override
|
||||||
|
public void handle(final @NotNull PacketGeneralMessage pkt, final IPayloadContext ctx) {
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
if (FMLEnvironment.dist.isClient()) {
|
||||||
|
TurnBasedMinecraftMod.proxy.handlePacket(pkt, ctx);
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
ctx.disconnect(Component.literal("Exception handling PacketGeneralMessage! " + e.getMessage()));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 60 KiB |
7
src/main/resources/pack.mcmeta
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "TurnBasedMinecraft resources",
|
||||||
|
"pack_format": 3,
|
||||||
|
"_comment": "A pack_format of 3 should be used starting with Minecraft 1.11. All resources, including language files, should be lowercase (eg: en_us.lang). A pack_format of 2 will load your mod resources with LegacyV2Adapter, which requires language files to have uppercase letters (eg: en_US.lang)."
|
||||||
|
}
|
||||||
|
}
|
94
src/main/templates/META-INF/neoforge.mods.toml
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods.
|
||||||
|
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
|
||||||
|
# The overall format is standard TOML format, v0.5.0.
|
||||||
|
# Note that there are a couple of TOML lists in this file.
|
||||||
|
# Find more information on toml format here: https://github.com/toml-lang/toml
|
||||||
|
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
||||||
|
modLoader="javafml" #mandatory
|
||||||
|
|
||||||
|
# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2.
|
||||||
|
loaderVersion="${loader_version_range}" #mandatory
|
||||||
|
|
||||||
|
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
||||||
|
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
||||||
|
license="${mod_license}"
|
||||||
|
|
||||||
|
# A URL to refer people to when problems occur with this mod
|
||||||
|
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
|
||||||
|
|
||||||
|
# A list of mods - how many allowed here is determined by the individual mod loader
|
||||||
|
[[mods]] #mandatory
|
||||||
|
|
||||||
|
# The modid of the mod
|
||||||
|
modId="${mod_id}" #mandatory
|
||||||
|
|
||||||
|
# The version number of the mod
|
||||||
|
version="${mod_version}" #mandatory
|
||||||
|
|
||||||
|
# A display name for the mod
|
||||||
|
displayName="${mod_name}" #mandatory
|
||||||
|
|
||||||
|
# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/
|
||||||
|
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
||||||
|
updateJSONURL="https://github.com/Stephen-Seo/TurnBasedMinecraftMod/raw/refs/heads/neoforge/update.json"
|
||||||
|
|
||||||
|
# A URL for the "homepage" for this mod, displayed in the mod UI
|
||||||
|
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
|
||||||
|
|
||||||
|
# A file name (in the root of the mod JAR) containing a logo for display
|
||||||
|
#logoFile="examplemod.png" #optional
|
||||||
|
logoFile="assets/com_burnedkirby_turnbasedminecraft/tbmm_icon.png"
|
||||||
|
|
||||||
|
# A text field displayed in the mod UI
|
||||||
|
#credits="" #optional
|
||||||
|
|
||||||
|
# A text field displayed in the mod UI
|
||||||
|
authors="${mod_authors}" #optional
|
||||||
|
|
||||||
|
# The description text for the mod (multi line!) (#mandatory)
|
||||||
|
description='''${mod_description}'''
|
||||||
|
|
||||||
|
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
|
||||||
|
#[[mixins]]
|
||||||
|
#config="${mod_id}.mixins.json"
|
||||||
|
|
||||||
|
# The [[accessTransformers]] block allows you to declare where your AT file is.
|
||||||
|
# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg
|
||||||
|
#[[accessTransformers]]
|
||||||
|
#file="META-INF/accesstransformer.cfg"
|
||||||
|
|
||||||
|
# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json
|
||||||
|
|
||||||
|
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
|
||||||
|
[[dependencies.${mod_id}]] #optional
|
||||||
|
# the modid of the dependency
|
||||||
|
modId="neoforge" #mandatory
|
||||||
|
# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive).
|
||||||
|
# 'required' requires the mod to exist, 'optional' does not
|
||||||
|
# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning
|
||||||
|
type="required" #mandatory
|
||||||
|
# Optional field describing why the dependency is required or why it is incompatible
|
||||||
|
# reason="..."
|
||||||
|
# The version range of the dependency
|
||||||
|
versionRange="${neo_version_range}" #mandatory
|
||||||
|
# An ordering relationship for the dependency.
|
||||||
|
# BEFORE - This mod is loaded BEFORE the dependency
|
||||||
|
# AFTER - This mod is loaded AFTER the dependency
|
||||||
|
ordering="NONE"
|
||||||
|
# Side this dependency is applied on - BOTH, CLIENT, or SERVER
|
||||||
|
side="BOTH"
|
||||||
|
|
||||||
|
# Here's another dependency
|
||||||
|
[[dependencies.${mod_id}]]
|
||||||
|
modId="minecraft"
|
||||||
|
type="required"
|
||||||
|
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
||||||
|
versionRange="${minecraft_version_range}"
|
||||||
|
ordering="NONE"
|
||||||
|
side="BOTH"
|
||||||
|
|
||||||
|
# Features are specific properties of the game environment, that you may want to declare you require. This example declares
|
||||||
|
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
|
||||||
|
# stop your mod loading on the server for example.
|
||||||
|
#[features.${mod_id}]
|
||||||
|
#openGLVersion="[3.2,)"
|
|
@ -1,3 +0,0 @@
|
||||||
# Frequently Asked Questions
|
|
||||||
|
|
||||||
[The FAQ is hosted on the repository.](https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/FAQ.md)
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Client-side Config
|
|
||||||
|
|
||||||
The client config can be opened in two ways.
|
|
||||||
|
|
||||||
One way is via the mod-list (only works in NeoForge, not Forge).
|
|
||||||
|
|
||||||
![Mod list config button](tbm-client-edit-modlist.jpg)
|
|
||||||
|
|
||||||
The other way is via the `/tbm-client-edit` command.
|
|
||||||
|
|
||||||
![tbm-client-edit command](tbm-client-edit-cmd.png)
|
|
||||||
|
|
||||||
Currently, the client config allows for configuration for client-side music
|
|
||||||
playback.
|
|
||||||
|
|
||||||
![client config](tbm-client-edit-config.jpg)
|
|
||||||
|
|
||||||
The "categories" settings are comma-separated words that define what "category"
|
|
||||||
triggers the "battle" music or the "silly" music.
|
|
||||||
|
|
||||||
"Silly Music Threshold" determines the percentage of silly-category-mobs in
|
|
||||||
battle required to play silly music. This means if the setting is 49%, and there
|
|
||||||
is one player, one zombie, and two sheep in battle, then the game will play
|
|
||||||
silly music (since 50% of the combatants are sheep and is greater than 49%).
|
|
||||||
|
|
||||||
## Battle/Silly Music
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The TurnBasedMinecraftMod (TBMM) configuration folders need to be
|
|
||||||
generated. Run the mod once to generate them, then close Minecraft.
|
|
||||||
|
|
||||||
There are two folders in the `.minecraft/config/TurnBasedMinecraft/Music/`
|
|
||||||
directory: `battle` and `silly`.
|
|
||||||
|
|
||||||
Place your music files in those folders, and the appropriate music will play
|
|
||||||
depending on if the battle is with `silly` mobs or not.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
A random song will be picked in the `battle` folder on `battle` music, and
|
|
||||||
in the `silly` music folder on `silly` music.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
It is recommended to use .ogg Vorbis files for music. There is support for
|
|
||||||
.mp3 files, but currently .ogg Vorbis files are more stable in this mod.
|
|
||||||
Note that .ogg Opus files are NOT supported, but .ogg Vorbis files are.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The following command can convert to .ogg Vorbis using ffmpeg:
|
|
||||||
`ffmpeg -i <music_file> -map a:0 -c:a libvorbis output.ogg`
|
|
|
@ -1,13 +0,0 @@
|
||||||
# TurnBasedMinecraftMod
|
|
||||||
|
|
||||||
[Main repository link.](https://github.com/Stephen-Seo/TurnBasedMinecraftMod)
|
|
||||||
|
|
||||||
[Alternate repository link.](https://git.seodisparate.com/stephenseo/TurnBasedMinecraftMod)
|
|
||||||
|
|
||||||
## Pages
|
|
||||||
|
|
||||||
[Client-side config](client_config.md)
|
|
||||||
|
|
||||||
[Server-side config](server_config.md)
|
|
||||||
|
|
||||||
[Frequently Asked Questions](FAQ.md)
|
|
|
@ -1,128 +0,0 @@
|
||||||
# Server-side Config
|
|
||||||
|
|
||||||
Invoke `/tbm-server-edit` to print out server settings to the "chat" area.
|
|
||||||
|
|
||||||
![tbm-server-edit output](tbm-server-edit-full.png)
|
|
||||||
|
|
||||||
In this text area, yellow texts are setting names, green texts are setting
|
|
||||||
values that can be set when clicked on, and dark-green texts are settings that
|
|
||||||
display more settings when clicked on.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
You can hover/click these texts by pressing the "t" key to open the chatbox,
|
|
||||||
and using the mouse.
|
|
||||||
|
|
||||||
![tbm-server-edit info when hovered](tbm-server-edit-hover.png)
|
|
||||||
|
|
||||||
![tbm-server-edit output when haste-speed is set](tbm-server-edit-set-haste.png)
|
|
||||||
|
|
||||||
When clicking on the dark-green texts, a description and list is shown. You can
|
|
||||||
hover over the text to show what actions may occur when clicked.
|
|
||||||
|
|
||||||
![tbm-server-edit ignore-damage-sources
|
|
||||||
output](tbm-server-edit-damage-sources.png)
|
|
||||||
|
|
||||||
On click, the action taken will be shown.
|
|
||||||
|
|
||||||
![tbm-server-edit ignore-damage-sources
|
|
||||||
modified](tbm-server-edit-damage-sources-set.png)
|
|
||||||
|
|
||||||
It is also possible to set config settings via a command, but it is recommended
|
|
||||||
to use the "click-on-chat" interface for general server config settings.
|
|
||||||
|
|
||||||
## Per-Mob Settings
|
|
||||||
|
|
||||||
To demonstrate setting "per-mob" settings, it will be shown how to do so with
|
|
||||||
sheep.
|
|
||||||
|
|
||||||
![sheep](tbm-edit-sheep.jpg)
|
|
||||||
|
|
||||||
Invoke `/tbm-edit`. Some helpful text will show.
|
|
||||||
|
|
||||||
![/tbm-edit output](tbm-edit-output.jpg)
|
|
||||||
|
|
||||||
At this point, TBMM (TurnBasedMinecraftMod) will be keeping track of who you
|
|
||||||
will attack next, so that you can edit their settings. Currently this only
|
|
||||||
applies to mobs like sheep, zombies, skeletons, bees, etc. This is done so that
|
|
||||||
you can physically select the mob you want to "edit".
|
|
||||||
|
|
||||||
![mob settings](tbm-edit-settings.jpg)
|
|
||||||
|
|
||||||
In this example, the "AE" (attack effect) setting will be changed to
|
|
||||||
"levitation", which will cause the sheep to make the player levitate 50% of the
|
|
||||||
time.
|
|
||||||
|
|
||||||
![mob setting attack effect levitation](tbm-edit-levitation.png)
|
|
||||||
|
|
||||||
Also, "DecisionAttack" will be set to 100%, so that the sheep will choose to
|
|
||||||
attack instead of fleeing battle.
|
|
||||||
|
|
||||||
![mob setting decision attack 100%](tbm-edit-decision-attack.png)
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Make sure to click on "Finished Editing" to save changes to the server-side
|
|
||||||
config.
|
|
||||||
|
|
||||||
Note that for sheep to enter battle, they must be removed from the "ignore
|
|
||||||
battle categories" setting (remove the "passive" category). Alternatively, one
|
|
||||||
can set sheep to a category other than "passive", like "animal" or "monster". A
|
|
||||||
category that isn't listed in the "ignore battle categories" setting will start
|
|
||||||
turn-based-battle.
|
|
||||||
|
|
||||||
![server edit remove passive from ignore
|
|
||||||
categories](tbm-edit-server-edit-ignore-battle-types.png)
|
|
||||||
|
|
||||||
Now, sheep will attack and cause "levitation" 50% of the time.
|
|
||||||
|
|
||||||
![after battle with levitation effect](tbm-edit-post-battle.jpg)
|
|
||||||
|
|
||||||
## Custom Name Settings
|
|
||||||
|
|
||||||
Additional entries can be added to the server-side config that applies to mobs
|
|
||||||
named with a specific name (like with a name-tag). Name a mob, then invoke
|
|
||||||
`/tbm-edit custom`.
|
|
||||||
|
|
||||||
![tbm-edit custom command](tbm-edit-custom.png)
|
|
||||||
|
|
||||||
Hit the named mob to start the editing process.
|
|
||||||
|
|
||||||
![tbm-edit custom editing](tbm-edit-custom-editing.jpg)
|
|
||||||
|
|
||||||
Make your changes and click on "Finished Editing", and any mob with that exact
|
|
||||||
name will have these battle settings applied.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
These settings are also in the server-side config.
|
|
||||||
|
|
||||||
## Per-Player Settings
|
|
||||||
|
|
||||||
As of TurnBasedMinecraftMod 1.26.4, one can add Player-specific config via a
|
|
||||||
command `/tbm-edit player <PLAYER_NAME>`.
|
|
||||||
|
|
||||||
![tbm-edit player command](tbm-edit-player-cmd.png)
|
|
||||||
|
|
||||||
You can make changes as usual, though some options for regular mobs do not
|
|
||||||
apply to the per-Player config.
|
|
||||||
|
|
||||||
![tbm-edit player editing](tbm-edit-player-display.png)
|
|
||||||
|
|
||||||
Don't forget to click on "Finished Editing" when done. Once saved, the
|
|
||||||
server/single-player-game should use these settings for the specified Player in
|
|
||||||
battle.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
These settings are also in the server-side config.
|
|
||||||
|
|
||||||
## Other Things to Know
|
|
||||||
|
|
||||||
<s>Sometimes a mod update will "reset" the settings in the server-config to
|
|
||||||
defaults. This is due to new mob entries in the settings.</s>
|
|
||||||
*As of version 1.26.5 of the mod, this should happen less frequently!*
|
|
||||||
Changes were added in 1.26.5 such that entries that exist in the default config
|
|
||||||
but not in the current config will be appended to the current config.
|
|
||||||
|
|
||||||
Older versions of this mod still retain the previous behavior: Check the
|
|
||||||
`.minecraft/config` folder (or `config` folder on the server) to see that the
|
|
||||||
old settings file was renamed and the new settings file is in its place. You
|
|
||||||
may have to compare the files to keep the settings you want. Though this
|
|
||||||
probably shouldn't happen anymore in newer versions of this mod.
|
|
Before Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5.8 KiB |
|
@ -1,11 +0,0 @@
|
||||||
site_name: Docs for TurnBasedMinecraftMod
|
|
||||||
theme:
|
|
||||||
name: mkdocs
|
|
||||||
color_mode: dark
|
|
||||||
user_color_mode_toggle: true
|
|
||||||
markdown_extensions:
|
|
||||||
- admonition
|
|
||||||
nav:
|
|
||||||
- Client-Side Config: client_config.md
|
|
||||||
- Server-Side Config: server_config.md
|
|
||||||
- Frequently Asked Questions: FAQ.md
|
|
22
update.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"homepage": "https://github.com/Stephen-Seo/TurnBasedMinecraftMod",
|
||||||
|
"1.21.3": {
|
||||||
|
"1.26.5": "Config improvements, NeoForge 21.3.11-beta.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.4": "Add player-specific config, NeoForge 21.3.6-beta.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.3": "Ported to NeoForge 21.3.2-beta (MC 1.21.3), minor tweak to packet.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md"
|
||||||
|
},
|
||||||
|
"1.21.1": {
|
||||||
|
"1.26.5-MC-1.21.1": "Config improvements, NeoForge 21.1.74.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.4-MC-1.21.1": "Add player-specific config, NeoForge 21.1.73.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.3-MC-1.21.1": "Minor tweak to packet.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.2": "Display Entities on attack menu in BattleGUI.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.1": "Ported to NeoForge 21.1.72, leave BattleGUI with Escape key, MC music paused in battle properly.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md",
|
||||||
|
"1.26.0": "Ported to NeoForge 21.1.69, client config.\n https://github.com/Stephen-Seo/TurnBasedMinecraftMod/blob/neoforge/Changelog.md"
|
||||||
|
},
|
||||||
|
"promos": {
|
||||||
|
"1.21.3-latest": "1.26.5",
|
||||||
|
"1.21.3-recommended": "1.26.5",
|
||||||
|
"1.21.1-latest": "1.26.5-MC-1.21.1",
|
||||||
|
"1.21.1-recommended": "1.26.5-MC-1.21.1"
|
||||||
|
}
|
||||||
|
}
|