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