mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 22:56:23 -04:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c38630bd6f | ||
|
|
4dba646de9 | ||
|
|
201da371a5 | ||
|
|
d6828baeec | ||
|
|
34f1fd2809 | ||
|
|
09a5fe4c5d | ||
|
|
c42bd7a293 | ||
|
|
33a303a2f5 | ||
|
|
9f7dd84ace | ||
|
|
68e93a13d8 | ||
|
|
611bfd9792 | ||
|
|
0c428526e7 | ||
|
|
b28355b8f2 | ||
|
|
8e163d0e14 | ||
|
|
1660a53867 | ||
|
|
c50ae2d478 | ||
|
|
497de9a8f1 | ||
|
|
f57300dc18 | ||
|
|
0e3c197d9b | ||
|
|
4a12a39287 | ||
|
|
2da2222540 |
@@ -24,7 +24,6 @@ eclipse
|
||||
run
|
||||
runClient
|
||||
runServer
|
||||
utilityscripts/implementation/*.txt
|
||||
|
||||
# Files from Forge MDK
|
||||
forge*changelog.txt
|
||||
@@ -37,11 +36,9 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/fabric/src/main/generated/*
|
||||
/.ignored/
|
||||
common/.fabric
|
||||
.kotlin/
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
/common/config/*
|
||||
|
||||
+2
-9
@@ -16,7 +16,6 @@ check_assets_cache:
|
||||
stage: preprocessing
|
||||
script:
|
||||
- echo "Checking if cached hash matches current hash"
|
||||
- yum install -y findutils
|
||||
|
||||
# Calculate the current hash
|
||||
- find common/src/main/resources/assets common/src/main/resources/data -type f -exec sha256sum {} + | sort -k 2 | sha256sum > current_hash.txt
|
||||
@@ -72,8 +71,8 @@ setup_packsquash:
|
||||
- echo "Installing wget & unzip"
|
||||
- yum install -y wget unzip
|
||||
- echo "Downloading PackSquash"
|
||||
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.1/packsquash-x86_64-unknown-linux-musl.zip
|
||||
- unzip packsquash-x86_64-unknown-linux-musl.zip -d ./packsquash_bin
|
||||
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.0/PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip
|
||||
- unzip PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip -d ./packsquash_bin
|
||||
- echo "Creating PackSquash options.toml"
|
||||
- |
|
||||
cat <<EOF > ./options.toml
|
||||
@@ -149,7 +148,6 @@ build:
|
||||
dependencies:
|
||||
- replace_resources
|
||||
script:
|
||||
- yum install -y findutils
|
||||
- export GRADLE_OPTS="-Xmx4G -Xms4G"
|
||||
- chmod +x ./gradlew
|
||||
- ./gradlew build
|
||||
@@ -157,9 +155,6 @@ build:
|
||||
paths:
|
||||
- neoforge/build/libs
|
||||
- fabric/build/libs
|
||||
exclude:
|
||||
- neoforge/build/libs/*-dev-shadow.jar
|
||||
- fabric/build/libs/*-dev-shadow.jar
|
||||
|
||||
publish:
|
||||
stage: deploy
|
||||
@@ -168,7 +163,6 @@ publish:
|
||||
rules:
|
||||
- if: '$CI_PROJECT_PATH == "cable-mc/cobblemon" && $CI_COMMIT_BRANCH == "main"'
|
||||
script:
|
||||
- yum install -y findutils
|
||||
- export GRADLE_OPTS="-Xmx4G -Xms4G"
|
||||
- chmod +x ./gradlew
|
||||
- ./gradlew publish
|
||||
@@ -182,7 +176,6 @@ deploy_artifacts:
|
||||
dependencies:
|
||||
- build
|
||||
script:
|
||||
- yum install -y findutils
|
||||
- echo "Collecting artifacts..."
|
||||
- find neoforge/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
|
||||
- find fabric/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
|
||||
|
||||
+41
-69
@@ -1,88 +1,60 @@
|
||||
# Changelog
|
||||
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
|
||||
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
|
||||
|
||||
### Additions
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
|
||||
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
|
||||
- Added new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added missing crops to Botany Pots integration
|
||||
|
||||
### Cries added/updated for the following Pokémon
|
||||
- Hoppip
|
||||
- Skiploom
|
||||
- Jumpluff
|
||||
- Houndour
|
||||
- Houndoom
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Surskit
|
||||
- Masquerain
|
||||
- Torkoal
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Combee
|
||||
- Vespiquen
|
||||
- Dusknoir
|
||||
- Helioptile
|
||||
- Heliolisk
|
||||
- Grubbin
|
||||
- Charjabug
|
||||
- Vikavolt
|
||||
- Applin (updated)
|
||||
- Flapple (updated)
|
||||
- Appletun
|
||||
- Runerigus
|
||||
- Dipplin
|
||||
### New rideable Pokémon
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Vikavolt
|
||||
|
||||
### Changes
|
||||
- Changed the Misc icon in the Campfire Pot Recipe Book to Health Candy
|
||||
- Moved EXP Candies from the Medicine recipe book category to the Misc category
|
||||
- Removed Dynamic Lights data from the Magmarizer item for consistency with the block
|
||||
- Added `doPokemonPredation` gamerule, which controls aggressiveness of Pokémon with `hate_entity` brain task defined
|
||||
- Added `predation_chance` server config value, which controls the possibility of attacking the hated entity found by `hate_entity` brain task
|
||||
- Prehistoric Eroded Pillar generates slightly more frequently
|
||||
- Restructured Botany Pots integration folder structure
|
||||
- Decrease default pastured mon wander radius to 32.
|
||||
- Factor in the weight of pokemon when calculating pushing forces between hitboxes.
|
||||
- Remade the Starter Selection Screen with new assets
|
||||
- Refactored berry trees to be less impactful on TPS (more performant)
|
||||
|
||||
### Fixes
|
||||
- Fixed datapacks with item interaction evolutions that don't specify an item crashing the world on load.
|
||||
- Fixed the Pokédex evolution tree crashing when a datapack's dex entry is missing an evolution's form.
|
||||
- Fixed the Pokédex evolution tree crashing with a stack overflow on evolutions that loop back on themselves.
|
||||
- Fixed the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
|
||||
- Fixed evolution trees not building properly when a Pokémon has several Pokédex entries for different regions.
|
||||
- Fixed form-changing evolutions sometimes resolving to the source form in the Pokédex evolution tree.
|
||||
- Broken datapack evolutions are now logged and skipped in the Pokédex evolution tree instead of crashing.
|
||||
- Fixed the `alpha` property having no tab completion in commands such as `/pokespawn`, `/pokegive` and `/pokeedit`, and added the missing `dmax_level`, `gmax_factor` and `tradable` aliases to tab completion
|
||||
- Fixed friendship booster items not being recognised as such
|
||||
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot)
|
||||
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event
|
||||
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands
|
||||
- Fixed MoLang that's targeting players intermittedly failing when they have died, needing them to relog
|
||||
- Fixed a NeoForge bug where none of the Cobblemon plants/berries could be placed into pots
|
||||
- Fixed party overlay not hiding when the player opens the Pokédex scanner
|
||||
- Added compat for AppleSkin
|
||||
- Fixed some display names having unintended font colours
|
||||
- Fixed `hate_entity` task focusing on its own entity
|
||||
- Thus enabled deadly warfare between Zangoose and Seviper
|
||||
- Prehistoric Frozen Pond, Prehistoric Frozen Spike, and Prehistoric Lush Den now generate
|
||||
- Fixed Frogadier floating in its battle_idle animation
|
||||
- Fixed absence or incorrect mergers in spawning conditions, making max height/depth of conditions to use the min value instead
|
||||
- Added stricter riding checks on server side to prevent possible packet forgery when player tries to ride pokemon
|
||||
- Fixed Pokémon cries and animation sounds played from GUIs, such as the starter selection screen and the Pokédex, being tied to the Master volume slider instead of Friendly Creatures
|
||||
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
|
||||
- Fix occasional crash on retrieving revived fossils
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed issue where particle like beam can disappear when you don't see the beginning of the beam or you look away
|
||||
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
|
||||
- Fix occasional crash on retrieving revived fossils
|
||||
- Fix brushing not working on Neoforge
|
||||
- [Various Submarine Ride Fixes](https://gitlab.com/cable-mc/cobblemon/-/merge_requests/2157)
|
||||
- Fix Pastured mons despawning and running away.
|
||||
- Fix Apricorn trees crashing when other mods bonemeal it
|
||||
- [Various UFO Ride Improvements](https://gitlab.com/cable-mc/cobblemon/-/merge_requests/2156)
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within it's menu
|
||||
- Fixed NPCs switching pokemon very often during battles
|
||||
- Fixed Campfire pot causing crashes under certain circumstances
|
||||
- Fixed Type Gems not being consumed upon use
|
||||
- Fixed Ancient Pokeball's to their respective modifiers
|
||||
- Fixed a Server warning when recalling Pokemon
|
||||
- Fixed sliding particles incorrectly handling block collision
|
||||
- Fixed Full Heal not curing status conditions in battle
|
||||
|
||||
### Developer
|
||||
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation
|
||||
- New Platform Event `PLAYER_RESPAWN` which is fired when a player respawns (either through death or returning from the end)
|
||||
- Platform Events `SERVER_PLAYER_TICK_PRE` and `SERVER_PLAYER_TICK_POST` no longer fire when the player is dead
|
||||
- it is now tied much closer to the actual entity rather than the player (or their connection)
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts
|
||||
|
||||
### Molang & Datapacks
|
||||
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`)
|
||||
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as target
|
||||
- Added hate chance optional parameter to `hate_entity` task. `1.0` will make an entity always attack the target
|
||||
- q.item.is_enchanted()
|
||||
- q.item.has_enchantment(minecraft:sharpness, 3)
|
||||
- Added `has_chosen_starter` & `get_starter_uuid` as available Molang functions
|
||||
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for pokemon
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
{
|
||||
"format_version": "1.21.11",
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [128, 128],
|
||||
"textures": {
|
||||
"2": "block/texture"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [9.999, 10.999, -0.001],
|
||||
"to": [15.001, 12.001, 0.001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.25, 1.125, 0.875, 1.25], "texture": "#2"},
|
||||
"east": {"uv": [0.25, 1.125, 0.25, 1.25], "texture": "#2"},
|
||||
"south": {"uv": [0.875, 1.125, 1.5, 1.25], "texture": "#2"},
|
||||
"west": {"uv": [0.875, 1.125, 0.875, 1.25], "texture": "#2"},
|
||||
"up": {"uv": [0.875, 1.125, 0.25, 1.125], "texture": "#2"},
|
||||
"down": {"uv": [1.5, 1.125, 0.875, 1.125], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-0.0001, 8.9999, 2.9999],
|
||||
"to": [2.0001, 13.0001, 13.0001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 2]},
|
||||
"faces": {
|
||||
"north": {"uv": [4.25, 6, 4.5, 6.5], "texture": "#2"},
|
||||
"east": {"uv": [3, 6, 4.25, 6.5], "texture": "#2"},
|
||||
"south": {"uv": [5.75, 6, 6, 6.5], "texture": "#2"},
|
||||
"west": {"uv": [4.5, 6, 5.75, 6.5], "texture": "#2"},
|
||||
"up": {"uv": [4.5, 6, 4.25, 4.75], "texture": "#2"},
|
||||
"down": {"uv": [4.75, 4.75, 4.5, 6], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-0.0001, 8.9999, -0.0001],
|
||||
"to": [16.0001, 13.0001, 3.0001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.375, 3.375, 2.375, 3.875], "texture": "#2"},
|
||||
"east": {"uv": [0, 3.375, 0.375, 3.875], "texture": "#2"},
|
||||
"south": {"uv": [2.75, 3.375, 4.75, 3.875], "texture": "#2"},
|
||||
"west": {"uv": [2.375, 3.375, 2.75, 3.875], "texture": "#2"},
|
||||
"up": {"uv": [2.375, 3.375, 0.375, 3], "texture": "#2"},
|
||||
"down": {"uv": [4.375, 3, 2.375, 3.375], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2.995, 10.995, -0.005],
|
||||
"to": [15.005, 13.005, 2.005],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.25, 6.75, 1.75, 7], "texture": "#2"},
|
||||
"east": {"uv": [0, 6.75, 0.25, 7], "texture": "#2"},
|
||||
"south": {"uv": [2, 6.75, 3.5, 7], "texture": "#2"},
|
||||
"west": {"uv": [1.75, 6.75, 2, 7], "texture": "#2"},
|
||||
"up": {"uv": [1.75, 6.75, 0.25, 6.5], "texture": "#2"},
|
||||
"down": {"uv": [3.25, 6.5, 1.75, 6.75], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [9.999, 10.999, -0.001],
|
||||
"to": [15.001, 12.001, 0.001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.25, 1.375, 0.875, 1.5], "texture": "#2"},
|
||||
"east": {"uv": [0.25, 1.375, 0.25, 1.5], "texture": "#2"},
|
||||
"south": {"uv": [0.875, 1.375, 1.5, 1.5], "texture": "#2"},
|
||||
"west": {"uv": [0.875, 1.375, 0.875, 1.5], "texture": "#2"},
|
||||
"up": {"uv": [0.875, 1.375, 0.25, 1.375], "texture": "#2"},
|
||||
"down": {"uv": [1.5, 1.375, 0.875, 1.375], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [13.9999, 8.9999, 2.9999],
|
||||
"to": [16.0001, 13.0001, 13.0001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 2]},
|
||||
"faces": {
|
||||
"north": {"uv": [1.25, 6, 1.5, 6.5], "texture": "#2"},
|
||||
"east": {"uv": [0, 6, 1.25, 6.5], "texture": "#2"},
|
||||
"south": {"uv": [2.75, 6, 3, 6.5], "texture": "#2"},
|
||||
"west": {"uv": [1.5, 6, 2.75, 6.5], "texture": "#2"},
|
||||
"up": {"uv": [1.5, 6, 1.25, 4.75], "texture": "#2"},
|
||||
"down": {"uv": [1.75, 4.75, 1.5, 6], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-0.0001, 8.9999, 12.9999],
|
||||
"to": [16.0001, 13.0001, 16.0001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, -1, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.375, 4.25, 2.375, 4.75], "texture": "#2"},
|
||||
"east": {"uv": [0, 4.25, 0.375, 4.75], "texture": "#2"},
|
||||
"south": {"uv": [2.75, 4.25, 4.75, 4.75], "texture": "#2"},
|
||||
"west": {"uv": [2.375, 4.25, 2.75, 4.75], "texture": "#2"},
|
||||
"up": {"uv": [2.375, 4.25, 0.375, 3.875], "texture": "#2"},
|
||||
"down": {"uv": [4.375, 3.875, 2.375, 4.25], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [1, 0, 1],
|
||||
"to": [15, 10, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [1.75, 1.75, 3.5, 3], "texture": "#2"},
|
||||
"east": {"uv": [0, 1.75, 1.75, 3], "texture": "#2"},
|
||||
"south": {"uv": [5.25, 1.75, 7, 3], "texture": "#2"},
|
||||
"west": {"uv": [3.5, 1.75, 5.25, 3], "texture": "#2"},
|
||||
"up": {"uv": [3.5, 1.75, 1.75, 0], "texture": "#2"},
|
||||
"down": {"uv": [5.25, 0, 3.5, 1.75], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3.999, 1.999, 0.999],
|
||||
"to": [12.001, 10.001, 1.001],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 7.375, 1, 8.375], "texture": "#2"},
|
||||
"east": {"uv": [0, 7.375, 0, 8.375], "texture": "#2"},
|
||||
"south": {"uv": [1, 7.375, 2, 8.375], "texture": "#2"},
|
||||
"west": {"uv": [1, 7.375, 1, 8.375], "texture": "#2"},
|
||||
"up": {"uv": [1, 7.375, 0, 7.375], "texture": "#2"},
|
||||
"down": {"uv": [2, 7.375, 1, 7.375], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 5],
|
||||
"to": [14, 12, 6],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.375, 6.625, 3.5], "texture": "#2"},
|
||||
"east": {"uv": [4.75, 3.375, 4.875, 3.5], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.375, 8.5, 3.5], "texture": "#2"},
|
||||
"west": {"uv": [6.875, 3.375, 7, 3.5], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.125, 5.125, 3], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3, 6.625, 3.125], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 4],
|
||||
"to": [14, 12, 5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.375, 6.625, 3.5], "texture": "#2"},
|
||||
"east": {"uv": [4.875, 3.375, 5, 3.5], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.375, 8.5, 3.5], "texture": "#2"},
|
||||
"west": {"uv": [6.75, 3.375, 6.875, 3.5], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.25, 5.125, 3.125], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3.125, 6.625, 3.25], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 3],
|
||||
"to": [14, 12, 4],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.375, 6.625, 3.5], "texture": "#2"},
|
||||
"east": {"uv": [5, 3.375, 5.125, 3.5], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.375, 8.5, 3.5], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 3.375, 6.75, 3.5], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.375, 5.125, 3.25], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3.25, 6.625, 3.375], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [10, 11, 6],
|
||||
"to": [14, 12, 8],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [5, 4.25, 5.5, 4.375], "texture": "#2"},
|
||||
"east": {"uv": [4.75, 4.25, 5, 4.375], "texture": "#2"},
|
||||
"south": {"uv": [5.75, 4.25, 6.25, 4.375], "texture": "#2"},
|
||||
"west": {"uv": [5.5, 4.25, 5.75, 4.375], "texture": "#2"},
|
||||
"up": {"uv": [5.5, 4.25, 5, 4], "texture": "#2"},
|
||||
"down": {"uv": [6, 4, 5.5, 4.25], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 6],
|
||||
"to": [6, 12, 8],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [5, 4.625, 5.5, 4.75], "texture": "#2"},
|
||||
"east": {"uv": [4.75, 4.625, 5, 4.75], "texture": "#2"},
|
||||
"south": {"uv": [5.75, 4.625, 6.25, 4.75], "texture": "#2"},
|
||||
"west": {"uv": [5.5, 4.625, 5.75, 4.75], "texture": "#2"},
|
||||
"up": {"uv": [5.5, 4.625, 5, 4.375], "texture": "#2"},
|
||||
"down": {"uv": [6, 4.375, 5.5, 4.625], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 10],
|
||||
"to": [14, 12, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.875, 6.625, 4], "texture": "#2"},
|
||||
"east": {"uv": [5, 3.875, 5.125, 4], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.875, 8.5, 4], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 3.875, 6.75, 4], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.875, 5.125, 3.75], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3.75, 6.625, 3.875], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 11],
|
||||
"to": [14, 12, 12],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.875, 6.625, 4], "texture": "#2"},
|
||||
"east": {"uv": [4.875, 3.875, 5, 4], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.875, 8.5, 4], "texture": "#2"},
|
||||
"west": {"uv": [6.75, 3.875, 6.875, 4], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.75, 5.125, 3.625], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3.625, 6.625, 3.75], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 12],
|
||||
"to": [14, 12, 13],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.125, 3.875, 6.625, 4], "texture": "#2"},
|
||||
"east": {"uv": [4.75, 3.875, 4.875, 4], "texture": "#2"},
|
||||
"south": {"uv": [7, 3.875, 8.5, 4], "texture": "#2"},
|
||||
"west": {"uv": [6.875, 3.875, 7, 4], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 3.625, 5.125, 3.5], "texture": "#2"},
|
||||
"down": {"uv": [8.125, 3.5, 6.625, 3.625], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [10, 11, 8],
|
||||
"to": [14, 12, 10],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.25, 5, 6.75, 5.125], "texture": "#2"},
|
||||
"east": {"uv": [6, 5, 6.25, 5.125], "texture": "#2"},
|
||||
"south": {"uv": [7, 5, 7.5, 5.125], "texture": "#2"},
|
||||
"west": {"uv": [6.75, 5, 7, 5.125], "texture": "#2"},
|
||||
"up": {"uv": [6.75, 5, 6.25, 4.75], "texture": "#2"},
|
||||
"down": {"uv": [7.25, 4.75, 6.75, 5], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [6, 11, 9],
|
||||
"to": [10, 12, 10],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.125, 5.625, 6.625, 5.75], "texture": "#2"},
|
||||
"east": {"uv": [6, 5.625, 6.125, 5.75], "texture": "#2"},
|
||||
"south": {"uv": [6.75, 5.625, 7.25, 5.75], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 5.625, 6.75, 5.75], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 5.625, 6.125, 5.5], "texture": "#2"},
|
||||
"down": {"uv": [7.125, 5.5, 6.625, 5.625], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11, 8],
|
||||
"to": [6, 12, 10],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-3, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.25, 5.375, 6.75, 5.5], "texture": "#2"},
|
||||
"east": {"uv": [6, 5.375, 6.25, 5.5], "texture": "#2"},
|
||||
"south": {"uv": [7, 5.375, 7.5, 5.5], "texture": "#2"},
|
||||
"west": {"uv": [6.75, 5.375, 7, 5.5], "texture": "#2"},
|
||||
"up": {"uv": [6.75, 5.375, 6.25, 5.125], "texture": "#2"},
|
||||
"down": {"uv": [7.25, 5.125, 6.75, 5.375], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [9, 11, 7],
|
||||
"to": [10, 12, 9],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.5, 4.25, 6.625, 4.375], "texture": "#2"},
|
||||
"east": {"uv": [6.25, 4.25, 6.5, 4.375], "texture": "#2"},
|
||||
"south": {"uv": [6.875, 4.25, 7, 4.375], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 4.25, 6.875, 4.375], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 4.25, 6.5, 4], "texture": "#2"},
|
||||
"down": {"uv": [6.75, 4, 6.625, 4.25], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [6, 11, 7],
|
||||
"to": [7, 12, 9],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.5, 4.625, 6.625, 4.75], "texture": "#2"},
|
||||
"east": {"uv": [6.25, 4.625, 6.5, 4.75], "texture": "#2"},
|
||||
"south": {"uv": [6.875, 4.625, 7, 4.75], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 4.625, 6.875, 4.75], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 4.625, 6.5, 4.375], "texture": "#2"},
|
||||
"down": {"uv": [6.75, 4.375, 6.625, 4.625], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [7, 11, 7],
|
||||
"to": [9, 12, 9],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.25, 6.25, 6.5, 6.375], "texture": "#2"},
|
||||
"east": {"uv": [6, 6.25, 6.25, 6.375], "texture": "#2"},
|
||||
"south": {"uv": [6.75, 6.25, 7, 6.375], "texture": "#2"},
|
||||
"west": {"uv": [6.5, 6.25, 6.75, 6.375], "texture": "#2"},
|
||||
"up": {"uv": [6.5, 6.25, 6.25, 6], "texture": "#2"},
|
||||
"down": {"uv": [6.75, 6, 6.5, 6.25], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [6, 11, 6],
|
||||
"to": [10, 12, 7],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 11, 6]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.125, 5.875, 6.625, 6], "texture": "#2"},
|
||||
"east": {"uv": [6, 5.875, 6.125, 6], "texture": "#2"},
|
||||
"south": {"uv": [6.75, 5.875, 7.25, 6], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 5.875, 6.75, 6], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 5.875, 6.125, 5.75], "texture": "#2"},
|
||||
"down": {"uv": [7.125, 5.75, 6.625, 5.875], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 13, 11],
|
||||
"to": [0, 15, 12],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, -2]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.375, 6.5, 6.375, 6.75], "texture": "#2"},
|
||||
"east": {"uv": [6.25, 6.5, 6.375, 6.75], "texture": "#2"},
|
||||
"south": {"uv": [6.5, 6.5, 6.5, 6.75], "texture": "#2"},
|
||||
"west": {"uv": [6.375, 6.5, 6.5, 6.75], "texture": "#2"},
|
||||
"up": {"uv": [6.375, 6.5, 6.375, 6.375], "texture": "#2"},
|
||||
"down": {"uv": [6.375, 6.375, 6.375, 6.5], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 13, 14],
|
||||
"to": [0, 20, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.125, 6.5, 6.125, 7.375], "texture": "#2"},
|
||||
"east": {"uv": [6, 6.5, 6.125, 7.375], "texture": "#2"},
|
||||
"south": {"uv": [6.25, 6.5, 6.25, 7.375], "texture": "#2"},
|
||||
"west": {"uv": [6.125, 6.5, 6.25, 7.375], "texture": "#2"},
|
||||
"up": {"uv": [6.125, 6.5, 6.125, 6.375], "texture": "#2"},
|
||||
"down": {"uv": [6.125, 6.375, 6.125, 6.5], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 13, 14],
|
||||
"to": [0, 20, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.625, 6.5, 6.625, 7.375], "texture": "#2"},
|
||||
"east": {"uv": [6.5, 6.5, 6.625, 7.375], "texture": "#2"},
|
||||
"south": {"uv": [6.75, 6.5, 6.75, 7.375], "texture": "#2"},
|
||||
"west": {"uv": [6.625, 6.5, 6.75, 7.375], "texture": "#2"},
|
||||
"up": {"uv": [6.625, 6.5, 6.625, 6.375], "texture": "#2"},
|
||||
"down": {"uv": [6.625, 6.375, 6.625, 6.5], "texture": "#2"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 13, 14],
|
||||
"to": [0, 20, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.875, 6.5, 6.875, 7.375], "texture": "#2"},
|
||||
"east": {"uv": [6.75, 6.5, 6.875, 7.375], "texture": "#2"},
|
||||
"south": {"uv": [7, 6.5, 7, 7.375], "texture": "#2"},
|
||||
"west": {"uv": [6.875, 6.5, 7, 7.375], "texture": "#2"},
|
||||
"up": {"uv": [6.875, 6.5, 6.875, 6.375], "texture": "#2"},
|
||||
"down": {"uv": [6.875, 6.375, 6.875, 6.5], "texture": "#2"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"name": "bone",
|
||||
"origin": [1, 0, 1],
|
||||
"color": 0,
|
||||
"children": [
|
||||
{
|
||||
"name": "bone9",
|
||||
"origin": [12.5, 11.5, 0],
|
||||
"color": 0,
|
||||
"children": [0]
|
||||
},
|
||||
1,
|
||||
2,
|
||||
{
|
||||
"name": "bone5",
|
||||
"origin": [9, 12, 1],
|
||||
"color": 0,
|
||||
"children": [3]
|
||||
},
|
||||
{
|
||||
"name": "bone4",
|
||||
"origin": [12.5, 11.5, 0],
|
||||
"color": 0,
|
||||
"children": [4]
|
||||
},
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
{
|
||||
"name": "bone8",
|
||||
"origin": [8, 6, 1],
|
||||
"color": 0,
|
||||
"children": [8]
|
||||
},
|
||||
{
|
||||
"name": "opening",
|
||||
"origin": [8, 11.5, 6],
|
||||
"color": 0,
|
||||
"children": [
|
||||
{
|
||||
"name": "bone3",
|
||||
"origin": [8, 11.5, 3],
|
||||
"color": 0,
|
||||
"children": [
|
||||
{
|
||||
"name": "bone10",
|
||||
"origin": [8, 11.5, 5.5],
|
||||
"color": 0,
|
||||
"children": [
|
||||
9,
|
||||
{
|
||||
"name": "bone11",
|
||||
"origin": [8, 11.5, 4.5],
|
||||
"color": 0,
|
||||
"children": [
|
||||
10,
|
||||
{
|
||||
"name": "bone12",
|
||||
"origin": [8, 11.5, 3.5],
|
||||
"color": 0,
|
||||
"children": [11]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
12,
|
||||
13
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bone2",
|
||||
"origin": [8, 11.5, 13.0375],
|
||||
"color": 0,
|
||||
"children": [
|
||||
{
|
||||
"name": "bone13",
|
||||
"origin": [8, 11.5, 10.5],
|
||||
"color": 0,
|
||||
"children": [
|
||||
14,
|
||||
{
|
||||
"name": "bone14",
|
||||
"origin": [8, 11.5, 11.5],
|
||||
"color": 0,
|
||||
"children": [15]
|
||||
},
|
||||
{
|
||||
"name": "bone15",
|
||||
"origin": [8, 11.5, 12.5],
|
||||
"color": 0,
|
||||
"children": [16]
|
||||
}
|
||||
]
|
||||
},
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
24,
|
||||
{
|
||||
"name": "bone7",
|
||||
"origin": [1, 0, 1],
|
||||
"color": 0,
|
||||
"children": [
|
||||
{
|
||||
"name": "bone6",
|
||||
"origin": [0, 16.5, 14.5],
|
||||
"color": 0,
|
||||
"children": [25]
|
||||
},
|
||||
{
|
||||
"name": "bone5red",
|
||||
"origin": [0, 16.5, 14.5],
|
||||
"color": 0,
|
||||
"children": [26]
|
||||
},
|
||||
{
|
||||
"name": "bone8green",
|
||||
"origin": [0, 16.5, 14.5],
|
||||
"color": 0,
|
||||
"children": [27]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+2
-5
@@ -1,6 +1,3 @@
|
||||
import utilities.gitBranch
|
||||
import utilities.gitShortCommit
|
||||
|
||||
plugins {
|
||||
id("cobblemon.root-conventions")
|
||||
}
|
||||
@@ -9,6 +6,6 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
|
||||
|
||||
val isSnapshot = project.property("snapshot")?.equals("true") == true
|
||||
if (isSnapshot) {
|
||||
val fixedBranchName = gitBranch().substringAfter("/")
|
||||
version = "$version-${fixedBranchName}-${gitShortCommit()}"
|
||||
val fixedBranchName = versioning.info.branch.substringAfter("/")
|
||||
version = "$version-${fixedBranchName}-${versioning.info.build}"
|
||||
}
|
||||
|
||||
@@ -1,532 +0,0 @@
|
||||
# Changelog
|
||||
## [1.8.0 - Make Your Move Update (September 6th, 2026)](#1-8-0)
|
||||
#### I think the red eyes mean they're friendly!
|
||||
|
||||
### Additions
|
||||
- Added Alpha Pokémon
|
||||
- Alpha Pokémon are bigger, stronger, and more aggressive than regular Pokémon.
|
||||
- They are also scarier.
|
||||
- They always know a TM move and will adapt to be stronger than your party.
|
||||
- Alphas will always lead herds and spawn in herds.
|
||||
- Added alpha Pokémon attraction bait effect to Hopo Berries.
|
||||
- Added intrinsic scaling to all Pokémon (anywhere from 5% smaller to 5% bigger than normal). Some cases, such as shoulder mounting, will not scale so that the shoulder animations don't look silly.
|
||||
- Added scaling to "baby" Pokémon (10% smaller at level 1 → normally sized at level 10).
|
||||
- Added TMs.
|
||||
- Added TM Machine. TMs are made from elemental type gems, and their recipes are unlocked by having Pokémon that know the move.
|
||||
- Added config options for making TMs infinite-use and instant unlocking of all TM recipes.
|
||||
- Added Type Gem blocks as well as crystal clusters and deepslate core blocks that are used for growing elemental type gems.
|
||||
- Added a new Starter Selection menu that looks way better.
|
||||
- Added a "Move Dex" to the Pokédex to help identify what moves a Pokémon can learn and what Pokémon can get a move.
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added 49 new habitat structures. In these habitats you will find rarer Pokémon and sometimes different Pokémon will appear on different days.
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys? It's true!!!
|
||||
- Added a way to obtain Porygon by inserting an Upgrade or Dubious Disc into a data monitor.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks. What does that have to do with Pokémon? How about you mind your own business!
|
||||
- Added missing crops to Botany Pots integration.
|
||||
- Added conditional seats for riding. Seats can be gated by Molang such as "is_alpha".
|
||||
- Added 10 new ruin structures.
|
||||
- Added a new shipwreck cove structure called the Magma Shipwreck Cove.
|
||||
- Added 7 new advancements.
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Held item visibility can now be controlled by the `held_item_visible` command argument.
|
||||
- Added config option to disable Pokémon spawning on a per-world basis.
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`).
|
||||
- Added `/givetm` command.
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Pidgeot
|
||||
- Crobat
|
||||
- Skarmory
|
||||
- Milotic
|
||||
- Archeops
|
||||
- Goodra
|
||||
- Goodra (Hisuian Form)
|
||||
- Drampa
|
||||
- Duraludon
|
||||
- Archaludon
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 3
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Seviper
|
||||
- Zangoose
|
||||
|
||||
#### Generation 4
|
||||
- Cherubi
|
||||
- Cherrim
|
||||
- Stunky
|
||||
- Skuntank
|
||||
|
||||
#### Generation 5
|
||||
- Audino
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
|
||||
#### Generation 6
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
|
||||
#### Generation 7
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
|
||||
#### Generation 8
|
||||
- Rolycoly
|
||||
- Carkol
|
||||
- Coalossal
|
||||
- Runerigus
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Indeedee
|
||||
- Duraludon
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
|
||||
#### Generation 9
|
||||
|
||||
- Nymble
|
||||
- Lokix
|
||||
- Kingambit
|
||||
- Dipplin
|
||||
- Archaludon
|
||||
- Hydrapple
|
||||
|
||||
#### Regional Forms
|
||||
- Sandshrew (Alolan Form)
|
||||
- Sandslash (Alolan Form)
|
||||
- Yamask (Galarian Form)
|
||||
- Avalugg (Hisuian Form)
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Pidgey
|
||||
- Pidgeotto
|
||||
- Pidgeot
|
||||
- Sandshrew
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Ditto
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Skarmory
|
||||
- Smoochum
|
||||
- Jynx
|
||||
- Popplio
|
||||
- Brionne
|
||||
- Primarina
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Nacli
|
||||
- Naclstack
|
||||
- Garganacl
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Tyranitar
|
||||
- Turtonator
|
||||
- Golett
|
||||
- Corsola
|
||||
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
|
||||
- ... and Substitute
|
||||
|
||||
#### Model changes to the regional bias resource pack:
|
||||
- Added Mime Jr. (Galar Bias)
|
||||
- Added Bergmite (Hisui Bias)
|
||||
- Changed models for every existing regional bias
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
|
||||
- Kakuna
|
||||
- Grimer
|
||||
- Nidoran♀ (+ New shoulder mount)
|
||||
- Nidorina
|
||||
- Nidoran♂ (+ New shoulder mount)
|
||||
- Sandshrew
|
||||
- Diglett
|
||||
- Dugtrio
|
||||
- Graveler
|
||||
- Happiny
|
||||
- Chansey
|
||||
- Blissey
|
||||
- Poliwhirl
|
||||
- Poliwrath
|
||||
- Zubat
|
||||
- Vulpix
|
||||
- Drowzee
|
||||
- Hypno
|
||||
- Noctowl
|
||||
- Skarmory
|
||||
- Skitty
|
||||
- Flygon
|
||||
- Shellos
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Joltik
|
||||
- Galvantula
|
||||
- Nidoking
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Archeops
|
||||
- Drampa
|
||||
- Sewaddle
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Shedinja
|
||||
- Skwovet
|
||||
- Greedent
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Luvdisc
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Togetic
|
||||
- Rellor
|
||||
- Rabsca
|
||||
- Yungoos
|
||||
- Gumshoos
|
||||
- Cottonee
|
||||
- Whimsicott
|
||||
- Corsola
|
||||
|
||||
#### Added sounds to the following moves
|
||||
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Draco Meteor
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
|
||||
### Changes
|
||||
- Removed the penalty for block destruction speed when on a hover mount, making them better for digging.
|
||||
- Golden Apples can now be crafted using the Applin Apples.
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes.
|
||||
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust.
|
||||
- Sharp Beak can now be crafted using bones and Flying Gems.
|
||||
- Metal Alloy can now be crafted using iron and Thunder Stones.
|
||||
- Ability Capsules can now be brewed from Medicinal Brew and Dragon Breath.
|
||||
- Using an Ability Capsule will now return an Empty Bottle.
|
||||
- Decreased the default pastured wander radius to 32 blocks.
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release.
|
||||
- Rooted Arch Ruins, Crumbling Arch Ruins, and all fossil structures have been improved so that they generate more naturally in terrain.
|
||||
- Gimmighoul towers now generate at ground-level rather than sinking one block into the ground.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- The Prehistoric Underwater Fissure and Prehistoric Submerged Impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves more easily.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma Cove gets Blast Burn, Lush Cove gets Frenzy Plant, and Submerged Cove gets Hydro Cannon.)
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
|
||||
- Phantom Membrane - Dreepy, Drakloak, Dragapult
|
||||
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
|
||||
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
|
||||
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples.
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame.
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this cleanup, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). This sucks, and we're sorry. We had to do this eventually so better now than later.
|
||||
- Forces between colliding hitboxes now factors in the weight of Pokémon.
|
||||
- Improved berry tree performance on singleplayer and servers.
|
||||
- Updated stat increase and decrease particle effects with the addition of alpha boost particles.
|
||||
- Improved the performance of pasture blocks on singleplayer and servers.
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts.
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level. No more free lunches with Pokémon that can't even hurt you.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen.
|
||||
- The party overlay's experience-gained animation now displays the amount of EXP the Pokémon gained.
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI.
|
||||
- Updated the sound for harvesting Apricorns.
|
||||
- Lowered the volume of harvesting berries.
|
||||
- Improved compatibility with various mods
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests.
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks.
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones.
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks.
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used.
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles.
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart.
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron.
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments).
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy.
|
||||
- Berry Trees are now compatible with the Planter properly
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues.
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items.
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets.
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create.
|
||||
- Rage Candy Bar has a Create Mixing recipe.
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels. All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems.
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed.
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders.
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops.
|
||||
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems.
|
||||
- Applin Apples can be used to make Honeyed Apples in Create.
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner. You probably didn't notice.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot.
|
||||
- Adjusted the walk speed for Croagunk and Aerodactyl.
|
||||
- The `/pc` command no longer plays PC activation sounds
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens.
|
||||
- Ice type Pokémon are now immune to Packed Snow.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
|
||||
### Fixes
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Fixed Omanyte and Omastar incorrectly using water animations on land.
|
||||
- Fixed Pokédex layout for genderless variations.
|
||||
- Fixed odd stacking behaviour with Poké Baits.
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed an issue where particles like beams can disappear when you don't see the beginning of the beam, or you look away.
|
||||
- Fixed Bedrock particle materials sometimes being incorrectly invisible from some sides.
|
||||
- Fixed an occasional crash on retrieving revived fossils.
|
||||
- Fixed brushing not working on Neoforge. Again.
|
||||
- Fixed braking not working for submarine-style mounts (whoops).
|
||||
- Fixed strafing not having the same friction as forward movement (funny whoops).
|
||||
- Fixed submarine-style mounts' surface-water animations not playing when supposed to.
|
||||
- Fixed submarine-style mounts' camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured Pokémon despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal them.
|
||||
- Fixed the issue that caused hover-style mounts without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Fixed horizontal and vertical collisions with hover-style mounts so they no longer accumulate or retain momentum.
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu. Well, I suppose all crashes are unexpected. You know what I mean.
|
||||
- Fixed NPCs switching Pokémon very often during battles.
|
||||
- Fixed Campfire Pot causing crashes under certain circumstances.
|
||||
- Fixed Type Gems not being consumed upon use in battles.
|
||||
- Fixed several Ancient Pokéball's not using the correct capture multiplier.
|
||||
- Fixed a server warning that could occur when recalling Pokémon.
|
||||
- Fixed sliding particles incorrectly handling block collision.
|
||||
- Fixed Full Heal not curing status conditions in battle. Literally it's one job.
|
||||
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation.
|
||||
- Fixed a very niche issue where structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed Galarian Ponyta missing its cry.
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT.
|
||||
- Tumblestone Clusters now display correctly on maps.
|
||||
- Fixed removing mulch from berry trees with a shovel not working. That was meant to be a thing.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full.
|
||||
- Fixed Stripped Saccharine Wood recipe being... not there.
|
||||
- Fixed some performance issues with a kind of sound event causing lag by attempting to create sounds every tick when out of range of hearing.
|
||||
- Fixed stat-reducing Aprijuice not being possible to apply on a maxed out Pokémon. Also fixed the possibility of going into negatives.
|
||||
- Fixed exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so.
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models.
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option.
|
||||
- Fixed gilded chests being more accessible than intended.
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokédex.
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokédex.
|
||||
- Fixed a crash involving berry trees that have been world edited.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks.
|
||||
- Fixed Pokémon entities leveled by rare candy not getting stronger as Minecraft entities unless recalled.
|
||||
- Fixed a freeze during game load that occurred when multiple mods tried to load Cobblemon API classes at the same time.
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry (shut up!).
|
||||
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon - shift clicking will use it as healing.
|
||||
- Resolved z-fighting (texture flickering) on Pokédex Models.
|
||||
- Restored Ninjask's ability to do barrel rolls.
|
||||
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine.
|
||||
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod.
|
||||
- Fixed Full Heal block not dropping Full Heals. Lmao.
|
||||
- Fixed status cure items not returning an empty bottle when used.
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone.
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe. Free bowls!
|
||||
- Fixed `/giveallpokemon` not respecting the min and max arguments.
|
||||
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen.
|
||||
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time.
|
||||
- Long config option labels now scroll instead of overflowing in the configuration menu.
|
||||
- Fixed some z-fighting and clipping on Dewott and Hisui-Bias Dewott.
|
||||
- Eggant Berries can now be composted properly on NeoForge.
|
||||
- Composting chances are now the same on NeoForge as on Fabric.
|
||||
- Fixed problem where furnaces which had smelted Revival Herbs into Heal Powder in older versions would glitch out on trying to get the experience out of the block.
|
||||
- Fixed the Battle AI not recognizing Surging Strikes as a multistrike move.
|
||||
- Fixed Tentacool not using its swim animation when on water surface.
|
||||
- Fixed Eevee using its left shoulder pose when in battle.
|
||||
- Fixed Magikarp and Gyarados Jump Pattern resourcepacks replacing shiny textures of addons targeting Magikarp or Gyarados.
|
||||
- Fixed Combee's pollinated texture overwriting the shiny and female textures.
|
||||
- Fixed a colour mistake with Dracovish's shiny.
|
||||
- Fixed Togedemaru's back spikes z-fighting.
|
||||
- Fixed Sliggoo (+ Hisuian) evolving in the Nether and End where it can't actually rain.
|
||||
- Corrected the scales, hitbox sizes, and eye heights for Ariados, Cetoddle, Durant, Galvantula, Klink, Lanturn, Lumineon, Mantine, Mantyke, Mudbray, Numel, Staravia, and Torkoal.
|
||||
- Fixed Torkoal's walk speed not matching its animation. He will no longer move faster than Bunnelby and Buneary. This tortoise would've won.
|
||||
- Fixed Lanturn T-posing due to missing ground walk and sleep animations.
|
||||
- Fixed Oshawott and Dewott using their sleep animation in unintended scenarios.
|
||||
- Fixed Dudunsparce and Maushold always having the same form instead of having the 1 in 100 chance of being different.
|
||||
- Fixed bird mounts rapidly switching between their flying and gliding animations when flying at low speed.
|
||||
- Fixed some battles being startable with fainted Pokémon in the initial sendout.
|
||||
- Fixed random gender selection for forms with strict gender divides (such as Meowstic and Oinkologne) never choosing female (that's sexist).
|
||||
- Fixed the "Press R to battle" prompt not disappearing after you finish your first battle, at least until you relogged.
|
||||
- Fixed the composter block being listed as "composer" in the cosmetic items species feature
|
||||
- Fixed Castform's forms being tagged as introduced in generation 4 instead of 3.
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`.
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly.
|
||||
- Fixed Litleo not blinking.
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters.
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters.
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts.
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
|
||||
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
|
||||
- Fixed riding camera offsets defined on Pokémon forms not being applied.
|
||||
- Marks can now provide aspects.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
|
||||
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
|
||||
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
|
||||
- Added basic chatter NPC Behaviour.
|
||||
- Added `q.item.is_enchanted()`.
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`.
|
||||
- Added `has_chosen_starter` and `get_starter_uuid` as available Molang functions.
|
||||
- Added `has_tm_move_unlocked`, `unlock_tm_move`, and `lock_tm_move` as available Molang functions for Players.
|
||||
- Added `marks`, `has_mark`, and `remove_marks` as available Molang functions for Pokémon.
|
||||
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers.
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen.
|
||||
- SpeciesAdditions append vs override now matches the wiki documentation.
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance.
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. Use type: `minecraft:chest` for the loot table.
|
||||
- Added `q.pokemon.entity` to access the entity of Pokémon.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up.
|
||||
- Ride sounds can now be set as exclusive to passengers.
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting.
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
- Added a q.failed function to move action effects.
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a pre-offset clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume, and pitch) instead of just the sound event and pitch.
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
- Blizzard
|
||||
- Disarming Voice
|
||||
- Draco Meteor
|
||||
- Dragon Breath
|
||||
- Dragon Rage
|
||||
- Drain Punch
|
||||
- Fairy Wind
|
||||
- Fire Fang
|
||||
- Gust
|
||||
- Horn Drill
|
||||
- Hydro Pump
|
||||
- Ice Fang
|
||||
- Leafage
|
||||
- Leaf Storm
|
||||
- Light Screen
|
||||
- Night Shade
|
||||
- Play Rough
|
||||
- Pyro Ball
|
||||
- Reflect
|
||||
- Rock Slide
|
||||
- Sonic Boom
|
||||
- Sucker Punch
|
||||
- Supersonic
|
||||
- Thunder Fang
|
||||
|
||||
#### Tweaks and Fixes (Moves)
|
||||
|
||||
- Confusion
|
||||
- Dragon Claw
|
||||
- Eruption
|
||||
- Fire Punch
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Frost Breath
|
||||
- Hypnosis
|
||||
- Ice Punch
|
||||
- Icy Wind
|
||||
- Mud Sport
|
||||
- Poison Fang
|
||||
- Protect
|
||||
- Psychic
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Sand Attack
|
||||
- Scratch
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Spore
|
||||
- String Shot
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunder Wave
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
|
||||
#### Miscellaneous Particles
|
||||
|
||||
- Added Wild Might particles for Alpha Pokémon.
|
||||
- Updated stat increase and decrease particles.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Japanese
|
||||
- German
|
||||
- Spanish
|
||||
- Ecuadorian Spanish
|
||||
- Portuguese
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Korean
|
||||
- Russian
|
||||
@@ -1,41 +0,0 @@
|
||||
# Changelog
|
||||
## [1.8.1 (September 13th, 2026)](#1-8-1)
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Flapple, Appletun, Dipplin, Hydrapple (placeholder animations only).
|
||||
|
||||
### Fixes
|
||||
- Fixed a potential world generation stall caused by Type Gem clusters.
|
||||
- Fixed Pokémon on shoulders not animating if they are visibly holding a held item.
|
||||
- Fixed NPCs getting their own TM progress file.
|
||||
- Fixed odd NeoForge crash due to a dependency being outdated. It will now correctly flag the dependency for being outdated.
|
||||
- Fixed the TM Machine refusing move selections that the `unlockAllMoveDexMovesByDefault` config had made visible, leaving the move slot blank and the craft button unresponsive.
|
||||
- Fixed the Beast Ball not applying its 0.1× penalty to Pokémon that are not Ultra Beasts.
|
||||
- Fixed the Moon Ball reading the world's total elapsed time instead of the time of day, so its bonus applied during the day instead.
|
||||
- Fixed the Love Ball never awarding its 2.5× opposite gender bonus, and matching species by display name instead of identifier.
|
||||
- Fixed the Timer Ball being weaker than a regular Poké Ball on early turns and only reaching 4× on turn 14 instead of turn 10.
|
||||
- Fixed the Luxury Ball only doubling Friendship gains of 1, leaving level up and berry gains unaffected.
|
||||
- Fixed Rufflet, Braviary, and Hisuian Braviary flooding logs with errors about missing sound events.
|
||||
- Fixed Pokémon property typos in Pangoro and Dedenne herd spawn files.
|
||||
- Fixed unintentional watermelon cosmetic for Hisuian Electrode.
|
||||
- Fixed Blastoise having an unintentional passenger seat.
|
||||
- Fixed form typo in Rockruff-Dusk spawn, preventing it from being able to spawn.
|
||||
- Fixed the `stat_compare` and `stat_equal` evolution requirements crashing the game when given `special_attack` or `special_defence`, and made them treat any non-permanent stat as a failed requirement instead of crashing.
|
||||
- Fixed Rowap and Jacoba berries not correctly altering Pokémon sizes when used as bait.
|
||||
- Fixed Ampharos' eyes not emitting particles.
|
||||
- Fixed Alolan Raticate's cry not playing.
|
||||
- Fixed several incorrect light-based spawning conditions for ghosts and bugs.
|
||||
- Removed unused, placeholder habitat pools.
|
||||
- Fixed Mantyke, Mantine, and Quaxly getting stuck in a recoil 'expression' during battles due to having an expression assigned as a recoil animation.
|
||||
- Fixed Lucario's aura particles not emitting.
|
||||
- Fixed Lycanroc's head movement.
|
||||
- Added head movement to Archaludon.
|
||||
- Fixed Duraludon line not having their sleep animations.
|
||||
- Fixed Diglett line not having their faint animations.
|
||||
- Fixed Kingambit moving its legs while walking.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Japanese
|
||||
@@ -1,211 +0,0 @@
|
||||
# Changelog
|
||||
## [1.8.2 (October 11th, 2026)](#1-8-2)
|
||||
|
||||
### Additions
|
||||
- The following items can now be placed on the ground as blocks:
|
||||
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Soft Sand, Electirizer, Magmarizer, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
|
||||
- I wasn't finished. Iron Ball, Metal Coat, Throat Spray, Terrain Extender, Medicinal Brew, Moomoo Milk, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
|
||||
- The following items can now be placed on various surfaces:
|
||||
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
|
||||
- The following items can now be placed as blocks and have additional functionality:
|
||||
- Ring Target, Metronome, and Eject Button.
|
||||
- The following items can now be worn by Players and Pokémon:
|
||||
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
|
||||
- Wearable items can now be equipped by a player by holding it and right-clicking (or whatever you've rebound use/interact to).
|
||||
- Wearable items that can be placed down are exempt from this behaviour.
|
||||
- Mint Leaves can now be placed on the ground as Leaf Litter, or in Flower Pots.
|
||||
- The recall animation now renders the ball in hand with the player's arm raised.
|
||||
- Added Oricorio Flowers.
|
||||
- Added Terrain Plants, four plants grown from Electric, Grassy, Misty, and Psychic Seeds, which glow under specific environmental conditions and can be farmed for more of their seeds.
|
||||
- Berries can now be placed in Flower Pots.
|
||||
- The evolution tree for a Pokémon can now be viewed in the Pokédex, showing the Pokémon's full family line and what's needed to evolve into each stage.
|
||||
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders, and dice.
|
||||
- Added paper as a new cosmetic item for Pikachu.
|
||||
- Burmy changes its cloak after a battle it took part in, based on the biome.
|
||||
- Trash Cloak in the Deep Dark, Nether and End.
|
||||
- Plant Cloak in lush biomes such as forests, jungles, plains and swamps.
|
||||
- Sandy Cloak in sandy biomes, beaches and caves.
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 4
|
||||
- Snover
|
||||
- Abomasnow
|
||||
- Burmy
|
||||
- Wormadam
|
||||
- Mothim
|
||||
|
||||
#### Generation 5
|
||||
- Tympole
|
||||
- Palpitoad
|
||||
- Seismitoad
|
||||
|
||||
#### Generation 6
|
||||
- Helioptile
|
||||
- Heliolisk
|
||||
|
||||
#### Generation 7
|
||||
- Grubbin
|
||||
- Charjabug
|
||||
- Vikavolt
|
||||
- Minior
|
||||
|
||||
#### Generation 8
|
||||
- Eternatus
|
||||
|
||||
#### Generation 9
|
||||
- Greavard
|
||||
- Houndstone
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Sudowoodo
|
||||
- Spheal
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Binacle
|
||||
- Barbaracle
|
||||
- Glimmet
|
||||
- Glimmora
|
||||
- Deerling
|
||||
- Sawsbuck (All forms)
|
||||
- Absol
|
||||
- Poliwag
|
||||
- Politoed
|
||||
- Ekans
|
||||
- Oddish
|
||||
- Gloom
|
||||
- Vileplume
|
||||
- Bellossom
|
||||
- Shroomish
|
||||
- Chespin
|
||||
- Quilladin
|
||||
- Chesnaught
|
||||
- Fennekin
|
||||
- Braixen
|
||||
- Delphox
|
||||
- Froakie
|
||||
- Frogadier
|
||||
- Greninja
|
||||
- Shinx
|
||||
- Luxio
|
||||
- Luxray
|
||||
- Slakoth
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Corsola
|
||||
- Mime Jr.
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Treecko (New Shoulder Mount!)
|
||||
- Bonsly (New Shoulder Mount!)
|
||||
- Sudowoodo
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Ekans
|
||||
- Arbok
|
||||
- Oddish
|
||||
- Gloom
|
||||
- Vileplume
|
||||
- Bellossom
|
||||
- Pichu
|
||||
- Pikachu
|
||||
- Raichu (+ Alolan)
|
||||
- Greninja
|
||||
- Slakoth
|
||||
|
||||
### Changes
|
||||
- Slowed the speed of Type Gem growth, and revamped their generation to grow in a more crystal-like shape.
|
||||
- Terrain Seeds can now be planted.
|
||||
- Terrain Seeds are now consumed during PvP and NPC battles.
|
||||
- Glow Item Frames can be crafted using Bright Powder as an alternative to Glow Ink Sacs.
|
||||
- Bright Powder can be used on Signs and Hanging Signs to make text glow, as an alternative to Glow Ink Sacs.
|
||||
- Updated various recipes to display in Recipe Groups within the Recipe Book.
|
||||
- Updated most recipes to display more logically and consistently within the Recipe Book's categories.
|
||||
- Updated the following recipes
|
||||
- Never-Melt Ice, Focus Sash, Blunder Policy, Weakness Policy, Blank TM.
|
||||
- Made ride rotations less jittery in multiplayer.
|
||||
- Changes Disc Shelf TMs to use textures instead of tinting and overlays to line up with item display.
|
||||
- Blank TM can now be inserted into the Disc Shelf.
|
||||
- Added a TM creative tab.
|
||||
- Made regional and Vivillon split-evo locations mutually exclusive.
|
||||
- Vivillon's patterns are now viewable as forms with form names and unique Pokédex entries displayed.
|
||||
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
|
||||
- Exceptions being "forced" evolutions which stop after the first one.
|
||||
- Removed Mega Drain as a TM.
|
||||
- PokeSnacks (and other area spawns) will now start the vertical offset checking from the center rather than base coordinate
|
||||
- This will help PokeSnacks to work inside caves or tighter spaces better.
|
||||
|
||||
### Fixes
|
||||
- Type Gem blocks now properly generate clusters off of the block.
|
||||
- Correctly grant "They're Minerals!" advancement when planting a Type Gem.
|
||||
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
|
||||
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
|
||||
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??).
|
||||
- Some other recipes without advancements have also been corrected.
|
||||
- Corrected the TM Recipes for Charge Beam and Payday.
|
||||
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport and you are not viewing the top or bottom edges.
|
||||
- The Random Starter option no longer runs off into other categories or becomes unaccessible due to scroll limits if it causes a new row.
|
||||
- Mochi and Feathers no longer drop glass bottles on use.
|
||||
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix compatibility issue.
|
||||
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
|
||||
- Fixed Rockruff line not blinking.
|
||||
- Fixed Flygon sometimes using its running animation instead of walking.
|
||||
- Fixed Bramblin always sleeping outside of battle.
|
||||
- Fixed Dragonair using its battle idle as a normal idle.
|
||||
- Fixed Lycanroc's head movement.
|
||||
- Added head movement to Archaludon.
|
||||
- Fixed Duraludon line not having any sleep animations.
|
||||
- Fixed Diglett line not having their faint animations.
|
||||
- Fixed Kingambit moving its legs while walking.
|
||||
- Fixed Alolan Diglett's cry not playing.
|
||||
- Fixed shiny alpha Snorlax using the non-shiny texture.
|
||||
- Fixed Crabominable's head breaking when looking around.
|
||||
- Fixed Shiny Dipplin and Hydrapple's textures.
|
||||
- Various structure fixes.
|
||||
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs.
|
||||
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations.
|
||||
- Fixed ball capture beam origin coming from behind the ball.
|
||||
- Fixed Safari Ball catch rate outside of battle.
|
||||
- Fixed alpha eye (and other) particles not respecting the client particle density setting in video options.
|
||||
- Fixed the TM Machine UI not closing when player is moving away from it.
|
||||
- Moved the config category for alpha/shiny chance config fields for fossil machine and honey log slathering to Spawning.
|
||||
- Fixed crashes for addon species with non-Cobblemon namespaces in evolution, fishing bait, and Pokédex lookups.
|
||||
- Bees will now grow Apricorns and the Galarica Nut Bush, which were in the bee growables tag but never handled, and correctly spend their nectar when they do.
|
||||
- Saccharine Leaves can now be bone mealed.
|
||||
- Fixed equipping a stack of wearable items instead of one item of a stack.
|
||||
- Fixed TM stack consumption in TM Machine screen when a player teaches a move to a Pokémon.
|
||||
- Fixed every Miltank giving mushroom stew, not only mushroom variants.
|
||||
- Medicinal Leeks can now be eaten when they aren't being planted, matching their food properties.
|
||||
- Sweeping attacks no longer set owned Pokémon on fire (e.g. with Fire Aspect) or knock them back. Friendly Fire is now off.
|
||||
- Fixed Pokémon not losing their previous form's signature moves when changing forms (e.g. Rotom keeping Overheat after leaving its Heat form).
|
||||
- Form-specific moves gained from a form change are now only granted if the Pokémon is high enough level to learn them, and no longer create duplicates in benched moves.
|
||||
- Spinda now learns Teeter Dance at level 32 instead of via TM.
|
||||
- Fixed Oricorio Pa'u form description in the Pokédex.
|
||||
- Fixed PC playing sound on close when opened via command.
|
||||
|
||||
### Developer
|
||||
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
|
||||
```json
|
||||
{
|
||||
"variant": "attack_defence_ratio",
|
||||
"pokedexIcon": "minecraft:diamond_sword",
|
||||
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
|
||||
}
|
||||
```
|
||||
- Fixed the wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing Pokémon to appear without the entity link.
|
||||
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon.
|
||||
|
||||
### Molang & Datapacks
|
||||
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
|
||||
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
|
||||
- Added a `battle_ended` callback, backed by the new `BATTLE_ENDED` event, which fires whenever a battle ends for any reason, alongside a multitude of flags not previously covered by `BATTLE_VICTORY`.
|
||||
- Added `battle_pokemon` to battle actor structs and `has_faced_opponents` to battle Pokémon structs.
|
||||
- Added the `cobblemon:form_change/bagworm_cloak/plant_cloak`, `sandy_cloak` and `trash_cloak` biome tags, which control Burmy's cloak after battle.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Brazilian Portuguese
|
||||
- Canadian French
|
||||
- Japanese
|
||||
- Korean
|
||||
- Russian
|
||||
- Ukrainian
|
||||
+6
-19
@@ -1,8 +1,9 @@
|
||||
|
||||
import utilities.isSnapshot
|
||||
import utilities.version
|
||||
import utilities.gitBranch
|
||||
import utilities.gitCommit
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/*
|
||||
*
|
||||
@@ -71,18 +72,6 @@ tasks.withType<Test> {
|
||||
}
|
||||
}
|
||||
|
||||
// Git info lives in its own output dir (not processResources) so a new commit only rewrites this one file.
|
||||
val writeGitInfo by tasks.registering(WriteProperties::class) {
|
||||
destinationFile = layout.buildDirectory.file("generated/gitInfo/cobblemon-git.properties")
|
||||
property("commit", gitCommit())
|
||||
property("branch", gitBranch())
|
||||
}
|
||||
sourceSets.main { output.dir(mapOf("builtBy" to writeGitInfo), layout.buildDirectory.dir("generated/gitInfo")) }
|
||||
|
||||
tasks.processResources {
|
||||
exclude("data/**/README.md")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
@@ -105,13 +94,11 @@ sourceSets {
|
||||
property("modid", "cobblemon")
|
||||
property("version", project.version())
|
||||
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
|
||||
property("gitCommit", versioning.info.commit)
|
||||
property("branch", versioning.info.branch)
|
||||
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
|
||||
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The platform jars are built from :common:jar, so the remapped common jars are only needed by `publish`
|
||||
tasks.assemble {
|
||||
setDependsOn(dependsOn.filterNot { (it as? TaskProvider<*>)?.name in setOf("remapJar", "remapSourcesJar") })
|
||||
}
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.camera;
|
||||
|
||||
import com.cobblemon.mod.common.client.util.compat.SodiumCompat;
|
||||
import com.cobblemon.mod.common.platform.events.ClientChunkEvent;
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class CameraClientChunkCacheExtension {
|
||||
private static final int MIN_RENDER_DISTANCE = 2;
|
||||
private static final int MAX_RENDER_DISTANCE = 64;
|
||||
private static final int RENDER_DISTANCE_MARGIN = 2;
|
||||
|
||||
private static final Long2ObjectOpenHashMap<LevelChunk> CHUNK_MAP = new Long2ObjectOpenHashMap<>();
|
||||
private static final Long2ObjectOpenHashMap<LevelChunk> CHUNK_MAP_OTHER_THREADS = new Long2ObjectOpenHashMap<>();
|
||||
private static volatile ChunkPos CAMERA_CENTER;
|
||||
|
||||
private CameraClientChunkCacheExtension() {}
|
||||
|
||||
public static void drop(ClientLevel level, ChunkPos chunkPos) {
|
||||
if (Minecraft.getInstance().isSameThread()) {
|
||||
long chunkPosLong = chunkPos.toLong();
|
||||
LevelChunk chunk = CHUNK_MAP.get(chunkPosLong);
|
||||
|
||||
if (chunk != null) {
|
||||
modifyChunkMaps(map -> map.remove(chunkPosLong));
|
||||
SodiumCompat.getInstance().notifyChunkRemoved(level, chunkPos.x, chunkPos.z);
|
||||
PlatformEvents.CLIENT_CHUNK_UNLOAD.postEvents(new ClientChunkEvent.Unload(chunk, level));
|
||||
level.unload(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk getChunk(int x, int z) {
|
||||
long chunkPos = ChunkPos.asLong(x, z);
|
||||
|
||||
if (Minecraft.getInstance().isSameThread())
|
||||
return CHUNK_MAP.get(chunkPos);
|
||||
else {
|
||||
synchronized (CHUNK_MAP_OTHER_THREADS) {
|
||||
return CHUNK_MAP_OTHER_THREADS.get(chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk replaceWithPacketData(ClientLevel level, int x, int z, FriendlyByteBuf packetData, CompoundTag chunkTag, Consumer<ClientboundLevelChunkPacketData.BlockEntityTagOutput> tagOutput) {
|
||||
if (!Minecraft.getInstance().isSameThread())
|
||||
throw new UnsupportedOperationException("replaceWithPacketData called off-thread, this shouldn't happen!");
|
||||
|
||||
ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
long longChunkPos = chunkPos.toLong();
|
||||
LevelChunk chunk = CHUNK_MAP.get(longChunkPos);
|
||||
|
||||
if (chunk == null) {
|
||||
LevelChunk newChunk = new LevelChunk(level, chunkPos);
|
||||
|
||||
chunk = newChunk;
|
||||
chunk.replaceWithPacketData(packetData, chunkTag, tagOutput);
|
||||
modifyChunkMaps(map -> map.put(longChunkPos, newChunk));
|
||||
}
|
||||
|
||||
level.onChunkLoaded(chunkPos);
|
||||
SodiumCompat.getInstance().notifyChunkAdded(level, x, z);
|
||||
PlatformEvents.CLIENT_CHUNK_LOAD.postEvents(new ClientChunkEvent.Load(chunk, level));
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private static void modifyChunkMaps(Consumer<Long2ObjectOpenHashMap<LevelChunk>> operation) {
|
||||
operation.accept(CHUNK_MAP);
|
||||
|
||||
synchronized (CHUNK_MAP_OTHER_THREADS) {
|
||||
operation.accept(CHUNK_MAP_OTHER_THREADS);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
CHUNK_MAP.clear();
|
||||
CHUNK_MAP_OTHER_THREADS.clear();
|
||||
CAMERA_CENTER = null;
|
||||
}
|
||||
|
||||
public static boolean hasCameraChunks() {
|
||||
if (Minecraft.getInstance().isSameThread()) {
|
||||
return !CHUNK_MAP.isEmpty();
|
||||
} else {
|
||||
synchronized (CHUNK_MAP_OTHER_THREADS) {
|
||||
return !CHUNK_MAP_OTHER_THREADS.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes render distance using Chebyshev distance with a safety margin.
|
||||
*/
|
||||
public static int getSuggestedRenderDistance(ChunkPos center) {
|
||||
int maxDistance = 0;
|
||||
|
||||
if (Minecraft.getInstance().isSameThread()) {
|
||||
for (long key : CHUNK_MAP.keySet()) {
|
||||
int x = ChunkPos.getX(key);
|
||||
int z = ChunkPos.getZ(key);
|
||||
int dx = Math.abs(x - center.x);
|
||||
int dz = Math.abs(z - center.z);
|
||||
maxDistance = Math.max(maxDistance, Math.max(dx, dz));
|
||||
}
|
||||
} else {
|
||||
synchronized (CHUNK_MAP_OTHER_THREADS) {
|
||||
for (long key : CHUNK_MAP_OTHER_THREADS.keySet()) {
|
||||
int x = ChunkPos.getX(key);
|
||||
int z = ChunkPos.getZ(key);
|
||||
int dx = Math.abs(x - center.x);
|
||||
int dz = Math.abs(z - center.z);
|
||||
maxDistance = Math.max(maxDistance, Math.max(dx, dz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(MIN_RENDER_DISTANCE, Math.min(MAX_RENDER_DISTANCE, maxDistance + RENDER_DISTANCE_MARGIN));
|
||||
}
|
||||
|
||||
public static void setCameraCenter(ChunkPos center) {
|
||||
CAMERA_CENTER = center;
|
||||
}
|
||||
|
||||
public static ChunkPos getCameraCenter() {
|
||||
return CAMERA_CENTER;
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.camera;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class CameraViewAreaExtension {
|
||||
private static final Long2ObjectOpenHashMap<SectionRenderDispatcher.RenderSection> SECTIONS = new Long2ObjectOpenHashMap<>();
|
||||
private static SectionRenderDispatcher sectionRenderDispatcher;
|
||||
private static int minSectionY;
|
||||
private static int maxSectionY;
|
||||
|
||||
private CameraViewAreaExtension() {
|
||||
}
|
||||
|
||||
public static void allChanged(SectionRenderDispatcher dispatcher, Level level) {
|
||||
clear();
|
||||
sectionRenderDispatcher = dispatcher;
|
||||
minSectionY = level.getMinSection();
|
||||
maxSectionY = level.getMaxSection();
|
||||
}
|
||||
|
||||
public static SectionRenderDispatcher.RenderSection rawFetch(int cx, int cy, int cz, boolean generateNew) {
|
||||
if (cy < minSectionY || cy >= maxSectionY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long sectionPos = SectionPos.asLong(cx, cy, cz);
|
||||
return generateNew ? provideSection(sectionPos) : SECTIONS.get(sectionPos);
|
||||
}
|
||||
|
||||
public static void setDirty(int cx, int cy, int cz, boolean playerChanged) {
|
||||
SectionRenderDispatcher.RenderSection section = rawFetch(cx, cy, cz, false);
|
||||
if (section != null) {
|
||||
section.setDirty(playerChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkUnload(int sectionX, int sectionZ) {
|
||||
for (int sectionY = minSectionY; sectionY < maxSectionY; sectionY++) {
|
||||
long sectionPos = SectionPos.asLong(sectionX, sectionY, sectionZ);
|
||||
SectionRenderDispatcher.RenderSection section = SECTIONS.remove(sectionPos);
|
||||
|
||||
if (section != null) {
|
||||
section.releaseBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
for (SectionRenderDispatcher.RenderSection section : SECTIONS.values()) {
|
||||
section.releaseBuffers();
|
||||
}
|
||||
SECTIONS.clear();
|
||||
}
|
||||
|
||||
public static int minSectionY() {
|
||||
return minSectionY;
|
||||
}
|
||||
|
||||
public static int maxSectionY() {
|
||||
return maxSectionY;
|
||||
}
|
||||
|
||||
private static SectionRenderDispatcher.RenderSection provideSection(long sectionPos) {
|
||||
return SECTIONS.computeIfAbsent(sectionPos, CameraViewAreaExtension::createSection);
|
||||
}
|
||||
|
||||
private static SectionRenderDispatcher.RenderSection createSection(long sectionPos) {
|
||||
if (sectionRenderDispatcher == null) {
|
||||
throw new IllegalStateException("SectionRenderDispatcher not initialised");
|
||||
}
|
||||
BlockPos origin = SectionPos.of(sectionPos).origin();
|
||||
return sectionRenderDispatcher.new RenderSection(0, origin.getX(), origin.getY(), origin.getZ());
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.camera.accessor;
|
||||
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
|
||||
public interface CameraLevelRendererAccessor {
|
||||
Frustum cobblemonGetFrustum();
|
||||
}
|
||||
@@ -13,6 +13,5 @@ public interface ChannelDuck {
|
||||
void cobblemon$inverseAttenuation(float rolloffFactor);
|
||||
void cobblemon$clearFilters();
|
||||
|
||||
void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density);
|
||||
void cobblemon$clearReverb();
|
||||
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Duck that facilitates getting a level key for a given level.
|
||||
*
|
||||
* @author dualspiral
|
||||
* @since August 30th, 2026
|
||||
*/
|
||||
public interface MinecraftServerDuck {
|
||||
Optional<ResourceKey<Level>> cobblemon$getLevelKey(Level level);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Duck that facilitates getting a level key that we store on the level.
|
||||
*
|
||||
* @author dualspiral
|
||||
* @since August 30th, 2026
|
||||
*/
|
||||
public interface ServerLevelDuck {
|
||||
ResourceLocation cobblemon$getLevelResourceLocation();
|
||||
}
|
||||
@@ -8,42 +8,54 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.animal.Bee;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.BonemealableBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeeGrowCropGoal")
|
||||
public abstract class BeeEntityMixin {
|
||||
|
||||
@Shadow(aliases = {"this$0", "field_20373"}) @Final private Bee bee;
|
||||
@Unique
|
||||
private BlockState cobblemon$result = null;
|
||||
|
||||
@Inject(method = "tick()V", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/block/BonemealableBlock;performBonemeal(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V",
|
||||
shift = At.Shift.BY,
|
||||
by = 2
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void injectCustomGrowth(CallbackInfo ci, int i, BlockPos blockPos, BlockState blockState, Block block) {
|
||||
if (block instanceof SaccharineLeafBlock) {
|
||||
|
||||
int age = blockState.getValue(SaccharineLeafBlock.Companion.getAGE());
|
||||
boolean waterlogged = blockState.getValue(BlockStateProperties.WATERLOGGED);
|
||||
|
||||
if (age < 2 && !waterlogged) {
|
||||
this.cobblemon$result = blockState.setValue(SaccharineLeafBlock.Companion.getAGE(), age + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grows bonemealable blocks vanilla's hardcoded checks miss, returning the state so it handles the rest.
|
||||
@ModifyVariable(method = "tick", at = @At(value = "LOAD", ordinal = 0), index = 5)
|
||||
private BlockState cobblemon$growBonemealableBlocks(BlockState grownState, @Local(ordinal = 0) BlockState blockState, @Local BlockPos pos) {
|
||||
if (grownState != null || blockState.is(Blocks.CAVE_VINES) || blockState.is(Blocks.CAVE_VINES_PLANT)) {
|
||||
return grownState;
|
||||
private BlockState applyCustomBlockState(BlockState before) {
|
||||
if(this.cobblemon$result != null && before == null) {
|
||||
BlockState result = this.cobblemon$result;
|
||||
this.cobblemon$result = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!(blockState.getBlock() instanceof BonemealableBlock bonemealable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerLevel level = (ServerLevel) this.bee.level();
|
||||
if (!bonemealable.isValidBonemealTarget(level, pos, blockState)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bonemealable.performBonemeal(level, this.bee.getRandom(), pos, blockState);
|
||||
return level.getBlockState(pos);
|
||||
return before;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.EXTEfx;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Channel.class)
|
||||
public abstract class ChannelMixin implements ChannelDuck {
|
||||
|
||||
@Shadow
|
||||
private int source;
|
||||
|
||||
// Id of filter applied when sound is obstructed
|
||||
private int cobblemon$lowPassFilterId = 0;
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
|
||||
// Delete old filter if it exists
|
||||
if (cobblemon$lowPassFilterId != 0) {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0); // Detach filter from source
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId); // Delete old filter
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
|
||||
// Generate a fresh filter
|
||||
int filterId = EXTEfx.alGenFilters();
|
||||
if (!EXTEfx.alIsFilter(filterId)) {
|
||||
System.err.println("Failed to create OpenAL low-pass filter");
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alFilteri(filterId, EXTEfx.AL_FILTER_TYPE, EXTEfx.AL_FILTER_LOWPASS);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAIN, gain);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAINHF, hfGain);
|
||||
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, filterId);
|
||||
|
||||
// Save the new filter ID
|
||||
cobblemon$lowPassFilterId = filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$inverseAttenuation(float rolloffFactor) {
|
||||
AL10.alSourcei(source, AL10.AL_DISTANCE_MODEL, AL10.AL_INVERSE_DISTANCE);
|
||||
AL10.alSourcef(source, AL10.AL_ROLLOFF_FACTOR, rolloffFactor);
|
||||
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, 1.0f);
|
||||
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$clearFilters() {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0);
|
||||
|
||||
if (EXTEfx.alIsFilter(cobblemon$lowPassFilterId)) {
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId);
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
@@ -46,24 +45,4 @@ public class ChunkStorageMixin {
|
||||
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
|
||||
return DataFixTypes.CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
|
||||
}
|
||||
|
||||
// Our update on the same MC version still needs its own fixes so run them here in that case.
|
||||
@Inject(
|
||||
method = "upgradeChunkTag(Lnet/minecraft/resources/ResourceKey;Ljava/util/function/Supplier;Lnet/minecraft/nbt/CompoundTag;Ljava/util/Optional;)Lnet/minecraft/nbt/CompoundTag;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$doChunkFixOnCurrentVersion(
|
||||
ResourceKey<Level> levelKey,
|
||||
Supplier<DimensionDataStorage> storage,
|
||||
CompoundTag chunkData,
|
||||
Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey,
|
||||
CallbackInfoReturnable<CompoundTag> cir
|
||||
) {
|
||||
if (ChunkStorage.getVersion(chunkData) != SharedConstants.getCurrentVersion().getDataVersion().getVersion()) {
|
||||
return;
|
||||
}
|
||||
int curVersion = chunkData.contains(VERSION_KEY) ? chunkData.getInt(VERSION_KEY) : 0;
|
||||
cir.setReturnValue(DataFixTypes.CHUNK.update(CobblemonSchemas.getDATA_FIXER(), chunkData, curVersion, CobblemonSchemas.DATA_VERSION));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(ClientPacketListener.class)
|
||||
public class ClientPacketListenerMixin {
|
||||
|
||||
@Shadow private ClientLevel level;
|
||||
|
||||
@Inject(method = "handleSetEntityPassengersPacket", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/Entity;hasIndirectPassenger(Lnet/minecraft/world/entity/Entity;)Z", shift = At.Shift.BEFORE), cancellable = true)
|
||||
private void cobblemon$handlePokemonPassengers(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
|
||||
Entity entity = this.level.getEntity(packet.getVehicle());
|
||||
if (entity instanceof PokemonEntity pokemon) {
|
||||
var passengers = new ArrayList<Entity>();
|
||||
for (int passenger : packet.getPassengers()) {
|
||||
passengers.add(this.level.getEntity(passenger));
|
||||
}
|
||||
Arrays
|
||||
.stream(pokemon.getOccupiedSeats())
|
||||
.filter(x -> !passengers.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(Entity::stopRiding);
|
||||
|
||||
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
|
||||
passengers.stream()
|
||||
.filter(x -> !occupiedSeats.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(x -> x.startRiding(pokemon, true));
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin implements MinecraftServerDuck {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<ResourceKey<Level>, ServerLevel> levels;
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
|
||||
return this.levels.entrySet()
|
||||
.stream()
|
||||
// Reference equality is intended here, the level we're handed is the same one as in the
|
||||
// map so we don't need to check value semantics.
|
||||
.filter(entry -> entry.getValue() == level)
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.NoteBlock;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(NoteBlock.class)
|
||||
public abstract class NoteBlockMixin {
|
||||
@Inject(method = "triggerEvent", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$triggerTMShelfSequencer(BlockState state, Level level, BlockPos pos, int type, int data, CallbackInfoReturnable<Boolean> cir) {
|
||||
final BlockPos shelfPos = pos.below();
|
||||
final BlockEntity blockEntity = level.getBlockEntity(shelfPos);
|
||||
if (!(blockEntity instanceof DiscShelfBlockEntity discShelfBlockEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!level.isClientSide) {
|
||||
final BlockPos instrumentPos = shelfPos.below();
|
||||
final SoundEvent soundEvent;
|
||||
if (level.getBlockState(instrumentPos).isAir()) {
|
||||
soundEvent = SoundEvents.NOTE_BLOCK_BIT.value();
|
||||
} else {
|
||||
final NoteBlockInstrument instrument = level.getBlockState(instrumentPos).instrument();
|
||||
final Holder<SoundEvent> holder = instrument.getSoundEvent();
|
||||
soundEvent = holder.value();
|
||||
}
|
||||
discShelfBlockEntity.onNoteBlockPulse(level, pos, soundEvent);
|
||||
}
|
||||
|
||||
// Suppress vanilla note-block playback while it is on top of a TM Shelf.
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
@@ -35,16 +35,13 @@ import com.cobblemon.mod.common.util.DataKeys;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
@@ -53,7 +50,6 @@ import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -375,33 +371,4 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
this.cobblemon$rideEyePos = rideEyePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeping attacks apply post-attack enchantment effects (e.g. Fire Aspect) to every entity caught
|
||||
* in the sweep, even if the damage itself was blocked. Skip them for Pokémon immune to the attack.
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "attack",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/EnchantmentHelper;doPostAttackEffects(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)V")
|
||||
)
|
||||
private void cobblemon$skipPostAttackEffectsOnImmunePokemon(ServerLevel level, Entity target, DamageSource source, Operation<Void> original) {
|
||||
if (target instanceof PokemonEntity pokemonEntity && pokemonEntity.isInvulnerableTo(source)) {
|
||||
return;
|
||||
}
|
||||
original.call(level, target, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeping attacks also knock back every entity caught in the sweep regardless of whether it took damage.
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "attack",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")
|
||||
)
|
||||
private void cobblemon$skipKnockbackOnImmunePokemon(LivingEntity target, double strength, double x, double z, Operation<Void> original, @Local DamageSource source) {
|
||||
if (target instanceof PokemonEntity pokemonEntity && pokemonEntity.isInvulnerableTo(source)) {
|
||||
return;
|
||||
}
|
||||
original.call(target, strength, x, z);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClientboundSeatAssignmentPacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -39,8 +37,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -57,15 +53,12 @@ public abstract class ServerEntityMixin {
|
||||
private Matrix3f cobblemon$lastSentOrientation;
|
||||
@Unique
|
||||
private boolean cobblemon$lastSentActive;
|
||||
@Unique
|
||||
private List<Entity> cobblemon$lastSeatPassengers = List.of();
|
||||
|
||||
@Inject(method = "sendChanges", at = @At("TAIL"))
|
||||
private void cobblemon$sendChanges(CallbackInfo ci) {
|
||||
cobblemon$sendOrientationChanges();
|
||||
cobblemon$sendRidingStateChanges();
|
||||
cobblemon$sendDriverInput();
|
||||
cobblemon$sendOccupiedSeatsChange();
|
||||
}
|
||||
|
||||
private void cobblemon$sendRidingStateChanges() {
|
||||
@@ -108,22 +101,6 @@ public abstract class ServerEntityMixin {
|
||||
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(driverInput, entity.getId()));
|
||||
}
|
||||
|
||||
private void cobblemon$sendOccupiedSeatsChange() {
|
||||
if (!(this.entity instanceof PokemonEntity pokemon)) return;
|
||||
|
||||
List<Entity> currentPassengers = pokemon.getPassengers();
|
||||
if (currentPassengers.equals(cobblemon$lastSeatPassengers)) return;
|
||||
cobblemon$lastSeatPassengers = List.copyOf(currentPassengers);
|
||||
|
||||
var assignments = new LinkedHashMap<String, Integer>();
|
||||
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
|
||||
if (seat.getLocator() == null) return;
|
||||
if (passenger == null) return;
|
||||
assignments.put(seat.getLocator(), passenger.getId());
|
||||
});
|
||||
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(pokemon.getId(), assignments));
|
||||
}
|
||||
|
||||
private void cobblemon$broadcast(NetworkPacket<?> packet) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
@@ -138,4 +115,5 @@ public abstract class ServerEntityMixin {
|
||||
CobblemonNetwork.INSTANCE.sendPacketToPlayer(player, packet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,69 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
|
||||
import com.cobblemon.mod.common.duck.ServerLevelDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.WritableLevelData;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelMixin extends Level implements ServerLevelDuck {
|
||||
|
||||
// Ignored by Mixin, fulfils Java contract
|
||||
protected ServerLevelMixin(
|
||||
WritableLevelData levelData,
|
||||
ResourceKey<Level> dimension,
|
||||
RegistryAccess registryAccess,
|
||||
Holder<DimensionType> dimensionTypeRegistration,
|
||||
Supplier<ProfilerFiller> profiler,
|
||||
boolean isClientSide,
|
||||
boolean isDebug,
|
||||
long biomeZoomSeed,
|
||||
int maxChainedNeighborUpdates
|
||||
) {
|
||||
super(levelData, dimension, registryAccess, dimensionTypeRegistration, profiler, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public abstract MinecraftServer shadow$getServer();
|
||||
|
||||
/**
|
||||
* Cached to speed up level world key checks every tick.
|
||||
*/
|
||||
private ResourceLocation cobblemon$levelResourceLocation;
|
||||
|
||||
@Override
|
||||
public ResourceLocation cobblemon$getLevelResourceLocation() {
|
||||
if (this.cobblemon$levelResourceLocation == null) {
|
||||
this.cobblemon$levelResourceLocation = ((MinecraftServerDuck) this.shadow$getServer())
|
||||
.cobblemon$getLevelKey(this)
|
||||
.map(ResourceKey::location)
|
||||
.orElse(null);
|
||||
}
|
||||
return this.cobblemon$levelResourceLocation;
|
||||
}
|
||||
public abstract class ServerLevelMixin {
|
||||
|
||||
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
|
||||
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
|
||||
|
||||
@@ -15,18 +15,15 @@ import com.cobblemon.mod.common.api.riding.RidingStyle;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
|
||||
import com.cobblemon.mod.common.api.stats.CobblemonStats;
|
||||
import com.cobblemon.mod.common.duck.ServerLevelDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -36,13 +33,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayer.class)
|
||||
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
@Shadow
|
||||
public abstract ServerLevel serverLevel();
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
public MinecraftServer server;
|
||||
@Unique
|
||||
public PlayerSpawner cobblemon$spawner;
|
||||
|
||||
@@ -62,9 +55,12 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void cobblemon$tickSpawner(CallbackInfo ci) {
|
||||
if (cobblemon$canSpawn()) {
|
||||
getPlayerSpawner().tick();
|
||||
if (!Cobblemon.config.getEnableSpawning()) {
|
||||
return;
|
||||
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
|
||||
return;
|
||||
}
|
||||
getPlayerSpawner().tick();
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
@@ -125,21 +121,4 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether we should spawn Cobblemon entities here.
|
||||
*
|
||||
* @return true if we can spawn cobblemon in the current level,
|
||||
* false otherwise
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
@Unique
|
||||
private boolean cobblemon$canSpawn() {
|
||||
return Cobblemon.config.getEnableSpawning() &&
|
||||
this.serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) &&
|
||||
// intentionally last as this is a more expensive check, even with all the caching
|
||||
!Cobblemon.config.getWorldSpawningBlocklist().contains(
|
||||
((ServerLevelDuck) this.serverLevel()).cobblemon$getLevelResourceLocation()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import net.minecraft.client.sounds.ChannelAccess;
|
||||
import net.minecraft.client.sounds.SoundEngine;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -33,7 +32,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -110,17 +108,6 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TM Shelf can intentionally push note-block pitches above vanilla's 2.0f ceiling for octave-up playback.
|
||||
* Keep vanilla clamping for all other sounds.
|
||||
*/
|
||||
@Inject(method = "calculatePitch", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$allowExtendedNoteblockPitch(SoundInstance sound, CallbackInfoReturnable<Float> cir) {
|
||||
if (sound.getLocation().getPath().startsWith("block.note_block.")) {
|
||||
cir.setReturnValue(Mth.clamp(sound.getPitch(), 0.5F, 4.0F));
|
||||
}
|
||||
}
|
||||
|
||||
/** Different behavior for resuming SoundInstances while a BattleMusicInstance is being played. We do not want to resume filtered sounds. */
|
||||
@Inject(method = "resume()V", at = @At("HEAD"), cancellable = true)
|
||||
public void resume(CallbackInfo cb) {
|
||||
|
||||
@@ -8,25 +8,21 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.mixin.accessor.SinglePoolElementAccessor;
|
||||
import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor;
|
||||
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -34,70 +30,38 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Structure.class)
|
||||
public abstract class StructureMixin {
|
||||
// Define a set of structures that should not spawn below Sea Level
|
||||
@Unique
|
||||
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
|
||||
CobblemonStructureIDs.STONJOURNER_HENGE,
|
||||
CobblemonStructureIDs.LUNA_HENGE,
|
||||
CobblemonStructureIDs.SOL_HENGE
|
||||
));
|
||||
|
||||
/**
|
||||
* Discards a structure start when any of its pieces carries a {@link HeightRangeStructureProcessor}
|
||||
* whose range the piece does not fit in.
|
||||
* Crab Note: We have to make this has to happen here rather than in the processor itself. Terrain adaptation and {@code /locate}
|
||||
* both read the start, so by the time processors run the terrain has already been generated and the
|
||||
* structure is already on the map which previously were left behdind when we were trying to make some structures not generate via processor. Now
|
||||
* Returning {@link StructureStart#INVALID_START} keeps the start from ever existing, which makes sure that weird generation isn't left behind.
|
||||
*/
|
||||
@Inject(
|
||||
method = "generate",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$rejectStartsOutsideHeightRange(
|
||||
RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
|
||||
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
|
||||
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
|
||||
Predicate<Holder<Biome>> validBiome,
|
||||
CallbackInfoReturnable<StructureStart> cir,
|
||||
@Local StructureStart structureStart
|
||||
) {
|
||||
for (StructurePiece piece : structureStart.getPieces()) {
|
||||
if (!(piece instanceof PoolElementStructurePiece poolPiece)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<HeightRangeStructureProcessor> heightRangeProcessor = cobblemon$getHeightRangeProcessor(poolPiece.getElement());
|
||||
if (heightRangeProcessor.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean valid = heightRangeProcessor.get().isValidPlacement(
|
||||
chunkGenerator,
|
||||
heightAccessor,
|
||||
randomState,
|
||||
piece.getBoundingBox()
|
||||
);
|
||||
|
||||
if (!valid) {
|
||||
cir.setReturnValue(StructureStart.INVALID_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Optional<HeightRangeStructureProcessor> cobblemon$getHeightRangeProcessor(StructurePoolElement element) {
|
||||
if (!(element instanceof SinglePoolElement)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value();
|
||||
for (StructureProcessor processor : processorList.list()) {
|
||||
if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) {
|
||||
return Optional.of(heightRangeStructureProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
|
||||
cancellable = true)
|
||||
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
|
||||
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
|
||||
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
|
||||
Predicate<Holder<Biome>> validBiome,
|
||||
CallbackInfoReturnable<StructureStart> cir,
|
||||
@Local StructureStart structureStart) {
|
||||
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
|
||||
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
|
||||
return;
|
||||
}
|
||||
for (StructurePiece piece : structureStart.getPieces()) {
|
||||
if (piece.getBoundingBox().minY() < chunkGenerator.getSeaLevel()) {
|
||||
cir.setReturnValue(StructureStart.INVALID_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public interface CameraAccessor {
|
||||
@Accessor("level")
|
||||
BlockGetter getLevel();
|
||||
|
||||
@Accessor("detached")
|
||||
void setDetached(boolean detached);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public interface GameRendererAccessor {
|
||||
@Accessor("mainCamera")
|
||||
Camera cobblemon$getMainCamera();
|
||||
|
||||
@Accessor("mainCamera")
|
||||
void cobblemon$setMainCamera(Camera camera);
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(SinglePoolElement.class)
|
||||
public interface SinglePoolElementAccessor {
|
||||
@Accessor("processors")
|
||||
Holder<StructureProcessorList> getProcessors();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.camera;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.cobblemon.mod.common.api.camera.CameraManager;
|
||||
import com.cobblemon.mod.common.api.camera.CameraManager.ChunkViewUpdate;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTrackingView;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@Mixin(value = ChunkMap.class, priority = 1100)
|
||||
public abstract class ChunkMapMixin {
|
||||
private static final Logger COBBLEMON$CAMERA_LOGGER = LogUtils.getLogger();
|
||||
|
||||
@Shadow
|
||||
protected abstract void markChunkPendingToSend(ServerPlayer player, ChunkPos pos);
|
||||
|
||||
@Shadow
|
||||
private static void markChunkPendingToSend(ServerPlayer player, LevelChunk chunk) {}
|
||||
|
||||
@Shadow
|
||||
private static void dropChunk(ServerPlayer player, ChunkPos chunkPos) {}
|
||||
|
||||
@Inject(method = "updateChunkTracking", at = @At("HEAD"))
|
||||
private void cobblemon$handleCameraChunkUpdates(ServerPlayer player, CallbackInfo ci) {
|
||||
ChunkViewUpdate update = CameraManager.consumeChunkViewUpdate(player);
|
||||
|
||||
if (update != null) {
|
||||
ChunkTrackingView playerView = player.getChunkTrackingView();
|
||||
ChunkTrackingView current = update.getCurrent();
|
||||
ChunkTrackingView previous = update.getPrevious();
|
||||
|
||||
if (current != null) {
|
||||
ChunkTrackingView.difference(playerView, current, chunkPos -> cobblemon$queueChunkForPlayer(player, chunkPos), chunkPos -> {});
|
||||
}
|
||||
|
||||
if (previous != null) {
|
||||
Set<ChunkPos> droppingChunks = new HashSet<>();
|
||||
|
||||
ChunkTrackingView targetComparison = current != null ? current : playerView;
|
||||
ChunkTrackingView.difference(previous, targetComparison, chunkPos -> {}, droppingChunks::add);
|
||||
|
||||
for (ChunkPos chunkPos : droppingChunks) {
|
||||
if (playerView == null || !playerView.contains(chunkPos)) {
|
||||
dropChunk(player, chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onChunkReadyToSend", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getChunkTrackingView()Lnet/minecraft/server/level/ChunkTrackingView;"))
|
||||
private void cobblemon$queueCameraChunk(LevelChunk chunk, CallbackInfo ci, @Local ServerPlayer player) {
|
||||
ChunkTrackingView view = CameraManager.getPlayerChunkView(player);
|
||||
|
||||
if (view != null && view.contains(chunk.getPos())) {
|
||||
markChunkPendingToSend(player, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "isChunkTracked", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$isCameraChunkTracked(ServerPlayer player, int x, int z, CallbackInfoReturnable<Boolean> cir) {
|
||||
ChunkTrackingView view = CameraManager.getPlayerChunkView(player);
|
||||
|
||||
if (view != null && view.contains(x, z) && !player.connection.chunkSender.isPending(ChunkPos.asLong(x, z))) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "dropChunk", at = @At("HEAD"), cancellable = true)
|
||||
private static void cobblemon$preventCameraChunkDrop(ServerPlayer player, ChunkPos pos, CallbackInfo ci) {
|
||||
ChunkTrackingView view = CameraManager.getPlayerChunkView(player);
|
||||
|
||||
if (view != null && view.contains(pos)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void cobblemon$queueChunkForPlayer(ServerPlayer player, ChunkPos chunkPos) {
|
||||
markChunkPendingToSend(player, chunkPos);
|
||||
}
|
||||
}
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraFeedHandler;
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraViewAreaExtension;
|
||||
import com.cobblemon.mod.common.client.render.camera.accessor.CameraLevelRendererAccessor;
|
||||
import com.cobblemon.mod.common.client.util.compat.SodiumCompat;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.ViewArea;
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = LevelRenderer.class, priority = 1100)
|
||||
public abstract class CameraLevelRendererMixin implements CameraLevelRendererAccessor {
|
||||
private static final int CINEMATIC_RENDER_DISTANCE_CHUNKS = 32;
|
||||
private static final float CINEMATIC_FOG_DISTANCE = CINEMATIC_RENDER_DISTANCE_CHUNKS * 16;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Minecraft minecraft;
|
||||
@Shadow
|
||||
private SectionRenderDispatcher sectionRenderDispatcher;
|
||||
@Shadow
|
||||
private ClientLevel level;
|
||||
|
||||
@Shadow private Frustum capturedFrustum;
|
||||
|
||||
@Shadow public Frustum cullingFrustum;
|
||||
|
||||
@WrapWithCondition(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ViewArea;repositionCamera(DD)V"))
|
||||
private boolean cobblemon$shouldRepositionCamera(ViewArea viewArea, double x, double z) {
|
||||
return !CameraFeedHandler.isCapturing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel vanilla setupRender during feed capture to avoid overwriting
|
||||
* the camera feed's visible sections. When Sodium is active, let it
|
||||
* proceed since Sodium manages its own chunk rendering pipeline and
|
||||
* needs setupRender to run.
|
||||
*/
|
||||
@Inject(method = "setupRender", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$onSetupRender(Camera camera, Frustum frustum, boolean hasCapturedFrustum, boolean isSpectator, CallbackInfo ci) {
|
||||
if (CameraFeedHandler.isCapturing() && !SodiumCompat.getInstance().isActive())
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "allChanged", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/SectionOcclusionGraph;waitAndReset(Lnet/minecraft/client/renderer/ViewArea;)V"))
|
||||
private void cobblemon$onAllChanged(CallbackInfo ci) {
|
||||
CameraViewAreaExtension.allChanged(sectionRenderDispatcher, level);
|
||||
}
|
||||
|
||||
@Inject(method = "isSectionCompiled", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$onIsSectionCompiled(BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (CameraFeedHandler.isCapturing()) {
|
||||
SectionPos sectionPos = SectionPos.of(pos);
|
||||
SectionRenderDispatcher.RenderSection section = CameraViewAreaExtension.rawFetch(sectionPos.x(), sectionPos.y(), sectionPos.z(), false);
|
||||
|
||||
if (section != null && section.getCompiled() != SectionRenderDispatcher.CompiledSection.UNCOMPILED) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/FogRenderer;setupColor(Lnet/minecraft/client/Camera;FLnet/minecraft/client/multiplayer/ClientLevel;IF)V"), ordinal = 1)
|
||||
private float cobblemon$modifyFogRenderDistance(float original) {
|
||||
if (CameraFeedHandler.isCapturing())
|
||||
return CINEMATIC_FOG_DISTANCE;
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* During feed capture the camera entity is a Marker, which causes the vanilla condition
|
||||
* {@code !(entity instanceof LocalPlayer) || camera.getEntity() == entity} to fail for
|
||||
* the local player — preventing them from rendering.
|
||||
*
|
||||
* By returning the local player from {@code getEntity()} and {@code true} from
|
||||
* {@code isDetached()}, both condition2 and condition3 in the entity rendering check pass:
|
||||
* <ul>
|
||||
* <li>condition2: {@code entity != player || isDetached()==true} → passes</li>
|
||||
* <li>condition3: {@code player == player} → passes</li>
|
||||
* </ul>
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "renderLevel",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;getEntity()Lnet/minecraft/world/entity/Entity;")
|
||||
)
|
||||
private Entity cobblemon$getCameraEntityDuringCapture(Camera camera, Operation<Entity> original) {
|
||||
if (CameraFeedHandler.isCapturing()) {
|
||||
Entity player = Minecraft.getInstance().player;
|
||||
if (player != null) return player;
|
||||
}
|
||||
return original.call(camera);
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "renderLevel",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;isDetached()Z")
|
||||
)
|
||||
private boolean cobblemon$isCameraDetachedDuringCapture(Camera camera, Operation<Boolean> original) {
|
||||
if (CameraFeedHandler.isCapturing()) {
|
||||
return true;
|
||||
}
|
||||
return original.call(camera);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frustum cobblemonGetFrustum() {
|
||||
return this.capturedFrustum != null ? this.capturedFrustum : this.cullingFrustum;
|
||||
}
|
||||
}
|
||||
@@ -8,22 +8,32 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.camera.CameraRotation;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
|
||||
import com.cobblemon.mod.common.client.camera.ClientCameraController;
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraFeedHandler;
|
||||
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
|
||||
import com.cobblemon.mod.common.duck.CameraDuck;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
@@ -32,6 +42,7 @@ public abstract class CameraMixin implements CameraDuck {
|
||||
@Shadow private float eyeHeightOld;
|
||||
@Shadow private BlockGetter level;
|
||||
@Shadow private Entity entity;
|
||||
@Final @Shadow private Quaternionf rotation;
|
||||
@Final @Shadow private Vector3f forwards;
|
||||
@Final @Shadow private Vector3f up;
|
||||
@Final @Shadow private Vector3f left;
|
||||
@@ -43,6 +54,11 @@ public abstract class CameraMixin implements CameraDuck {
|
||||
@Override public BlockGetter cobblemon$getLevel() {
|
||||
return level;
|
||||
}
|
||||
@Shadow protected abstract void setRotation(float yRot, float xRot);
|
||||
|
||||
@Unique private float returnTimer = 0;
|
||||
@Unique private float rollAngleStart = 0;
|
||||
@Unique Minecraft minecraft = Minecraft.getInstance();
|
||||
|
||||
@Override public Entity cobblemon$getEntity() {
|
||||
return entity;
|
||||
@@ -66,6 +82,28 @@ public abstract class CameraMixin implements CameraDuck {
|
||||
Entity vehicle = entity.getVehicle();
|
||||
MountedPokemonAnimationRenderController.INSTANCE.reset();
|
||||
|
||||
// Check for cinematic camera override
|
||||
if (ClientCameraController.isActive()) {
|
||||
// When capturing a feed, use that specific camera's transform
|
||||
// instead of the primary camera to avoid both feeds sharing the same position.
|
||||
java.util.UUID capturedId = CameraFeedHandler.getCurrentlyCapturedCamera();
|
||||
ClientCameraController.ActiveTransform cameraTransform = capturedId != null
|
||||
? ClientCameraController.getActiveCameraTransform(capturedId)
|
||||
: ClientCameraController.getCameraTransform();
|
||||
if (cameraTransform != null) {
|
||||
Vec3 position = cameraTransform.getPosition();
|
||||
CameraRotation rotation = cameraTransform.getRotation();
|
||||
setPosition(new Vec3(position.x, position.y, position.z));
|
||||
setRotation(rotation.getYaw(), rotation.getPitch());
|
||||
|
||||
|
||||
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
|
||||
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
|
||||
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(vehicle instanceof Rideable){
|
||||
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(
|
||||
instance,
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.AL11;
|
||||
import org.lwjgl.openal.EXTEfx;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Channel.class)
|
||||
public abstract class ChannelMixin implements ChannelDuck {
|
||||
|
||||
@Shadow
|
||||
private int source;
|
||||
|
||||
// Ids of effects applied to the channel
|
||||
private int cobblemon$lowPassFilterId = 0;
|
||||
private int cobblemon$reverbEffectId = 0;
|
||||
private int cobblemon$reverbSlotId = 0;
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
|
||||
// Delete old filter if it exists
|
||||
if (cobblemon$lowPassFilterId != 0) {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0); // Detach filter from source
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId); // Delete old filter
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
|
||||
// Generate a fresh filter
|
||||
int filterId = EXTEfx.alGenFilters();
|
||||
if (!EXTEfx.alIsFilter(filterId)) {
|
||||
System.err.println("Failed to create OpenAL low-pass filter");
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alFilteri(filterId, EXTEfx.AL_FILTER_TYPE, EXTEfx.AL_FILTER_LOWPASS);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAIN, gain);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAINHF, hfGain);
|
||||
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, filterId);
|
||||
|
||||
// Save the new filter ID
|
||||
cobblemon$lowPassFilterId = filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$inverseAttenuation(float rolloffFactor) {
|
||||
AL10.alSourcei(source, AL10.AL_DISTANCE_MODEL, AL10.AL_INVERSE_DISTANCE);
|
||||
AL10.alSourcef(source, AL10.AL_ROLLOFF_FACTOR, rolloffFactor);
|
||||
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, 1.0f);
|
||||
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$clearFilters() {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0);
|
||||
|
||||
if (EXTEfx.alIsFilter(cobblemon$lowPassFilterId)) {
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId);
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density) {
|
||||
// Clean up existing reverb first if it exists
|
||||
cobblemon$clearReverb();
|
||||
|
||||
// Create the effect object
|
||||
int effectId = EXTEfx.alGenEffects();
|
||||
if (!EXTEfx.alIsEffect(effectId)) {
|
||||
System.err.println("Failed to create OpenAL reverb effect");
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alEffecti(effectId, EXTEfx.AL_EFFECT_TYPE, EXTEfx.AL_EFFECT_REVERB);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DECAY_TIME, decayTime);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_GAIN, gain);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DIFFUSION, diffusion);
|
||||
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DENSITY, density);
|
||||
|
||||
// Create auxiliary effect slot and load effect into it
|
||||
int slotId = EXTEfx.alGenAuxiliaryEffectSlots();
|
||||
if (!EXTEfx.alIsAuxiliaryEffectSlot(slotId)) {
|
||||
System.err.println("Failed to create OpenAL auxiliary effect slot");
|
||||
EXTEfx.alDeleteEffects(effectId);
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alAuxiliaryEffectSloti(slotId, EXTEfx.AL_EFFECTSLOT_EFFECT, effectId);
|
||||
|
||||
// Route source through the slot — send 0, no direct filter
|
||||
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER, slotId, 0, EXTEfx.AL_FILTER_NULL);
|
||||
|
||||
cobblemon$reverbEffectId = effectId;
|
||||
cobblemon$reverbSlotId = slotId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$clearReverb() {
|
||||
if (cobblemon$reverbSlotId != 0) {
|
||||
// Detach source from slot
|
||||
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER,
|
||||
EXTEfx.AL_EFFECTSLOT_NULL, 0, EXTEfx.AL_FILTER_NULL);
|
||||
EXTEfx.alDeleteAuxiliaryEffectSlots(cobblemon$reverbSlotId);
|
||||
cobblemon$reverbSlotId = 0;
|
||||
}
|
||||
if (cobblemon$reverbEffectId != 0) {
|
||||
EXTEfx.alDeleteEffects(cobblemon$reverbEffectId);
|
||||
cobblemon$reverbEffectId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.camera.CameraClientChunkCacheExtension;
|
||||
import com.cobblemon.mod.common.client.camera.CameraChunkVisibilityManager;
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraFeedHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientChunkCache;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(value = ClientChunkCache.class, priority = 1100)
|
||||
public abstract class ClientChunkCacheMixin {
|
||||
@Shadow
|
||||
volatile ClientChunkCache.Storage storage;
|
||||
@Shadow
|
||||
@Final
|
||||
ClientLevel level;
|
||||
/**
|
||||
* Intercept updateViewCenter to use camera position when active.
|
||||
*/
|
||||
@Inject(method = "drop", at = @At("HEAD"))
|
||||
private void cobblemon$onDrop(ChunkPos chunkPos, CallbackInfo ci) {
|
||||
int renderDistance = Minecraft.getInstance().options.renderDistance().get();
|
||||
|
||||
if (CameraChunkVisibilityManager.shouldKeepChunk(chunkPos, renderDistance)
|
||||
|| CameraFeedHandler.shouldAddChunk(chunkPos, renderDistance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CameraClientChunkCacheExtension.drop(level, chunkPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override replaceWithPacketData to accept chunks based on camera position.
|
||||
*/
|
||||
@Inject(method = "replaceWithPacketData", at = @At("HEAD"), cancellable = true)
|
||||
public void cobblemon$onReplaceWithPacketData(int x, int z, FriendlyByteBuf buffer, CompoundTag chunkTag, Consumer<ClientboundLevelChunkPacketData.BlockEntityTagOutput> tagOutputConsumer, CallbackInfoReturnable<LevelChunk> cir) {
|
||||
int renderDistance = Minecraft.getInstance().options.renderDistance().get();
|
||||
ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
boolean isInPlayerRange = storage.inRange(x, z);
|
||||
boolean shouldAddChunk = CameraChunkVisibilityManager.shouldKeepChunk(chunkPos, renderDistance)
|
||||
|| CameraFeedHandler.shouldAddChunk(chunkPos, renderDistance);
|
||||
|
||||
if (shouldAddChunk) {
|
||||
LevelChunk newChunk = CameraClientChunkCacheExtension.replaceWithPacketData(level, x, z, new FriendlyByteBuf(buffer.copy()), chunkTag, tagOutputConsumer);
|
||||
if (!isInPlayerRange)
|
||||
cir.setReturnValue(newChunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getChunk(IILnet/minecraft/world/level/chunk/status/ChunkStatus;Z)Lnet/minecraft/world/level/chunk/LevelChunk;", at = @At("TAIL"), cancellable = true)
|
||||
private void cobblemon$onGetChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk, CallbackInfoReturnable<LevelChunk> cir) {
|
||||
if (!storage.inRange(x, z)) {
|
||||
LevelChunk chunk = CameraClientChunkCacheExtension.getChunk(x, z);
|
||||
|
||||
if (chunk != null)
|
||||
cir.setReturnValue(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-7
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.ClientRecipeBook;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
|
||||
@@ -64,12 +63,23 @@ public abstract class ClientRecipeBookMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setupCollections",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/ImmutableMap;copyOf(Ljava/util/Map;)Lcom/google/common/collect/ImmutableMap;"
|
||||
))
|
||||
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(name = "map2") Map<RecipeBookCategories, List<RecipeCollection>> map2) {
|
||||
@Inject(method = "setupCollections", at = @At("RETURN"))
|
||||
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci) {
|
||||
Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> map = categorizeAndGroupRecipes(recipes);
|
||||
Map<RecipeBookCategories, List<RecipeCollection>> map2 = Maps.newHashMap();
|
||||
ImmutableList.Builder<RecipeCollection> builder = ImmutableList.builder();
|
||||
|
||||
map.forEach((recipeBookCategories, list) -> {
|
||||
List<RecipeCollection> collections = list.stream()
|
||||
.map(listx -> new RecipeCollection(registryAccess, listx))
|
||||
.peek(builder::add)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
map2.put(recipeBookCategories, collections);
|
||||
});
|
||||
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
|
||||
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
|
||||
|
||||
this.collectionsByTab = ImmutableMap.copyOf(map2);
|
||||
this.allCollections = builder.build();
|
||||
}
|
||||
}
|
||||
+102
-15
@@ -9,43 +9,122 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
|
||||
import com.cobblemon.mod.common.api.tween.TweenManager;
|
||||
import com.cobblemon.mod.common.client.camera.GameCamera;
|
||||
import com.cobblemon.mod.common.client.camera.ClientCameraController;
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraFeedHandler;
|
||||
import com.cobblemon.mod.common.client.render.camera.compositor.CompositorManager;
|
||||
import net.minecraft.client.Camera;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
@Unique
|
||||
long lastTime = -1;
|
||||
public abstract class GameRendererMixin {
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(value = "TAIL")
|
||||
)
|
||||
public void render(DeltaTracker counter, boolean tick, CallbackInfo ci) {
|
||||
@Shadow @Final @Mutable
|
||||
private Camera mainCamera;
|
||||
|
||||
@Unique
|
||||
private Camera cobblemon$playerCamera;
|
||||
|
||||
@Unique
|
||||
private final GameCamera cobblemon$gameCamera = new GameCamera();
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$usingCinematic;
|
||||
|
||||
@Unique
|
||||
private long cobblemon$lastTime = -1;
|
||||
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void cobblemon$swapCamera(DeltaTracker counter, boolean tick, CallbackInfo ci) {
|
||||
Minecraft client = Minecraft.getInstance();
|
||||
long newTime = System.currentTimeMillis();
|
||||
// Don't play scheduled animations when the game is paused
|
||||
if (client.isPaused()) {
|
||||
lastTime = newTime;
|
||||
if (client.level == null) {
|
||||
cobblemon$restoreCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastTime != -1) {
|
||||
ClientTaskTracker.INSTANCE.update((newTime - lastTime) / 1000F);
|
||||
if (ClientCameraController.shouldUseCinematicCamera()) {
|
||||
if (!cobblemon$usingCinematic) {
|
||||
cobblemon$playerCamera = mainCamera;
|
||||
mainCamera = cobblemon$gameCamera;
|
||||
cobblemon$usingCinematic = true;
|
||||
}
|
||||
ClientCameraController.prepareCinematicCamera(client, counter.getGameTimeDeltaPartialTick(tick));
|
||||
} else if (cobblemon$usingCinematic) {
|
||||
cobblemon$restoreCamera();
|
||||
}
|
||||
lastTime = newTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the vanilla renderLevel() call when a fullscreen compositor is active since
|
||||
* the compositor covers the entire screen with camera feeds.
|
||||
* This avoids a full wasted scene render every frame.
|
||||
*/
|
||||
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(Lnet/minecraft/client/DeltaTracker;)V"))
|
||||
private void cobblemon$skipVanillaRenderLevel(GameRenderer instance, DeltaTracker deltaTracker) {
|
||||
if (!CompositorManager.isFullscreenActive()) {
|
||||
instance.renderLevel(deltaTracker);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;tryTakeScreenshotIfNeeded()V"))
|
||||
private void cobblemon$afterLevelRendering(DeltaTracker deltaTracker, boolean renderLevel, CallbackInfo ci) {
|
||||
// Only capture feeds when a compositor is actively consuming them.
|
||||
// This avoids expensive full-resolution renders (especially with Iris)
|
||||
// when feeds aren't being displayed.
|
||||
boolean compositorNeedsFeeds = CompositorManager.isFullscreenActive()
|
||||
|| !CompositorManager.INSTANCE.getRequiredFeeds().isEmpty();
|
||||
if (compositorNeedsFeeds && CameraFeedHandler.hasFeeds()) {
|
||||
CameraFeedHandler.captureFeeds(deltaTracker);
|
||||
}
|
||||
if (CompositorManager.isFullscreenActive()) {
|
||||
CompositorManager.INSTANCE.compositeAll();
|
||||
CompositorManager.renderFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "pick", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$preventFramePick(float partialTicks, CallbackInfo ci) {
|
||||
if (CameraFeedHandler.isCapturing())
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
private void cobblemon$afterRender(DeltaTracker counter, boolean tick, CallbackInfo ci) {
|
||||
Minecraft client = Minecraft.getInstance();
|
||||
|
||||
long newTime = System.currentTimeMillis();
|
||||
|
||||
if (client.isPaused()) {
|
||||
cobblemon$lastTime = newTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cobblemon$lastTime != -1) {
|
||||
float delta = (newTime - cobblemon$lastTime) / 1000F;
|
||||
ClientTaskTracker.INSTANCE.update(delta);
|
||||
TweenManager.Companion.getClient().update(delta);
|
||||
}
|
||||
cobblemon$lastTime = newTime;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
@@ -60,4 +139,12 @@ public class GameRendererMixin {
|
||||
return (entityX) -> !(entity.getVehicle() == entityX && entityX instanceof PokemonEntity) && original.test(entityX);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$restoreCamera() {
|
||||
if (cobblemon$usingCinematic) {
|
||||
mainCamera = cobblemon$playerCamera;
|
||||
cobblemon$playerCamera = null;
|
||||
cobblemon$usingCinematic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,11 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -98,14 +96,4 @@ public class HumanoidModelMixin {
|
||||
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setupAnim*", at = @At("TAIL"))
|
||||
public void cobblemon$poseRecallArm(LivingEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, CallbackInfo ci) {
|
||||
if (!(entity instanceof AbstractClientPlayer) || entity.getVehicle() instanceof PokemonEntity) return;
|
||||
float raise = RecallArmAnimationTracker.INSTANCE.raiseFactor(entity.getUUID());
|
||||
if (raise <= 0f) return;
|
||||
this.rightArm.xRot = Mth.lerp(raise, this.rightArm.xRot, (float) (-Math.PI / 2.0));
|
||||
this.rightArm.yRot = Mth.lerp(raise, this.rightArm.yRot, 0f);
|
||||
this.rightArm.zRot = Mth.lerp(raise, this.rightArm.zRot, 0f);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ItemInHandLayer.class)
|
||||
public class ItemInHandLayerMixin {
|
||||
@Inject(method = "renderArmWithItem", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$hideRecallHandItem(LivingEntity entity, ItemStack itemStack, ItemDisplayContext itemDisplayContext, HumanoidArm humanoidArm, PoseStack poseStack, MultiBufferSource multiBufferSource, int light, CallbackInfo ci) {
|
||||
if (humanoidArm == HumanoidArm.RIGHT && RecallArmAnimationTracker.INSTANCE.isRecalling(entity.getUUID())) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.FirstPersonRecallBallRenderer;
|
||||
import com.cobblemon.mod.common.client.camera.ClientCameraController;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -29,10 +29,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
public class ItemInHandRendererMixin {
|
||||
@Inject(method = "renderArmWithItem", at = @At(value = "HEAD"), cancellable = true)
|
||||
private void cobblemon$renderArmWithItem(AbstractClientPlayer abstractClientPlayer, float f, float g, InteractionHand interactionHand, float h, ItemStack itemStack, float i, PoseStack poseStack, MultiBufferSource multiBufferSource, int j, CallbackInfo ci) {
|
||||
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
|
||||
// Hide arms when cinematic camera is active
|
||||
if (ClientCameraController.isActive()) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
else if (interactionHand == InteractionHand.MAIN_HAND && FirstPersonRecallBallRenderer.INSTANCE.render(poseStack, multiBufferSource, j, f)) {
|
||||
|
||||
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
|
||||
ci.cancel();
|
||||
}
|
||||
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents;
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import com.cobblemon.mod.common.ModAPI;
|
||||
import com.cobblemon.mod.common.item.PokeBallItem;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.cobblemon.mod.common.item.WearableItem;
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
@@ -57,17 +54,11 @@ public abstract class ItemRendererMixin {
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
|
||||
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED || stack.getItem() == CobblemonItems.TECHNICAL_MACHINE;
|
||||
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
|
||||
ResourceLocation resourceLocation = null;
|
||||
if (shouldBe2d) {
|
||||
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
|
||||
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
|
||||
else if (stack.getItem() == CobblemonItems.TECHNICAL_MACHINE) {
|
||||
TMMoveComponent component = stack.get(CobblemonItemComponents.TM_MOVE);
|
||||
if (component != null) {
|
||||
resourceLocation = new ResourceLocation(Cobblemon.MODID, "tm/" + component.getMove().getElementalType().showdownId() + "_tm");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (renderMode != ItemDisplayContext.HEAD && stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel2d();
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdatePassengerPositionPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.CameraType;
|
||||
@@ -149,22 +148,10 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
cobblemon$lastRideYRot = rideYRot;
|
||||
cobblemon$lastRideEyePos = rideEyePos;
|
||||
|
||||
CobblemonNetwork.INSTANCE.sendToServer(
|
||||
new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos)
|
||||
);
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("TAIL"))
|
||||
private void cobblemon$updatePassengerPosition(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
|
||||
if (pokemonEntity.getSeatForPassenger(this) == null) return;
|
||||
|
||||
var offset = this.position().subtract(pokemonEntity.position());
|
||||
CobblemonNetwork.INSTANCE.sendToServer(
|
||||
new ServerboundUpdatePassengerPositionPacket(this.getId(), offset)
|
||||
);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -230,10 +217,9 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
Seat seat = pokemonEntity.getSeatForPassenger(this);;
|
||||
if (seat != null) {
|
||||
cir.setReturnValue(seat.getHandsBusy());
|
||||
}
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
cir.setReturnValue(seat.getHandsBusy());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
@@ -17,76 +18,29 @@ import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public abstract class ModelLoaderMixin {
|
||||
|
||||
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
|
||||
|
||||
@Shadow protected abstract void registerModel(ModelResourceLocation modelId, UnbakedModel unbakedModel);
|
||||
|
||||
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
|
||||
|
||||
@Unique
|
||||
List<String> dynamicFolders = Arrays.asList("egg", "tm");
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V",
|
||||
ordinal = 1
|
||||
)
|
||||
)
|
||||
public void loadCustomItemModels(BlockColors blockColors,
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
/*
|
||||
* The models/item/*.json's only get loaded if there's an item in the registry that actually asks for it. It
|
||||
* naively looks for things in the root with an item ID and calls upon it, and some of those JSONs reference
|
||||
* other model JSONs and so it cascades a little bit. Used for block models but more importantly here, sprites.
|
||||
*
|
||||
* The thing about the eggs (and TMs) is, we really want the eggs to be addon-compatible in a way that doesn't restrict
|
||||
* the species that can have eggs, so we can't pre-plan all of them. This mixin looks for the things inside the
|
||||
* egg subfolder (which won't get loaded automagically) and manually inserts them into the unbaked model list so
|
||||
* Minecraft loads them for us. We'll then dynamically call these models from the ItemRendererMixin; so it's
|
||||
* hacky at the init stage and hacky at the render stage. Pretty cool in the gameplay stage though.
|
||||
*
|
||||
* - Hiro
|
||||
*/
|
||||
|
||||
dynamicFolders.forEach(folder -> {
|
||||
Map<ResourceLocation, Resource> map = Minecraft.getInstance().getResourceManager().listResources("models/item/" + folder, (t) -> true);
|
||||
map.forEach((key, value) -> {
|
||||
ResourceLocation resourceLocation = new ResourceLocation(key.getNamespace(), key.getPath().replace("models/item/", "").replace(".json", ""));
|
||||
this.loadItemModelAndDependencies(resourceLocation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
public void init(BlockColors blockColors,
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci) {
|
||||
ProfilerFiller profiler,
|
||||
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
|
||||
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
|
||||
CallbackInfo ci) {
|
||||
CobblemonBakingOverrides.INSTANCE.getModels().forEach(bakingOverride -> {
|
||||
var unbakedModel = this.getModel(bakingOverride.getModelLocation());
|
||||
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.camera.ClientCameraController;
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.PlayerModel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
@@ -22,6 +25,7 @@ import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerRenderer.class)
|
||||
@@ -31,6 +35,27 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
|
||||
super(context, model, shadowRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the camera type check to force player body rendering when cinematic camera is active.
|
||||
* This makes Minecraft think we're not in first person mode (for rendering purposes only),
|
||||
* so the local player's body is rendered even though the camera entity is still the player.
|
||||
*/
|
||||
@Redirect(
|
||||
method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z"
|
||||
)
|
||||
)
|
||||
private boolean cobblemon$allowBodyRenderInCinematic(CameraType instance) {
|
||||
// When cinematic camera is active and configured to show player body,
|
||||
// pretend we're not in first person so the player body renders
|
||||
if (ClientCameraController.isActive() && ClientCameraController.shouldRenderPlayerBody()) {
|
||||
return false;
|
||||
}
|
||||
return instance.isFirstPerson();
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer(player);
|
||||
|
||||
+43
-13
@@ -9,25 +9,55 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonRecipeCategories;
|
||||
import com.llamalad7.mixinextras.expression.Definition;
|
||||
import com.llamalad7.mixinextras.expression.Expression;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.ClientRecipeBook;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookPage;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RecipeBookComponent.class)
|
||||
public class RecipeBookComponentMixin {
|
||||
@Definition(id = "recipeBookCategories", local = @Local(name = "recipeBookCategories"))
|
||||
@Definition(id = "FURNACE_SEARCH", field = "Lnet/minecraft/client/RecipeBookCategories;FURNACE_SEARCH:Lnet/minecraft/client/RecipeBookCategories;")
|
||||
@Expression("recipeBookCategories != FURNACE_SEARCH")
|
||||
@ModifyExpressionValue(
|
||||
method = "updateTabs",
|
||||
at = @At(value = "MIXINEXTRAS:EXPRESSION")
|
||||
)
|
||||
private boolean notCookingPotSearch(boolean original, @Local(name = "recipeBookCategories") RecipeBookCategories recipeBookCategories) {
|
||||
return original && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory();
|
||||
|
||||
@Shadow private ClientRecipeBook book;
|
||||
|
||||
@Shadow protected Minecraft minecraft;
|
||||
|
||||
@Shadow private int width;
|
||||
|
||||
@Shadow private int xOffset;
|
||||
|
||||
@Shadow private int height;
|
||||
|
||||
@Shadow @Final private RecipeBookPage recipeBookPage;
|
||||
|
||||
@Inject(method = "updateTabs", at = @At("HEAD"), cancellable = true)
|
||||
private void customUpdateTabs(CallbackInfo ci) {
|
||||
RecipeBookComponent instance = (RecipeBookComponent) (Object) this;
|
||||
int i = (width - 147) / 2 - xOffset - 30;
|
||||
int j = (height - 166) / 2 + 3;
|
||||
int k = 27;
|
||||
int l = 0;
|
||||
|
||||
for (RecipeBookTabButton recipeBookTabButton : instance.tabButtons) {
|
||||
RecipeBookCategories recipeBookCategories = recipeBookTabButton.getCategory();
|
||||
if (recipeBookCategories != RecipeBookCategories.CRAFTING_SEARCH && recipeBookCategories != RecipeBookCategories.FURNACE_SEARCH && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory()) {
|
||||
if (recipeBookTabButton.updateVisibility(book)) {
|
||||
recipeBookTabButton.setPosition(i, j + 27 * l++);
|
||||
recipeBookTabButton.startAnimation(minecraft);
|
||||
}
|
||||
} else {
|
||||
recipeBookTabButton.visible = true;
|
||||
recipeBookTabButton.setPosition(i, j + k * l++);
|
||||
}
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.camera.CameraViewAreaExtension;
|
||||
import net.minecraft.client.renderer.ViewArea;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ViewArea.class)
|
||||
public class ViewAreaMixin {
|
||||
@Inject(method = "setDirty", at = @At("HEAD"))
|
||||
private void cobblemon$markCinematicDirty(int cx, int cy, int cz, boolean reRenderOnMainThread, CallbackInfo ci) {
|
||||
CameraViewAreaExtension.setDirty(cx, cy, cz, reRenderOnMainThread);
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(AbstractContainerScreen.class)
|
||||
public interface AbstractContainerScreenInvoker {
|
||||
@Invoker("findSlot")
|
||||
@Nullable Slot callFindSlot(double mouseX, double mouseY);
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// There appears to be a data driven way to do this now, however I am not sure how it works since there is no wiki or anything so yolo
|
||||
//This is a yucky hack but better than what we were doing before
|
||||
@Mixin(BlockVariantSets.class)
|
||||
public abstract class AdornRegisterInvoker {
|
||||
@Final
|
||||
|
||||
@@ -8,12 +8,8 @@ object CobblemonBuildDetails {
|
||||
const val VERSION = "{{ version }}"
|
||||
const val SNAPSHOT = {{ isSnapshot }}
|
||||
|
||||
// Read at runtime rather than templated in, so a new commit doesn't invalidate compiled code.
|
||||
private val git = java.util.Properties().apply {
|
||||
CobblemonBuildDetails::class.java.getResourceAsStream("/cobblemon-git.properties")?.use(::load)
|
||||
}
|
||||
@JvmField val GIT_COMMIT: String = git.getProperty("commit", "Not Specified")
|
||||
@JvmField val BRANCH: String = git.getProperty("branch", "Not Specified")
|
||||
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
|
||||
const val BRANCH = "{{ branch | default("Not Specified") }}"
|
||||
|
||||
fun smallCommitHash(): String {
|
||||
return this.GIT_COMMIT.substring(0, 7)
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.cobblemon.mod.common.api.drop.EvolutionItemDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.molang.MoLangLoadedFilesCache
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
@@ -34,7 +33,6 @@ import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.WeightedChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
|
||||
@@ -64,8 +62,6 @@ import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.TMMoveMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.TMMoveNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -76,8 +72,11 @@ import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.api.camera.track.CameraTrackDeserializers
|
||||
import com.cobblemon.mod.common.block.dispenser.DispenserBehaviorRegistry
|
||||
import com.cobblemon.mod.common.client.CobblemonPack
|
||||
import com.cobblemon.mod.common.api.tween.EasingRegistry
|
||||
import com.cobblemon.mod.common.api.tween.TweenManager
|
||||
import com.cobblemon.mod.common.command.argument.*
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
@@ -94,11 +93,9 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.aspects.ALPHA_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.CHARACTERISTIC_RAINBOW_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.MARK_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeature
|
||||
@@ -108,7 +105,6 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HeldItemVisibleProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
@@ -116,7 +112,13 @@ import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.requestWallpapers
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -245,8 +247,6 @@ object Cobblemon {
|
||||
|
||||
DATA_SYNCHRONIZED.subscribe {
|
||||
storage.onPlayerDataSync(it)
|
||||
it.tmList().scheduleFullSyncFromStores(it.party(), it.pc())
|
||||
it.pokedex().scheduleFullSyncFromStores(it.party(), it.pc())
|
||||
playerDataManager.syncAllToPlayer(it)
|
||||
starterHandler.handleJoin(it)
|
||||
it.requestWallpapers()
|
||||
@@ -259,9 +259,7 @@ object Cobblemon {
|
||||
playerDataManager.onPlayerDisconnect(it.player)
|
||||
RequestManager.onLogoff(it.player)
|
||||
serverPlayerStructs.remove(it.player.uuid)
|
||||
}
|
||||
PlatformEvents.PLAYER_RESPAWN.subscribe {
|
||||
serverPlayerStructs.remove(it.player.uuid)
|
||||
com.cobblemon.mod.common.api.molangui.MolangUiSessionManager.closeAllForPlayer(it.player.uuid)
|
||||
}
|
||||
PlatformEvents.PLAYER_DEATH.subscribe {
|
||||
PCLinkManager.removeLink(it.player.uuid)
|
||||
@@ -304,6 +302,7 @@ object Cobblemon {
|
||||
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
|
||||
}
|
||||
|
||||
|
||||
fun sendServerSettingsPacketToPlayer(player: ServerPlayer) {
|
||||
ServerSettingsPacket(
|
||||
this.config.preventCompletePartyDeposit,
|
||||
@@ -312,9 +311,6 @@ object Cobblemon {
|
||||
this.config.maxPokemonLevel,
|
||||
this.config.maxPokemonFriendship,
|
||||
this.config.maxDynamaxLevel,
|
||||
this.config.unlockAllMoveDexMovesByDefault,
|
||||
this.config.pokemonIntrinsicSizeMin,
|
||||
this.config.pokemonIntrinsicSizeMax
|
||||
).sendToPlayer(player)
|
||||
}
|
||||
|
||||
@@ -328,11 +324,8 @@ object Cobblemon {
|
||||
GENDER_ASPECT.register()
|
||||
COSMETIC_SLOT_ASPECT.register()
|
||||
CHARACTERISTIC_RAINBOW_ASPECT.register()
|
||||
ALPHA_ASPECT.register()
|
||||
MARK_ASPECT.register()
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["weighted_choice"] = WeightedChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
|
||||
|
||||
@@ -354,7 +347,10 @@ object Cobblemon {
|
||||
CustomPokemonProperty.register(UnaspectPropertyType)
|
||||
CustomPokemonProperty.register(FreezeFrameProperty)
|
||||
CustomPokemonProperty.register(NoAIProperty)
|
||||
CustomPokemonProperty.register(HeldItemVisibleProperty)
|
||||
|
||||
// Tween & camera system initialization
|
||||
EasingRegistry.registerBuiltinEasings()
|
||||
CameraTrackDeserializers.registerBuiltinTracks()
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
@@ -368,6 +364,7 @@ object Cobblemon {
|
||||
PlatformEvents.SERVER_TICK_POST.subscribe {
|
||||
ServerTaskTracker.update(1/20F)
|
||||
ServerRealTimeTaskTracker.update()
|
||||
TweenManager.server.update(1/20F)
|
||||
}
|
||||
PlatformEvents.SERVER_TICK_PRE.subscribe {
|
||||
ServerRealTimeTaskTracker.update()
|
||||
@@ -376,7 +373,6 @@ object Cobblemon {
|
||||
val server = event.server
|
||||
MoLangLoadedFilesCache.initialize(server)
|
||||
playerDataManager = PlayerInstancedDataStoreManager().also { it.setup(server) }
|
||||
HabitatPools.onServerLoading(server)
|
||||
|
||||
val mongoClient: MongoClient?
|
||||
|
||||
@@ -389,12 +385,8 @@ object Cobblemon {
|
||||
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
|
||||
pokedexNbtFactory.setup(server)
|
||||
|
||||
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
|
||||
tmMoveFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
|
||||
if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
@@ -421,14 +413,8 @@ object Cobblemon {
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
|
||||
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
|
||||
)
|
||||
tmMovesMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
@@ -612,7 +598,6 @@ object Cobblemon {
|
||||
this.implementation.registerCommandArgument(cobblemonResource("mark"), MarkArgumentType::class, SingletonArgumentInfo.contextFree(MarkArgumentType::mark))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("transform_type"), TransformTypeArgumentType::class, SingletonArgumentInfo.contextFree(TransformTypeArgumentType::transformType))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("model_part"), ModelPartArgumentType::class, SingletonArgumentInfo.contextFree(ModelPartArgumentType::modelPart))
|
||||
this.implementation.registerCommandArgument(cobblemonResource("tm"), TmArgumentType::class, SingletonArgumentInfo.contextFree(TmArgumentType::tm))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,15 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.*
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import com.cobblemon.mod.common.block.entity.*
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.resources.ResourceKey
|
||||
|
||||
object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, ResourceKey<Registry<BlockEntityType<*>>>, BlockEntityType<*>>() {
|
||||
|
||||
@@ -32,9 +39,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
|
||||
|
||||
@JvmField
|
||||
val DISC_SHELF: BlockEntityType<DiscShelfBlockEntity> = this.create("disc_shelf", BlockEntityType.Builder.of(::DiscShelfBlockEntity, CobblemonBlocks.DISC_SHELF).build(null))
|
||||
|
||||
@JvmField
|
||||
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
|
||||
|
||||
@@ -44,13 +48,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
|
||||
@JvmField
|
||||
val TM_MACHINE: BlockEntityType<TMMachineBlockEntity> = this.create("tm_machine", BlockEntityType.Builder.of(::TMMachineBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK: BlockEntityType<HabitatBlockEntity> = this.create("habitat_block",
|
||||
BlockEntityType.Builder.of(::HabitatBlockEntity, CobblemonBlocks.HABITAT_BLOCK).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
@@ -111,19 +108,8 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TERRAIN_PLANT: BlockEntityType<TerrainPlantBlockEntity> = this.create(
|
||||
"terrain_plant",
|
||||
BlockEntityType.Builder.of(::TerrainPlantBlockEntity,
|
||||
CobblemonBlocks.VOLTACONIA,
|
||||
CobblemonBlocks.LUCIBELL,
|
||||
CobblemonBlocks.VERDROOT
|
||||
).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val MISTLEAF_FLOWER: BlockEntityType<MistleafFlowerBlockEntity> = this.create(
|
||||
"mistleaf",
|
||||
BlockEntityType.Builder.of(::MistleafFlowerBlockEntity,
|
||||
CobblemonBlocks.MISTLEAF
|
||||
).build(null)
|
||||
val STADIUM_BLOCK: BlockEntityType<StadiumBlockEntity> = this.create(
|
||||
"stadium_block",
|
||||
BlockEntityType.Builder.of(::StadiumBlockEntity, CobblemonBlocks.STADIUM_BLOCK).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,29 +11,20 @@ package com.cobblemon.mod.common
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.block.*
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.LecternBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotBlock
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import com.cobblemon.mod.common.block.decor.*
|
||||
import com.cobblemon.mod.common.block.general.BaleBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
import com.cobblemon.mod.common.block.grower.SaccharineTreeGrower
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
||||
import com.cobblemon.mod.common.block.general.MintLitterBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.BlocksInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.FireBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
@@ -55,6 +46,7 @@ import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.level.material.PushReaction
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
|
||||
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
|
||||
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
|
||||
@@ -129,19 +121,19 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val APRICORN_PLANKS = this.create("apricorn_planks",
|
||||
Block(
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS)
|
||||
.strength(2.0F, 3.0F).sound(SoundType.WOOD)
|
||||
.strength(2.0f, 3.0f).sound(SoundType.WOOD)
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val APRICORN_LEAVES = leaves("apricorn_leaves")
|
||||
@JvmField
|
||||
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0F, 3.0F).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).noCollission().strength(0.5F).sound(SoundType.WOOD)))
|
||||
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).noCollission().strength(0.5f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
|
||||
|
||||
@@ -158,7 +150,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_STAIRS = this.create("apricorn_stairs", StairsBlockInvoker.`cobblemon$create`(APRICORN_PLANKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(APRICORN_PLANKS)))
|
||||
@JvmField
|
||||
@@ -195,12 +187,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", RotatedPillarBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(
|
||||
MapColor.PLANT
|
||||
).strength(0.2F).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
.isValidSpawn { _: BlockState?, _: BlockGetter?, _: BlockPos?, entity: EntityType<*>? ->
|
||||
entity === EntityType.OCELOT || entity === EntityType.PARROT
|
||||
}.isSuffocating { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
@@ -212,13 +204,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
false
|
||||
}))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
|
||||
@JvmField
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5F).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
@@ -228,7 +220,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_HANGING_SIGN = this.create("saccharine_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = this.create("saccharine_stairs", StairsBlockInvoker.`cobblemon$create`(SACCHARINE_PLANKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SACCHARINE_PLANKS)))
|
||||
@JvmField
|
||||
@@ -257,31 +249,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.TERRACOTTA_ORANGE)
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER, MapColor.TERRACOTTA_ORANGE)
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE, MapColor.TERRACOTTA_ORANGE)
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE, MapColor.TERRACOTTA_ORANGE)
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.COLOR_LIGHT_BLUE)
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER, MapColor.COLOR_LIGHT_BLUE)
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE, MapColor.COLOR_LIGHT_BLUE)
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE, MapColor.COLOR_LIGHT_BLUE)
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.TERRACOTTA_BLACK)
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER, MapColor.TERRACOTTA_BLACK)
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE, MapColor.TERRACOTTA_BLACK)
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE, MapColor.TERRACOTTA_BLACK)
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@@ -290,87 +282,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = this.create("deepslate_crystal_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0F, 10.0F).requiresCorrectToolForDrops().mapColor(MapColor.DEEPSLATE)))
|
||||
|
||||
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("normal_gem_block", MapColor.QUARTZ)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = typeGemBlock("fire_gem_block", MapColor.FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = typeGemBlock("water_gem_block", MapColor.WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = typeGemBlock("grass_gem_block", MapColor.COLOR_LIGHT_GREEN)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("electric_gem_block", MapColor.GOLD)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = typeGemBlock("ice_gem_block", MapColor.ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("fighting_gem_block", MapColor.COLOR_RED)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = typeGemBlock("poison_gem_block", MapColor.COLOR_PURPLE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = typeGemBlock("ground_gem_block", MapColor.PODZOL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = typeGemBlock("flying_gem_block", MapColor.COLOR_LIGHT_BLUE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("psychic_gem_block", MapColor.COLOR_MAGENTA)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = typeGemBlock("bug_gem_block", MapColor.TERRACOTTA_LIGHT_GREEN)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = typeGemBlock("rock_gem_block", MapColor.TERRACOTTA_LIGHT_GRAY)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = typeGemBlock("ghost_gem_block", MapColor.TERRACOTTA_MAGENTA)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("dragon_gem_block", MapColor.COLOR_BLUE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = typeGemBlock("dark_gem_block", MapColor.TERRACOTTA_BLACK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = typeGemBlock("steel_gem_block", MapColor.METAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("fairy_gem_block", MapColor.COLOR_PINK)
|
||||
|
||||
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("normal_gem_cluster", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"), MapColor.QUARTZ)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("fire_gem_cluster", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"), MapColor.FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = typeGemCluster("water_gem_cluster", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"), MapColor.WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("grass_gem_cluster", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"), MapColor.COLOR_LIGHT_GREEN)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("electric_gem_cluster", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"), MapColor.GOLD)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = typeGemCluster("ice_gem_cluster", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"), MapColor.ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("fighting_gem_cluster", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"), MapColor.COLOR_RED)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = typeGemCluster("poison_gem_cluster", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"), MapColor.COLOR_PURPLE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("ground_gem_cluster", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"), MapColor.PODZOL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("flying_gem_cluster", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"), MapColor.COLOR_LIGHT_BLUE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("psychic_gem_cluster", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"), MapColor.COLOR_MAGENTA)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = typeGemCluster("bug_gem_cluster", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"), MapColor.TERRACOTTA_LIGHT_GREEN)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("rock_gem_cluster", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"), MapColor.TERRACOTTA_LIGHT_GRAY)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("ghost_gem_cluster", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"), MapColor.TERRACOTTA_MAGENTA)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("dragon_gem_cluster", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"), MapColor.COLOR_BLUE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = typeGemCluster("dark_gem_cluster", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"), MapColor.TERRACOTTA_BLACK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("steel_gem_cluster", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"), MapColor.METAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("fairy_gem_cluster", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"), MapColor.COLOR_PINK)
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@JvmField
|
||||
@@ -539,18 +450,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = this.create(
|
||||
name = "habitat_block",
|
||||
entry = HabitatBlock(
|
||||
properties = BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.STONE)
|
||||
.strength(1.5F, Blocks.OBSIDIAN.explosionResistance)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{14}, false))
|
||||
|
||||
@@ -560,7 +459,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4F).mapColor(MapColor.GOLD), false))
|
||||
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4f), false))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST = create("gilded_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.RED))
|
||||
@@ -613,141 +512,47 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val PINK_PLAQUE = create("pink_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
|
||||
@JvmField
|
||||
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_SMALL_SOUNDS).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
@JvmField
|
||||
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_SMALL_SOUNDS).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_LARGE_SOUNDS).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_LARGE_SOUNDS).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
|
||||
@JvmField
|
||||
val RING_TARGET = create("ring_target", RingTargetBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).noOcclusion().strength(0.125f)))
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val MAGNET = create("magnet", MagnetBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion().strength(0.075f)))
|
||||
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val GALARICA_WREATH = create("galarica_wreath", GalaricaWreathBlock(BlockBehaviour.Properties.of().sound(SoundType.AZALEA_LEAVES).mapColor(MapColor.NONE).noOcclusion().noCollission()))
|
||||
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
|
||||
@JvmField
|
||||
val FLAME_ORB = create("flame_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{15}))
|
||||
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val TOXIC_ORB = create("toxic_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion()))
|
||||
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val LIFE_ORB = create("life_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{7}))
|
||||
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val LIGHT_BALL = create("light_ball", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{13}))
|
||||
@JvmField
|
||||
val SMOKE_BALL = create("smoke_ball", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion()))
|
||||
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
|
||||
@JvmField
|
||||
val KINGS_ROCK = create("kings_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.CENTERED_6x))
|
||||
|
||||
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val GALARICA_CUFF = create("galarica_cuff", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.AZALEA_LEAVES).mapColor(MapColor.NONE).noCollission().noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_10x4))
|
||||
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val UNREMARKABLE_TEACUP = create("unremarkable_teacup", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEACUP))
|
||||
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val MASTERPIECE_TEACUP = create("masterpiece_teacup", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEACUP))
|
||||
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
@JvmField
|
||||
val CHIPPED_POT = create("chipped_pot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEAPOT))
|
||||
@JvmField
|
||||
val CRACKED_POT = create("cracked_pot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEAPOT))
|
||||
@JvmField
|
||||
val ELECTIRIZER = create("electirizer", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.IZER))
|
||||
@JvmField
|
||||
val MAGMARIZER = create("magmarizer", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.IZER))
|
||||
|
||||
@JvmField
|
||||
val SACHET = create("sachet", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val METAL_POWDER = create("metal_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val QUICK_POWDER = create("quick_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val BRIGHT_POWDER = create("bright_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion().lightLevel{3}, DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val SOFT_SAND = create("soft_sand", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
|
||||
@JvmField
|
||||
val NEVER_MELT_ICE = create("never_melt_ice", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().friction(0.989F), DecorativeItemBlock.DecorativeItemBlockType.NEVERMELT))
|
||||
@JvmField
|
||||
val SCOPE_LENS = create("scope_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.SCOPE))
|
||||
@JvmField
|
||||
val WIDE_LENS = create("wide_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.WIDE))
|
||||
@JvmField
|
||||
val ZOOM_LENS = create("zoom_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.ZOOM))
|
||||
|
||||
@JvmField
|
||||
val ICY_ROCK = create("icy_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val HEAT_ROCK = create("heat_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val SMOOTH_ROCK = create("smooth_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val DAMP_ROCK = create("damp_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x6))
|
||||
@JvmField
|
||||
val DESTINY_KNOT = create("destiny_knot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.KNOT))
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = create("luminous_moss", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.MOSS).mapColor(MapColor.NONE).noOcclusion().lightLevel{5}, DecorativeItemBlock.DecorativeItemBlockType.CENTERED_8x))
|
||||
|
||||
@JvmField
|
||||
val METRONOME = create("metronome", ActivatableDecorationBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion(), ActivatableDecorationBlock.ActivatableDecorationBlockType.METRONOME))
|
||||
@JvmField
|
||||
val EJECT_BUTTON = create("eject_button", EjectButtonBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().strength(0.125f), 40))
|
||||
|
||||
@JvmField
|
||||
val LOADED_DICE = create("loaded_dice", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_DICE_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.DICE))
|
||||
@JvmField
|
||||
val IRON_BALL = create("iron_ball", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.IRON_BALL))
|
||||
@JvmField
|
||||
val METAL_COAT = create("metal_coat", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.METAL_COAT))
|
||||
@JvmField
|
||||
val UPGRADE = create("upgrade", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.UPGRADE))
|
||||
@JvmField
|
||||
val DUBIOUS_DISC = create("dubious_disc", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.UPGRADE))
|
||||
|
||||
@JvmField
|
||||
val THROAT_SPRAY = create("throat_spray", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.THROAT_SPRAY))
|
||||
@JvmField
|
||||
val TERRAIN_EXTENDER = create("terrain_extender", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_METAL_CAN_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.EXTENDER))
|
||||
@JvmField
|
||||
val MOOMOO_MILK = create("moomoo_milk", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_JAR_GLASS_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.GLASS_BOTTLE))
|
||||
@JvmField
|
||||
val MEDICINAL_BREW = create("medicinal_brew", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_JAR_GLASS_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.GLASS_BOTTLE))
|
||||
|
||||
@JvmField
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
|
||||
@JvmField
|
||||
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
@JvmField
|
||||
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
|
||||
@JvmField
|
||||
val DIRE_HIT = create("dire_hit", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@@ -766,68 +571,35 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val X_SPEED = create("x_${Stats.SPEED.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
|
||||
@JvmField
|
||||
val HP_UP = create("hp_up", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PROTEIN = create("protein", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val IRON = create("iron", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val CARBOS = create("carbos", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val CALCIUM = create("calcium", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val ZINC = create("zinc", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PP_UP = create("pp_up", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PP_MAX = create("pp_max", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
|
||||
@JvmField
|
||||
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
|
||||
@JvmField
|
||||
val DAMAGED_MONITOR = create("damaged_monitor", DamagedMonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { when (it.getValue(DamagedMonitorBlock.SCREEN)) { DamagedMonitorBlock.BrokenMonitorScreen.GLITCHING -> 10; else -> 0 } }))
|
||||
@JvmField
|
||||
val FOSSIL_ANALYZER = create("fossil_analyzer", FossilAnalyzerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
|
||||
@JvmField
|
||||
val RESTORATION_TANK = create("restoration_tank", RestorationTankBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.GLASS).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
|
||||
|
||||
@JvmField
|
||||
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
|
||||
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2f).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
|
||||
|
||||
@JvmField
|
||||
val STADIUM_BLOCK = create("stadium_block", StadiumBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).strength(3.5F, 6.0F).noOcclusion().pushReaction(PushReaction.BLOCK)))
|
||||
|
||||
@JvmField
|
||||
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMMachineBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_GREEN)
|
||||
.sound(SoundType.METAL)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.strength(2F)
|
||||
.noOcclusion()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val DISC_SHELF = create("disc_shelf", DiscShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
|
||||
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
|
||||
@JvmField
|
||||
|
||||
val RED_MINT = create("red_mint", MintBlock(MintType.RED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_RED).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MINT_SOUNDS).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MINT_SOUNDS).pushReaction(PushReaction.DESTROY)))
|
||||
@@ -845,68 +617,29 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(MobEffects.LEVITATION, 10F, BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
|
||||
|
||||
|
||||
@JvmField
|
||||
val RED_MINT_LEAF_LITTER = this.mintLitter("red_mint_leaf_litter", MapColor.COLOR_RED)
|
||||
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_RED_MINT = this.flowerPot("potted_red_mint", RED_MINT_LEAF_LITTER)
|
||||
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val BLUE_MINT_LEAF_LITTER = this.mintLitter("blue_mint_leaf_litter", MapColor.COLOR_BLUE)
|
||||
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_BLUE_MINT = this.flowerPot("potted_blue_mint", BLUE_MINT_LEAF_LITTER)
|
||||
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val CYAN_MINT_LEAF_LITTER = this.mintLitter("cyan_mint_leaf_litter", MapColor.COLOR_CYAN)
|
||||
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_CYAN_MINT = this.flowerPot("potted_cyan_mint", CYAN_MINT_LEAF_LITTER)
|
||||
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val PINK_MINT_LEAF_LITTER = this.mintLitter("pink_mint_leaf_litter", MapColor.COLOR_PINK)
|
||||
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_PINK_MINT = this.flowerPot("potted_pink_mint", PINK_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val GREEN_MINT_LEAF_LITTER = this.mintLitter("green_mint_leaf_litter", MapColor.COLOR_GREEN)
|
||||
@JvmField
|
||||
val POTTED_GREEN_MINT = this.flowerPot("potted_green_mint", GREEN_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val WHITE_MINT_LEAF_LITTER = this.mintLitter("white_mint_leaf_litter", MapColor.SNOW)
|
||||
@JvmField
|
||||
val POTTED_WHITE_MINT = this.flowerPot("potted_white_mint", WHITE_MINT_LEAF_LITTER)
|
||||
|
||||
@JvmField
|
||||
val LOBAILA = this.create("lobaila", LobailaFlowerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).ignitedByLava().pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val POMOHANA = this.create("pomohana", PomohanaFlowerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).ignitedByLava().pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val PAUVILLEA = this.create("pauvillea",PauvilleaFlowerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().sound(SoundType.GRASS).instabreak().ignitedByLava().pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val WISTENSU = this.create("wistensu", WistensuFlowerHeadBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().sound(SoundType.GRASS).instabreak().ignitedByLava().pushReaction(PushReaction.DESTROY).lightLevel{11}))
|
||||
@JvmField
|
||||
val WISTENSU_VINE = this.create("wistensu_vine", WistensuFlowerVineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().sound(SoundType.GRASS).instabreak().ignitedByLava().pushReaction(PushReaction.DESTROY)))
|
||||
|
||||
@JvmField
|
||||
val ELECTRIC_SEED = this.terrainSeed("electric_seed", TerrainSeedBlock.TerrainSeedType.ELECTRIC)
|
||||
@JvmField
|
||||
val PSYCHIC_SEED = this.terrainSeed("psychic_seed", TerrainSeedBlock.TerrainSeedType.PSYCHIC)
|
||||
@JvmField
|
||||
val MISTY_SEED = this.terrainSeed("misty_seed", TerrainSeedBlock.TerrainSeedType.MISTY)
|
||||
@JvmField
|
||||
val GRASSY_SEED = this.terrainSeed("grassy_seed", TerrainSeedBlock.TerrainSeedType.GRASSY)
|
||||
@JvmField
|
||||
val VOLTACONIA = this.terrainPlant("voltaconia") { state -> if (state.getValue(TerrainPlantBlock.GLOWING)) 8 else 0 }
|
||||
@JvmField
|
||||
val VERDROOT = this.terrainPlant("verdroot") { state -> if (state.getValue(TerrainPlantBlock.GLOWING)) 5 else 0 }
|
||||
@JvmField
|
||||
val LUCIBELL = this.terrainPlant("lucibell") { state -> if (state.getValue(TerrainPlantBlock.GLOWING)) 8 else 0 }
|
||||
@JvmField
|
||||
val MISTLEAF = this.create(
|
||||
"mistleaf",
|
||||
MistleafFlowerBlock {
|
||||
if ((it.getValue(MistleafFlowerBlock.GLOWING) >= 1)) it.getValue(MistleafFlowerBlock.GLOWING) + 2 else 0
|
||||
}
|
||||
)
|
||||
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(CobblemonSounds.HEARTY_GRAIN_BALE_SOUNDS)))
|
||||
@@ -930,9 +663,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(BlockBehaviour.Properties.of().mapColor(apricorn.mapColor()).randomTicks().strength(Blocks.OAK_LOG.defaultDestroyTime(), Blocks.OAK_LOG.explosionResistance).sound(SoundType.WOOD).noOcclusion(), apricorn))
|
||||
|
||||
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?, mapColor: MapColor) : Block {
|
||||
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
|
||||
return this.create(name, TumblestoneBlock(BlockBehaviour.Properties.of()
|
||||
.mapColor(mapColor)
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
.noOcclusion()
|
||||
.strength(1.5F)
|
||||
@@ -1084,165 +816,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
|
||||
|
||||
@JvmField
|
||||
val POTTED_AGUAV_BERRY = this.flowerPot("potted_aguav_berry", AGUAV_BERRY)
|
||||
@JvmField
|
||||
val POTTED_APICOT_BERRY = this.flowerPot("potted_apicot_berry", APICOT_BERRY)
|
||||
@JvmField
|
||||
val POTTED_ASPEAR_BERRY = this.flowerPot("potted_aspear_berry", ASPEAR_BERRY)
|
||||
@JvmField
|
||||
val POTTED_BABIRI_BERRY = this.flowerPot("potted_babiri_berry", BABIRI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_BELUE_BERRY = this.flowerPot("potted_belue_berry", BELUE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_BLUK_BERRY = this.flowerPot("potted_bluk_berry", BLUK_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CHARTI_BERRY = this.flowerPot("potted_charti_berry", CHARTI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CHERI_BERRY = this.flowerPot("potted_cheri_berry", CHERI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CHESTO_BERRY = this.flowerPot("potted_chesto_berry", CHESTO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CHILAN_BERRY = this.flowerPot("potted_chilan_berry", CHILAN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CHOPLE_BERRY = this.flowerPot("potted_chople_berry", CHOPLE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_COBA_BERRY = this.flowerPot("potted_coba_berry", COBA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_COLBUR_BERRY = this.flowerPot("potted_colbur_berry", COLBUR_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CORNN_BERRY = this.flowerPot("potted_cornn_berry", CORNN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_CUSTAP_BERRY = this.flowerPot("potted_custap_berry", CUSTAP_BERRY)
|
||||
@JvmField
|
||||
val POTTED_DURIN_BERRY = this.flowerPot("potted_durin_berry", DURIN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_EGGANT_BERRY = this.flowerPot("potted_eggant_berry", EGGANT_BERRY)
|
||||
@JvmField
|
||||
val POTTED_ENIGMA_BERRY = this.flowerPot("potted_enigma_berry", ENIGMA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_FIGY_BERRY = this.flowerPot("potted_figy_berry", FIGY_BERRY)
|
||||
@JvmField
|
||||
val POTTED_GANLON_BERRY = this.flowerPot("potted_ganlon_berry", GANLON_BERRY)
|
||||
@JvmField
|
||||
val POTTED_GREPA_BERRY = this.flowerPot("potted_grepa_berry", GREPA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_HABAN_BERRY = this.flowerPot("potted_haban_berry", HABAN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_HONDEW_BERRY = this.flowerPot("potted_hondew_berry", HONDEW_BERRY)
|
||||
@JvmField
|
||||
val POTTED_HOPO_BERRY = this.flowerPot("potted_hopo_berry", HOPO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_IAPAPA_BERRY = this.flowerPot("potted_iapapa_berry", IAPAPA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_JABOCA_BERRY = this.flowerPot("potted_jaboca_berry", JABOCA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_KASIB_BERRY = this.flowerPot("potted_kasib_berry", KASIB_BERRY)
|
||||
@JvmField
|
||||
val POTTED_KEBIA_BERRY = this.flowerPot("potted_kebia_berry", KEBIA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_KEE_BERRY = this.flowerPot("potted_kee_berry", KEE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_KELPSY_BERRY = this.flowerPot("potted_kelpsy_berry", KELPSY_BERRY)
|
||||
@JvmField
|
||||
val POTTED_LANSAT_BERRY = this.flowerPot("potted_lansat_berry", LANSAT_BERRY)
|
||||
@JvmField
|
||||
val POTTED_LEPPA_BERRY = this.flowerPot("potted_leppa_berry", LEPPA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_LIECHI_BERRY = this.flowerPot("potted_liechi_berry", LIECHI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_LUM_BERRY = this.flowerPot("potted_lum_berry", LUM_BERRY)
|
||||
@JvmField
|
||||
val POTTED_MAGO_BERRY = this.flowerPot("potted_mago_berry", MAGO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_MAGOST_BERRY = this.flowerPot("potted_magost_berry", MAGOST_BERRY)
|
||||
@JvmField
|
||||
val POTTED_MARANGA_BERRY = this.flowerPot("potted_maranga_berry", MARANGA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_MICLE_BERRY = this.flowerPot("potted_micle_berry", MICLE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_NANAB_BERRY = this.flowerPot("potted_nanab_berry", NANAB_BERRY)
|
||||
@JvmField
|
||||
val POTTED_NOMEL_BERRY = this.flowerPot("potted_nomel_berry", NOMEL_BERRY)
|
||||
@JvmField
|
||||
val POTTED_OCCA_BERRY = this.flowerPot("potted_occa_berry", OCCA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_ORAN_BERRY = this.flowerPot("potted_oran_berry", ORAN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PAMTRE_BERRY = this.flowerPot("potted_pamtre_berry", PAMTRE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PASSHO_BERRY = this.flowerPot("potted_passho_berry", PASSHO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PAYAPA_BERRY = this.flowerPot("potted_payapa_berry", PAYAPA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PECHA_BERRY = this.flowerPot("potted_pecha_berry", PECHA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PERSIM_BERRY = this.flowerPot("potted_persim_berry", PERSIM_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PETAYA_BERRY = this.flowerPot("potted_petaya_berry", PETAYA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PINAP_BERRY = this.flowerPot("potted_pinap_berry", PINAP_BERRY)
|
||||
@JvmField
|
||||
val POTTED_POMEG_BERRY = this.flowerPot("potted_pomeg_berry", POMEG_BERRY)
|
||||
@JvmField
|
||||
val POTTED_QUALOT_BERRY = this.flowerPot("potted_qualot_berry", QUALOT_BERRY)
|
||||
@JvmField
|
||||
val POTTED_RABUTA_BERRY = this.flowerPot("potted_rabuta_berry", RABUTA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_RAWST_BERRY = this.flowerPot("potted_rawst_berry", RAWST_BERRY)
|
||||
@JvmField
|
||||
val POTTED_RAZZ_BERRY = this.flowerPot("potted_razz_berry", RAZZ_BERRY)
|
||||
@JvmField
|
||||
val POTTED_RINDO_BERRY = this.flowerPot("potted_rindo_berry", RINDO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_ROSELI_BERRY = this.flowerPot("potted_roseli_berry", ROSELI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_ROWAP_BERRY = this.flowerPot("potted_rowap_berry", ROWAP_BERRY)
|
||||
@JvmField
|
||||
val POTTED_SALAC_BERRY = this.flowerPot("potted_salac_berry", SALAC_BERRY)
|
||||
@JvmField
|
||||
val POTTED_SHUCA_BERRY = this.flowerPot("potted_shuca_berry", SHUCA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_SITRUS_BERRY = this.flowerPot("potted_sitrus_berry", SITRUS_BERRY)
|
||||
@JvmField
|
||||
val POTTED_SPELON_BERRY = this.flowerPot("potted_spelon_berry", SPELON_BERRY)
|
||||
@JvmField
|
||||
val POTTED_STARF_BERRY = this.flowerPot("potted_starf_berry", STARF_BERRY)
|
||||
@JvmField
|
||||
val POTTED_TAMATO_BERRY = this.flowerPot("potted_tamato_berry", TAMATO_BERRY)
|
||||
@JvmField
|
||||
val POTTED_TANGA_BERRY = this.flowerPot("potted_tanga_berry", TANGA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_TOUGA_BERRY = this.flowerPot("potted_touga_berry", TOUGA_BERRY)
|
||||
@JvmField
|
||||
val POTTED_WACAN_BERRY = this.flowerPot("potted_wacan_berry", WACAN_BERRY)
|
||||
@JvmField
|
||||
val POTTED_WATMEL_BERRY = this.flowerPot("potted_watmel_berry", WATMEL_BERRY)
|
||||
@JvmField
|
||||
val POTTED_WEPEAR_BERRY = this.flowerPot("potted_wepear_berry", WEPEAR_BERRY)
|
||||
@JvmField
|
||||
val POTTED_WIKI_BERRY = this.flowerPot("potted_wiki_berry", WIKI_BERRY)
|
||||
@JvmField
|
||||
val POTTED_YACHE_BERRY = this.flowerPot("potted_yache_berry", YACHE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.flowerPot("potted_pep_up_flower", PEP_UP_FLOWER)
|
||||
@JvmField
|
||||
val POTTED_RED_APRICORN_SAPLING = this.flowerPot("potted_red_apricorn_sapling", RED_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_YELLOW_APRICORN_SAPLING = this.flowerPot("potted_yellow_apricorn_sapling", YELLOW_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_GREEN_APRICORN_SAPLING = this.flowerPot("potted_green_apricorn_sapling", GREEN_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_BLUE_APRICORN_SAPLING = this.flowerPot("potted_blue_apricorn_sapling", BLUE_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_PINK_APRICORN_SAPLING = this.flowerPot("potted_pink_apricorn_sapling", PINK_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_BLACK_APRICORN_SAPLING = this.flowerPot("potted_black_apricorn_sapling", BLACK_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_WHITE_APRICORN_SAPLING = this.flowerPot("potted_white_apricorn_sapling", WHITE_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_SACCHARINE_SAPLING = this.flowerPot("potted_saccharine_sapling", SACCHARINE_SAPLING)
|
||||
|
||||
init {
|
||||
/**
|
||||
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
|
||||
@@ -1320,81 +893,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
private fun typeGemBlock(name: String, mapColor: MapColor): Block {
|
||||
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().mapColor(mapColor).strength(3.0F, 6.0F).sound(CobblemonSounds.TYPE_GEM_BLOCK_SOUNDS)))
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemBlocks[identifier] = typeGemBlock
|
||||
|
||||
return typeGemBlock
|
||||
}
|
||||
|
||||
fun typeGemBlocks() = typeGemBlocks.toMap()
|
||||
|
||||
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation, mapColor: MapColor): Block {
|
||||
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().mapColor(mapColor).strength(2.0F, 3.0F).sound(CobblemonSounds.TYPE_GEM_CLUSTER_SOUNDS).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
|
||||
|
||||
TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemClusters[identifier] = gemClusterBlock
|
||||
|
||||
return gemClusterBlock
|
||||
}
|
||||
|
||||
fun typeGemClusters() = typeGemClusters.toMap()
|
||||
|
||||
private fun flowerPot(name: String, flower: Block) = this.create(
|
||||
name,
|
||||
FlowerPotBlock(
|
||||
flower,
|
||||
BlockBehaviour.Properties.of()
|
||||
.instabreak()
|
||||
.noOcclusion()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
)
|
||||
)
|
||||
|
||||
private fun mintLitter(name: String, color: MapColor) = this.create(
|
||||
name,
|
||||
MintLitterBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.sound(CobblemonSounds.MINT_SOUNDS)
|
||||
.mapColor(color)
|
||||
.noOcclusion()
|
||||
.noCollission()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
)
|
||||
)
|
||||
|
||||
private fun terrainSeed(name: String, type: TerrainSeedBlock.TerrainSeedType) = this.create(
|
||||
name,
|
||||
TerrainSeedBlock(
|
||||
type,
|
||||
BlockBehaviour.Properties.of()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
.ignitedByLava()
|
||||
.mapColor(MapColor.PLANT)
|
||||
.offsetType(BlockBehaviour.OffsetType.XZ)
|
||||
.noCollission()
|
||||
.randomTicks()
|
||||
.instabreak()
|
||||
)
|
||||
)
|
||||
|
||||
private fun terrainPlant(name: String, lightLevel: (BlockState) -> Int) = this.create(
|
||||
name,
|
||||
TerrainPlantBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.PLANT)
|
||||
.noCollission()
|
||||
.instabreak()
|
||||
.sound(SoundType.GRASS)
|
||||
.offsetType(BlockBehaviour.OffsetType.XZ)
|
||||
.ignitedByLava()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
.lightLevel(lightLevel)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,11 @@ import net.minecraft.commands.CommandSourceStack
|
||||
import net.minecraft.commands.Commands
|
||||
|
||||
object CobblemonCommands {
|
||||
fun register(dispatcher: CommandDispatcher<CommandSourceStack>, registry: CommandBuildContext, selection: Commands.CommandSelection) {
|
||||
fun register(
|
||||
dispatcher: CommandDispatcher<CommandSourceStack>,
|
||||
registry: CommandBuildContext,
|
||||
selection: Commands.CommandSelection
|
||||
) {
|
||||
SpawnPokemon.register(dispatcher)
|
||||
SpawnNPCCommand.register(dispatcher)
|
||||
GivePokemon.register(dispatcher)
|
||||
@@ -71,11 +75,13 @@ object CobblemonCommands {
|
||||
MarkGiveAllCommand.register(dispatcher)
|
||||
PcSearchCommand.register(dispatcher)
|
||||
PcTakeCommand.register(dispatcher)
|
||||
GiveTmCommand.register(dispatcher)
|
||||
TmCommand.register(dispatcher)
|
||||
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
CalculateSeatPositionCommand.register(dispatcher)
|
||||
CameraDebugCommand.register(dispatcher)
|
||||
StadiumBattleCommand.register(dispatcher)
|
||||
MolangUiCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.mojang.brigadier.arguments.ArgumentType
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -20,7 +19,6 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
@@ -169,16 +167,12 @@ interface CobblemonImplementation {
|
||||
* TODO
|
||||
*
|
||||
* @param identifier
|
||||
* @param reloaderFactory
|
||||
* @param reloader
|
||||
* @param type
|
||||
* @param dependencies
|
||||
*/
|
||||
fun registerResourceReloader(
|
||||
identifier: ResourceLocation,
|
||||
type: PackType,
|
||||
dependencies: Collection<ResourceLocation>,
|
||||
reloaderFactory: (HolderLookup.Provider) -> PreparableReloadListener
|
||||
)
|
||||
fun registerResourceReloader(identifier: ResourceLocation, reloader: PreparableReloadListener, type: PackType, dependencies: Collection<ResourceLocation>)
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
|
||||
@@ -69,12 +69,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
@JvmField
|
||||
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
|
||||
.persistent(TMMoveComponent.CODEC)
|
||||
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
@JvmField
|
||||
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
|
||||
.persistent(IngredientComponent.CODEC)
|
||||
@@ -107,7 +101,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ride_boosts"), RIDE_BOOST)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
|
||||
@@ -21,8 +21,8 @@ import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
@@ -35,9 +35,11 @@ import com.cobblemon.mod.common.item.berry.HealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.interactive.PonigiriItem
|
||||
import com.cobblemon.mod.common.item.food.SinisterTeaItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
|
||||
import com.cobblemon.mod.common.item.stadium.StadiumPlacementWandItem
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
@@ -74,6 +76,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val NPC_EDITOR = create("npc_editor", CobblemonItem(Item.Properties().stacksTo(1)))
|
||||
|
||||
@JvmField
|
||||
val STADIUM_PLACEMENT_WAND = create("stadium_placement_wand", StadiumPlacementWandItem())
|
||||
|
||||
@JvmField
|
||||
val pokeBalls = mutableListOf<PokeBallItem>()
|
||||
@JvmField
|
||||
@@ -405,7 +410,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.3f, false)
|
||||
@JvmField
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false)
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 7, 0.3f, false)
|
||||
@JvmField
|
||||
@@ -452,19 +457,15 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MONITOR = blockItem("monitor", CobblemonBlocks.MONITOR)
|
||||
@JvmField
|
||||
val DAMAGED_MONITOR = blockItem("damaged_monitor", CobblemonBlocks.DAMAGED_MONITOR)
|
||||
@JvmField
|
||||
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE, Rarity.UNCOMMON)
|
||||
@JvmField
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
@JvmField
|
||||
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
|
||||
@JvmField
|
||||
val DISC_SHELF = blockItem("disc_shelf", CobblemonBlocks.DISC_SHELF)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val STADIUM_BLOCK = blockItem("stadium_block", CobblemonBlocks.STADIUM_BLOCK)
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET) // TODO after 1.7
|
||||
|
||||
@@ -472,18 +473,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@JvmField val DRAGON_SCALE = noSettingsItem("dragon_scale")
|
||||
@JvmField val METAL_COAT = itemNameBlockItem("metal_coat", CobblemonBlocks.METAL_COAT)
|
||||
@JvmField val UPGRADE = itemNameBlockItem("upgrade", CobblemonBlocks.UPGRADE)
|
||||
@JvmField val DUBIOUS_DISC = itemNameBlockItem("dubious_disc", CobblemonBlocks.DUBIOUS_DISC)
|
||||
@JvmField val METAL_COAT = noSettingsItem("metal_coat")
|
||||
@JvmField val UPGRADE = noSettingsItem("upgrade")
|
||||
@JvmField val DUBIOUS_DISC = noSettingsItem("dubious_disc")
|
||||
@JvmField val DEEP_SEA_SCALE = noSettingsItem("deep_sea_scale")
|
||||
@JvmField val DEEP_SEA_TOOTH = noSettingsItem("deep_sea_tooth")
|
||||
@JvmField val ELECTIRIZER = itemNameBlockItem("electirizer", CobblemonBlocks.ELECTIRIZER)
|
||||
@JvmField val MAGMARIZER = itemNameBlockItem("magmarizer", CobblemonBlocks.MAGMARIZER)
|
||||
@JvmField val ELECTIRIZER = noSettingsItem("electirizer")
|
||||
@JvmField val MAGMARIZER = noSettingsItem("magmarizer")
|
||||
@JvmField val OVAL_STONE = noSettingsItem("oval_stone")
|
||||
@JvmField val PROTECTOR = noSettingsItem("protector")
|
||||
@JvmField val REAPER_CLOTH = noSettingsItem("reaper_cloth")
|
||||
@JvmField val PRISM_SCALE = noSettingsItem("prism_scale")
|
||||
@JvmField val SACHET = itemNameBlockItem("sachet", CobblemonBlocks.SACHET)
|
||||
@JvmField val SACHET = noSettingsItem("sachet")
|
||||
@JvmField val WHIPPED_DREAM = create("whipped_dream", foodItem(8, 0.3f))
|
||||
@JvmField val STRAWBERRY_SWEET = create("strawberry_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val LOVE_SWEET = create("love_sweet", foodItem(6, 0.125f))
|
||||
@@ -492,20 +493,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val FLOWER_SWEET = create("flower_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val STAR_SWEET = create("star_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val RIBBON_SWEET = create("ribbon_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val CHIPPED_POT = itemNameBlockItem("chipped_pot", CobblemonBlocks.CHIPPED_POT)
|
||||
@JvmField val CRACKED_POT = itemNameBlockItem("cracked_pot", CobblemonBlocks.CRACKED_POT)
|
||||
@JvmField val MASTERPIECE_TEACUP = itemNameBlockItem("masterpiece_teacup", CobblemonBlocks.MASTERPIECE_TEACUP)
|
||||
@JvmField val UNREMARKABLE_TEACUP = itemNameBlockItem("unremarkable_teacup", CobblemonBlocks.UNREMARKABLE_TEACUP)
|
||||
@JvmField val CHIPPED_POT = noSettingsItem("chipped_pot")
|
||||
@JvmField val CRACKED_POT = noSettingsItem("cracked_pot")
|
||||
@JvmField val MASTERPIECE_TEACUP = noSettingsItem("masterpiece_teacup")
|
||||
@JvmField val UNREMARKABLE_TEACUP = noSettingsItem("unremarkable_teacup")
|
||||
@JvmField val SWEET_APPLE = compostableItem("sweet_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val TART_APPLE = compostableItem("tart_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val SYRUPY_APPLE = compostableItem("syrupy_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val GALARICA_CUFF = itemNameBlockItem("galarica_cuff", CobblemonBlocks.GALARICA_CUFF)
|
||||
@JvmField val GALARICA_CUFF = noSettingsItem("galarica_cuff")
|
||||
@JvmField val GALARICA_WREATH = noSettingsItem("galarica_wreath")
|
||||
@JvmField val BLACK_AUGURITE = noSettingsItem("black_augurite")
|
||||
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
|
||||
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
|
||||
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
|
||||
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
|
||||
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
|
||||
@JvmField val METAL_ALLOY = noSettingsItem("metal_alloy")
|
||||
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
|
||||
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
|
||||
@@ -602,9 +605,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
|
||||
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = blockItem("habitat_block", CobblemonBlocks.HABITAT_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUTS = compostableItemNameBlockItem("galarica_nuts", CobblemonBlocks.GALARICA_NUT_BUSH)
|
||||
|
||||
@@ -649,17 +649,17 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val EXPERIENCE_CANDY_XL = candyItem("exp_candy_xl") { _, _ -> CandyItem.DEFAULT_XL_CANDY_YIELD }
|
||||
@JvmField
|
||||
val CALCIUM = create("calcium", VitaminItem(Stats.SPECIAL_ATTACK, CobblemonBlocks.CALCIUM))
|
||||
val CALCIUM = create("calcium", VitaminItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField
|
||||
val CARBOS = create("carbos", VitaminItem(Stats.SPEED, CobblemonBlocks.CARBOS))
|
||||
val CARBOS = create("carbos", VitaminItem(Stats.SPEED))
|
||||
@JvmField
|
||||
val HP_UP = create("hp_up", VitaminItem(Stats.HP, CobblemonBlocks.HP_UP))
|
||||
val HP_UP = create("hp_up", VitaminItem(Stats.HP))
|
||||
@JvmField
|
||||
val IRON = create("iron", VitaminItem(Stats.DEFENCE, CobblemonBlocks.IRON))
|
||||
val IRON = create("iron", VitaminItem(Stats.DEFENCE))
|
||||
@JvmField
|
||||
val PROTEIN = create("protein", VitaminItem(Stats.ATTACK, CobblemonBlocks.PROTEIN))
|
||||
val PROTEIN = create("protein", VitaminItem(Stats.ATTACK))
|
||||
@JvmField
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE, CobblemonBlocks.ZINC))
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val HEALTH_MOCHI = create("health_mochi", MochiItem(Stats.HP))
|
||||
@JvmField
|
||||
@@ -711,7 +711,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val PEP_UP_FLOWER = compostableBlockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
|
||||
@JvmField
|
||||
val MEDICINAL_BREW = itemNameBlockItem("medicinal_brew", CobblemonBlocks.MEDICINAL_BREW)
|
||||
val MEDICINAL_BREW = noSettingsItem("medicinal_brew")
|
||||
@JvmField
|
||||
val REMEDY = compostableItem("remedy", RemedyItem(RemedyItem.NORMAL), 0.65f)
|
||||
@JvmField
|
||||
@@ -719,7 +719,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 1f)
|
||||
@JvmField
|
||||
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16)), CobblemonBlocks.MOOMOO_MILK))
|
||||
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION, CobblemonBlocks.POTION))
|
||||
@@ -741,34 +741,34 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MAX_REVIVE = create("max_revive", ReviveItem(max = true))
|
||||
@JvmField
|
||||
val PP_UP = create("pp_up", PPUpItem(1, CobblemonBlocks.PP_UP))
|
||||
val PP_UP = create("pp_up", PPUpItem(1))
|
||||
@JvmField
|
||||
val PP_MAX = create("pp_max", PPUpItem(3, CobblemonBlocks.PP_MAX))
|
||||
val PP_MAX = create("pp_max", PPUpItem(3))
|
||||
|
||||
@JvmField
|
||||
val RED_MINT_SEEDS = mintSeed("red", MintType.RED.getCropBlock())
|
||||
@JvmField
|
||||
val RED_MINT_LEAF = compostableItemNameBlockItem("red_mint_leaf", CobblemonBlocks.RED_MINT_LEAF_LITTER, 0.50f)
|
||||
val RED_MINT_LEAF = mintLeaf("red", MintLeafItem(MintType.RED))
|
||||
@JvmField
|
||||
val BLUE_MINT_SEEDS = mintSeed("blue", MintType.BLUE.getCropBlock())
|
||||
@JvmField
|
||||
val BLUE_MINT_LEAF = compostableItemNameBlockItem("blue_mint_leaf", CobblemonBlocks.BLUE_MINT_LEAF_LITTER, 0.50f)
|
||||
val BLUE_MINT_LEAF = mintLeaf("blue", MintLeafItem(MintType.BLUE))
|
||||
@JvmField
|
||||
val CYAN_MINT_SEEDS = mintSeed("cyan", MintType.CYAN.getCropBlock())
|
||||
@JvmField
|
||||
val CYAN_MINT_LEAF = compostableItemNameBlockItem("cyan_mint_leaf", CobblemonBlocks.CYAN_MINT_LEAF_LITTER, 0.50f)
|
||||
val CYAN_MINT_LEAF = mintLeaf("cyan", MintLeafItem(MintType.CYAN))
|
||||
@JvmField
|
||||
val PINK_MINT_SEEDS = mintSeed("pink", MintType.PINK.getCropBlock())
|
||||
@JvmField
|
||||
val PINK_MINT_LEAF = compostableItemNameBlockItem("pink_mint_leaf", CobblemonBlocks.PINK_MINT_LEAF_LITTER, 0.50f)
|
||||
val PINK_MINT_LEAF = mintLeaf("pink", MintLeafItem(MintType.PINK))
|
||||
@JvmField
|
||||
val GREEN_MINT_SEEDS = mintSeed("green", MintType.GREEN.getCropBlock())
|
||||
@JvmField
|
||||
val GREEN_MINT_LEAF = compostableItemNameBlockItem("green_mint_leaf", CobblemonBlocks.GREEN_MINT_LEAF_LITTER, 0.50f)
|
||||
val GREEN_MINT_LEAF = mintLeaf("green", MintLeafItem(MintType.GREEN))
|
||||
@JvmField
|
||||
val WHITE_MINT_SEEDS = mintSeed("white", MintType.WHITE.getCropBlock())
|
||||
@JvmField
|
||||
val WHITE_MINT_LEAF = compostableItemNameBlockItem("white_mint_leaf", CobblemonBlocks.WHITE_MINT_LEAF_LITTER, 0.50f)
|
||||
val WHITE_MINT_LEAF = mintLeaf("white", MintLeafItem(MintType.WHITE))
|
||||
|
||||
val mints = mutableMapOf<String, MintItem>()
|
||||
|
||||
@@ -920,18 +920,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val CHOICE_SPECS = wearableItem("choice_specs")
|
||||
@JvmField val EXP_SHARE = wearableItem("exp_share")
|
||||
@JvmField val FOCUS_BAND = wearableItem("focus_band")
|
||||
@JvmField val KINGS_ROCK = wearableBlockItem("kings_rock", CobblemonBlocks.KINGS_ROCK)
|
||||
@JvmField val NEVER_MELT_ICE = wearableBlockItem("never_melt_ice", CobblemonBlocks.NEVER_MELT_ICE)
|
||||
@JvmField val KINGS_ROCK = wearableItem("kings_rock")
|
||||
@JvmField val MUSCLE_BAND = wearableItem("muscle_band")
|
||||
@JvmField val ROCKY_HELMET = wearableItem("rocky_helmet")
|
||||
@JvmField val SAFETY_GOGGLES = wearableItem("safety_goggles")
|
||||
@JvmField val WISE_GLASSES = wearableItem("wise_glasses")
|
||||
@JvmField val SHARP_BEAK = wearableItem("sharp_beak")
|
||||
@JvmField val COVERT_CLOAK = wearableItem("covert_cloak")
|
||||
@JvmField val SHELL_HELMET = wearableItem("shell_helmet")
|
||||
@JvmField val GALARICA_WREATH = wearableBlockItem("galarica_wreath", CobblemonBlocks.GALARICA_WREATH)
|
||||
@JvmField val CLEANSE_TAG = wearableBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
|
||||
@JvmField val SPELL_TAG = wearableBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
|
||||
|
||||
// Held Items
|
||||
@JvmField
|
||||
@@ -959,17 +952,21 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val CHOICE_SCARF = heldItem("choice_scarf")
|
||||
@JvmField
|
||||
val CLEANSE_TAG = itemNameBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
|
||||
@JvmField
|
||||
val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = itemNameBlockItem("destiny_knot", CobblemonBlocks.DESTINY_KNOT)
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
@JvmField
|
||||
val DRAGON_FANG = heldItem("dragon_fang")
|
||||
@JvmField
|
||||
val EJECT_BUTTON = itemNameBlockItem("eject_button", CobblemonBlocks.EJECT_BUTTON)
|
||||
val EJECT_BUTTON = heldItem("eject_button")
|
||||
@JvmField
|
||||
val EJECT_PACK = heldItem("eject_pack")
|
||||
@JvmField
|
||||
val ELECTRIC_SEED = itemNameBlockItem("electric_seed", CobblemonBlocks.ELECTRIC_SEED)
|
||||
val ELECTRIC_SEED = heldItem("electric_seed")
|
||||
@JvmField
|
||||
val EVERSTONE = heldItem("everstone")
|
||||
@JvmField
|
||||
@@ -979,13 +976,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val FAIRY_FEATHER = heldItem("fairy_feather")
|
||||
@JvmField
|
||||
val FLAME_ORB = itemNameBlockItem("flame_orb", CobblemonBlocks.FLAME_ORB)
|
||||
val FLAME_ORB = heldItem("flame_orb")
|
||||
@JvmField
|
||||
val FLOAT_STONE = heldItem("float_stone")
|
||||
@JvmField
|
||||
val FOCUS_SASH = heldItem("focus_sash")
|
||||
@JvmField
|
||||
val GRASSY_SEED = itemNameBlockItem("grassy_seed", CobblemonBlocks.GRASSY_SEED)
|
||||
val GRASSY_SEED = heldItem("grassy_seed")
|
||||
@JvmField
|
||||
val GRIP_CLAW = heldItem("grip_claw")
|
||||
@JvmField
|
||||
@@ -993,32 +990,34 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
|
||||
@JvmField
|
||||
val IRON_BALL = itemNameBlockItem("iron_ball", CobblemonBlocks.IRON_BALL)
|
||||
val IRON_BALL = heldItem("iron_ball")
|
||||
@JvmField
|
||||
val LAGGING_TAIL = heldItem("lagging_tail")
|
||||
@JvmField
|
||||
val LEFTOVERS = compostableHeldItem("leftovers", null, 0.50f)
|
||||
@JvmField
|
||||
val LIFE_ORB = itemNameBlockItem("life_orb", CobblemonBlocks.LIFE_ORB)
|
||||
val LIFE_ORB = heldItem("life_orb")
|
||||
@JvmField
|
||||
val LIGHT_BALL = itemNameBlockItem("light_ball", CobblemonBlocks.LIGHT_BALL)
|
||||
val LIGHT_BALL = heldItem("light_ball")
|
||||
@JvmField
|
||||
val LIGHT_CLAY = heldItem("light_clay")
|
||||
@JvmField
|
||||
val LOADED_DICE = itemNameBlockItem("loaded_dice", CobblemonBlocks.LOADED_DICE)
|
||||
val LOADED_DICE = heldItem("loaded_dice")
|
||||
@JvmField
|
||||
val LUCKY_EGG = heldItem("lucky_egg")
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = itemNameBlockItem("luminous_moss", CobblemonBlocks.LUMINOUS_MOSS)
|
||||
val LUMINOUS_MOSS = heldItem("luminous_moss")
|
||||
@JvmField
|
||||
val MAGNET = itemNameBlockItem("magnet", CobblemonBlocks.MAGNET)
|
||||
val MAGNET = heldItem("magnet")
|
||||
@JvmField
|
||||
val METRONOME = itemNameBlockItem("metronome", CobblemonBlocks.METRONOME)
|
||||
val METRONOME = heldItem("metronome")
|
||||
@JvmField
|
||||
val MIRACLE_SEED = heldItem("miracle_seed")
|
||||
@JvmField
|
||||
val MYSTIC_WATER = heldItem("mystic_water")
|
||||
@JvmField
|
||||
val NEVER_MELT_ICE = heldItem("never_melt_ice")
|
||||
@JvmField
|
||||
val POISON_BARB = heldItem("poison_barb")
|
||||
@JvmField
|
||||
val POWER_ANKLET = heldItem("power_anklet")
|
||||
@@ -1033,7 +1032,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val POWER_WEIGHT = heldItem("power_weight")
|
||||
@JvmField
|
||||
val PSYCHIC_SEED = itemNameBlockItem("psychic_seed", CobblemonBlocks.PSYCHIC_SEED)
|
||||
val PSYCHIC_SEED = heldItem("psychic_seed")
|
||||
@JvmField
|
||||
val PROTECTIVE_PADS = heldItem("protective_pads")
|
||||
@JvmField
|
||||
@@ -1043,11 +1042,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val RED_CARD = heldItem("red_card")
|
||||
@JvmField
|
||||
val RING_TARGET = itemNameBlockItem("ring_target", CobblemonBlocks.RING_TARGET)
|
||||
val RING_TARGET = heldItem("ring_target")
|
||||
@JvmField
|
||||
val ROOM_SERVICE = heldItem("room_service")
|
||||
@JvmField
|
||||
val SCOPE_LENS = itemNameBlockItem("scope_lens", CobblemonBlocks.SCOPE_LENS)
|
||||
val SCOPE_LENS = heldItem("scope_lens")
|
||||
@JvmField
|
||||
val SHARP_BEAK = heldItem("sharp_beak")
|
||||
@JvmField
|
||||
val SHED_SHELL = heldItem("shed_shell")
|
||||
@JvmField
|
||||
@@ -1057,19 +1058,21 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SILVER_POWDER = heldItem("silver_powder")
|
||||
@JvmField
|
||||
val SOFT_SAND = itemNameBlockItem("soft_sand", CobblemonBlocks.SOFT_SAND)
|
||||
val SOFT_SAND = heldItem("soft_sand")
|
||||
@JvmField
|
||||
val SMOKE_BALL = itemNameBlockItem("smoke_ball", CobblemonBlocks.SMOKE_BALL)
|
||||
val SPELL_TAG = itemNameBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
|
||||
@JvmField
|
||||
val SMOKE_BALL = heldItem("smoke_ball")
|
||||
@JvmField
|
||||
val SOOTHE_BELL = heldItem("soothe_bell")
|
||||
@JvmField
|
||||
val STICKY_BARB = heldItem("sticky_barb")
|
||||
@JvmField
|
||||
val TERRAIN_EXTENDER = itemNameBlockItem("terrain_extender", CobblemonBlocks.TERRAIN_EXTENDER)
|
||||
val TERRAIN_EXTENDER = heldItem("terrain_extender")
|
||||
@JvmField
|
||||
val THROAT_SPRAY = itemNameBlockItem("throat_spray", CobblemonBlocks.THROAT_SPRAY)
|
||||
val THROAT_SPRAY = heldItem("throat_spray")
|
||||
@JvmField
|
||||
val TOXIC_ORB = itemNameBlockItem("toxic_orb", CobblemonBlocks.TOXIC_ORB)
|
||||
val TOXIC_ORB = heldItem("toxic_orb")
|
||||
@JvmField
|
||||
val TWISTED_SPOON = heldItem("twisted_spoon")
|
||||
@JvmField
|
||||
@@ -1077,33 +1080,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = itemNameBlockItem("weakness_policy", CobblemonBlocks.WEAKNESS_POLICY)
|
||||
@JvmField
|
||||
val WIDE_LENS = itemNameBlockItem("wide_lens", CobblemonBlocks.WIDE_LENS)
|
||||
val WIDE_LENS = heldItem("wide_lens")
|
||||
@JvmField
|
||||
val ZOOM_LENS = itemNameBlockItem("zoom_lens", CobblemonBlocks.ZOOM_LENS)
|
||||
val ZOOM_LENS = heldItem("zoom_lens")
|
||||
@JvmField
|
||||
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 0.85F)
|
||||
@JvmField
|
||||
val MIRROR_HERB = compostableHeldItem("mirror_herb", null, 0.85F)
|
||||
@JvmField
|
||||
val MISTY_SEED = itemNameBlockItem("misty_seed", CobblemonBlocks.MISTY_SEED)
|
||||
val MISTY_SEED = heldItem("misty_seed")
|
||||
@JvmField
|
||||
val POWER_HERB = compostableHeldItem("power_herb", null, 0.85F)
|
||||
@JvmField
|
||||
val WHITE_HERB = compostableHeldItem("white_herb", null, 0.85F)
|
||||
@JvmField
|
||||
val BRIGHT_POWDER = glowItem("bright_powder", CobblemonBlocks.BRIGHT_POWDER)
|
||||
val BRIGHT_POWDER = heldItem("bright_powder")
|
||||
@JvmField
|
||||
val METAL_POWDER = itemNameBlockItem("metal_powder", CobblemonBlocks.METAL_POWDER)
|
||||
val METAL_POWDER = heldItem("metal_powder")
|
||||
@JvmField
|
||||
val QUICK_POWDER = itemNameBlockItem("quick_powder", CobblemonBlocks.QUICK_POWDER)
|
||||
val QUICK_POWDER = heldItem("quick_powder")
|
||||
@JvmField
|
||||
val DAMP_ROCK = itemNameBlockItem("damp_rock", CobblemonBlocks.DAMP_ROCK)
|
||||
val DAMP_ROCK = heldItem("damp_rock")
|
||||
@JvmField
|
||||
val HEAT_ROCK = itemNameBlockItem("heat_rock", CobblemonBlocks.HEAT_ROCK)
|
||||
val HEAT_ROCK = heldItem("heat_rock")
|
||||
@JvmField
|
||||
val SMOOTH_ROCK = itemNameBlockItem("smooth_rock", CobblemonBlocks.SMOOTH_ROCK)
|
||||
val SMOOTH_ROCK = heldItem("smooth_rock")
|
||||
@JvmField
|
||||
val ICY_ROCK = itemNameBlockItem("icy_rock", CobblemonBlocks.ICY_ROCK)
|
||||
val ICY_ROCK = heldItem("icy_rock")
|
||||
|
||||
// Mulch
|
||||
@JvmField
|
||||
@@ -1211,83 +1214,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = blockItem("deepslate_crystal_core", CobblemonBlocks.TYPE_GEM_CORE)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = blockItem("normal_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = blockItem("fire_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = blockItem("water_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("electric_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = blockItem("grass_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = blockItem("ice_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("fighting_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = blockItem("poison_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = blockItem("ground_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = blockItem("flying_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("psychic_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = blockItem("bug_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = blockItem("rock_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = blockItem("ghost_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = blockItem("dragon_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = blockItem("dark_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = blockItem("steel_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = blockItem("fairy_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = blockItem("normal_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = blockItem("fire_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = blockItem("water_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("electric_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = blockItem("grass_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = blockItem("ice_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = blockItem("fighting_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = blockItem("poison_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = blockItem("ground_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = blockItem("flying_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("psychic_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = blockItem("bug_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = blockItem("rock_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = blockItem("ghost_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = blockItem("dragon_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = blockItem("dark_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = blockItem("steel_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = blockItem("fairy_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
|
||||
Component.translatable("item.minecraft.fishing_rod").blue(),
|
||||
@@ -1512,71 +1438,47 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
@JvmField
|
||||
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Properties()))
|
||||
@JvmField
|
||||
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
val NORMAL_GEM = this.create("normal_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL))
|
||||
val NORMAL_GEM = noSettingsItem("normal_gem")
|
||||
@JvmField
|
||||
val FIRE_GEM = this.create("fire_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE))
|
||||
val FIRE_GEM = noSettingsItem("fire_gem")
|
||||
@JvmField
|
||||
val WATER_GEM = this.create("water_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_WATER))
|
||||
val WATER_GEM = noSettingsItem("water_gem")
|
||||
@JvmField
|
||||
val GRASS_GEM = this.create("grass_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS))
|
||||
val GRASS_GEM = noSettingsItem("grass_gem")
|
||||
@JvmField
|
||||
val ELECTRIC_GEM = this.create("electric_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC))
|
||||
val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
||||
@JvmField
|
||||
val ICE_GEM = this.create("ice_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ICE))
|
||||
val ICE_GEM = noSettingsItem("ice_gem")
|
||||
@JvmField
|
||||
val FIGHTING_GEM = this.create("fighting_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING))
|
||||
val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
||||
@JvmField
|
||||
val POISON_GEM = this.create("poison_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_POISON))
|
||||
val POISON_GEM = noSettingsItem("poison_gem")
|
||||
@JvmField
|
||||
val GROUND_GEM = this.create("ground_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND))
|
||||
val GROUND_GEM = noSettingsItem("ground_gem")
|
||||
@JvmField
|
||||
val FLYING_GEM = this.create("flying_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING))
|
||||
val FLYING_GEM = noSettingsItem("flying_gem")
|
||||
@JvmField
|
||||
val PSYCHIC_GEM = this.create("psychic_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC))
|
||||
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
||||
@JvmField
|
||||
val BUG_GEM = this.create("bug_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_BUG))
|
||||
val BUG_GEM = noSettingsItem("bug_gem")
|
||||
@JvmField
|
||||
val ROCK_GEM = this.create("rock_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK))
|
||||
val ROCK_GEM = noSettingsItem("rock_gem")
|
||||
@JvmField
|
||||
val GHOST_GEM = this.create("ghost_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST))
|
||||
val GHOST_GEM = noSettingsItem("ghost_gem")
|
||||
@JvmField
|
||||
val DRAGON_GEM = this.create("dragon_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON))
|
||||
val DRAGON_GEM = noSettingsItem("dragon_gem")
|
||||
@JvmField
|
||||
val DARK_GEM = this.create("dark_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DARK))
|
||||
val DARK_GEM = noSettingsItem("dark_gem")
|
||||
@JvmField
|
||||
val STEEL_GEM = this.create("steel_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL))
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
@JvmField
|
||||
val FAIRY_GEM = this.create("fairy_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY))
|
||||
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
|
||||
// Plants, Flora, and Flowers
|
||||
@JvmField
|
||||
val LOBAILA = compostableBlockItem("lobaila", CobblemonBlocks.LOBAILA, 0.65f)
|
||||
@JvmField
|
||||
val POMOHANA = compostableBlockItem("pomohana", CobblemonBlocks.POMOHANA, 0.3f)
|
||||
@JvmField
|
||||
val PAUVILLEA = compostableBlockItem("pauvillea", CobblemonBlocks.PAUVILLEA, 0.65f)
|
||||
@JvmField
|
||||
val WISTENSU = compostableBlockItem("wistensu", CobblemonBlocks.WISTENSU, 0.5f)
|
||||
|
||||
@JvmField
|
||||
val VOLTACONIA = compostableBlockItem("voltaconia", CobblemonBlocks.VOLTACONIA, 0.85f)
|
||||
@JvmField
|
||||
val VERDROOT = compostableBlockItem("verdroot", CobblemonBlocks.VERDROOT, 0.85f)
|
||||
@JvmField
|
||||
val MISTLEAF = compostableBlockItem("mistleaf", CobblemonBlocks.MISTLEAF, 0.85f)
|
||||
@JvmField
|
||||
val LUCIBELL = compostableBlockItem("lucibell", CobblemonBlocks.LUCIBELL, 0.85f)
|
||||
|
||||
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
|
||||
|
||||
private fun itemNameBlockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): ItemNameBlockItem = this.create(name, ItemNameBlockItem(block, Item.Properties().rarity(rarity)))
|
||||
@@ -1613,16 +1515,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
private fun wearableItem(name: String, heldItemRemappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
WearableHatItem(name).also {
|
||||
wearables.add(it)
|
||||
if (heldItemRemappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, heldItemRemappedName)
|
||||
}
|
||||
}
|
||||
)
|
||||
private fun wearableBlockItem(name: String, block: Block, heldItemRemappedName: String? = null): WearableBlockItem = create(
|
||||
name,
|
||||
WearableBlockItem(name, block).also {
|
||||
WearableItem(name).also {
|
||||
wearables.add(it)
|
||||
if (heldItemRemappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, heldItemRemappedName)
|
||||
@@ -1779,6 +1672,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
|
||||
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
|
||||
}
|
||||
|
||||
private fun glowItem(name: String, block: Block): GlowItem = this.create(name, GlowItem(block, Item.Properties()))
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.api.molangui.container.MolangContainerMenu
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
@@ -20,8 +20,8 @@ import net.minecraft.world.inventory.MenuType
|
||||
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
|
||||
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
val MOLANG_UI = MenuType.register("molang_ui", ::MolangContainerMenu)
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveSelector
|
||||
import com.cobblemon.mod.common.api.moves.MovesetBuilder
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.MovesetBuilderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.StringToObjectAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonMovesetBuilders : JsonDataRegistry<MovesetBuilder> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(MovesetBuilder::class.java, MovesetBuilderAdapter)
|
||||
.registerTypeAdapter(MoveSelector::class.java, StringToObjectAdapter(MoveSelector.selectors))
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(MovesetBuilder::class.java)
|
||||
override val resourcePath = "moveset_builders"
|
||||
override val id: ResourceLocation = cobblemonResource("moveset_builders")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonMovesetBuilders>()
|
||||
|
||||
val movesetBuilders = mutableMapOf<ResourceLocation, MovesetBuilder>()
|
||||
|
||||
fun getOrThrow(id: ResourceLocation): MovesetBuilder {
|
||||
return movesetBuilders[id]
|
||||
?: throw IllegalArgumentException("Unknown MovesetBuilder id: $id")
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) { /* These don't sync. But what if they did? Nah probably shouldn't sync. */ }
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, MovesetBuilder>) {
|
||||
movesetBuilders.clear()
|
||||
data.forEach { (id, value) -> value.id = id }
|
||||
movesetBuilders.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
|
||||
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
@@ -17,6 +18,14 @@ import com.cobblemon.mod.common.client.net.battle.*
|
||||
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraDebugHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraEndHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraFocusHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraStartHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraStateHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CameraTransformHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.CompositorHandler
|
||||
import com.cobblemon.mod.common.client.net.camera.SplitScreenHandler
|
||||
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
|
||||
import com.cobblemon.mod.common.client.net.data.CobblemonMechanicsSyncHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
@@ -32,11 +41,10 @@ import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.ExpGainedDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.molangui.MolangUiCloseHandler
|
||||
import com.cobblemon.mod.common.client.net.molangui.MolangUiOpenHandler
|
||||
import com.cobblemon.mod.common.client.net.molangui.MolangUiUpdateHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SynchronizeActiveTMHandler
|
||||
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
|
||||
@@ -46,13 +54,13 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHandler
|
||||
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundSeatAssignmentPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
|
||||
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.stadium.OpenStadiumConfigHandler
|
||||
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
|
||||
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.RemoveClientPokemonHandler
|
||||
@@ -72,11 +80,20 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraDebugPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraFocusPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraStartPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraTransformPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CompositorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.SplitScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
@@ -84,6 +101,9 @@ import com.cobblemon.mod.common.net.messages.client.cooking.SeasoningRegistrySyn
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.RequestOpenRidingStatsDebugGUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.molangui.MolangUiClosePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.molangui.MolangUiOpenPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.molangui.MolangUiUpdatePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
@@ -91,7 +111,6 @@ import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacke
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.habitat.OpenHabitatBlockEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
|
||||
@@ -105,9 +124,9 @@ import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegis
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClientboundSeatAssignmentPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.stadium.OpenStadiumConfigPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
|
||||
@@ -132,7 +151,6 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangeP
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
@@ -145,17 +163,8 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
|
||||
@@ -165,7 +174,6 @@ import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -178,8 +186,13 @@ import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidin
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.habitat.SaveHabitatBlockSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.molangui.MolangUiActionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.molangui.MolangUiClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.molangui.MolangUiVersionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.stadium.RequestStadiumPlacementWandPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.stadium.StadiumPlacementSetPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.stadium.UpdateStadiumBlockPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.SetPastureConflictPacket
|
||||
@@ -196,7 +209,6 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePa
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdatePassengerPositionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
|
||||
@@ -223,7 +235,6 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -236,10 +247,16 @@ import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRiding
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.molangui.MolangUiActionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.molangui.MolangUiClosedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.molangui.MolangUiVersionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.habitat.SaveHabitatBlockSettingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.stadium.RequestStadiumPlacementWandHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.stadium.StadiumPlacementSetHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.stadium.UpdateStadiumBlockHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
|
||||
@@ -254,7 +271,6 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdatePassengerPositionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdateRiderRotationHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
@@ -263,7 +279,6 @@ import com.cobblemon.mod.common.net.serverhandling.storage.RequestMoveSwapHandle
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
@@ -342,8 +357,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(AlphaUpdatePacket.ID, AlphaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ScaleModifierUpdatePacket.ID, ScaleModifierUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Evolution start
|
||||
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
|
||||
@@ -377,7 +390,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
|
||||
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
|
||||
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
|
||||
list.add(PacketRegisterInfo(SynchronizeActiveTMPacket.ID, SynchronizeActiveTMPacket::decode, SynchronizeActiveTMHandler))
|
||||
|
||||
// Starter packets
|
||||
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
|
||||
@@ -403,9 +415,85 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BattlePersistentStatusPacket.ID, BattlePersistentStatusPacket::decode, BattlePersistentStatusHandler))
|
||||
list.add(PacketRegisterInfo(BattleMadeInvalidChoicePacket.ID, BattleMadeInvalidChoicePacket::decode, BattleMadeInvalidChoiceHandler))
|
||||
list.add(PacketRegisterInfo(BattleMusicPacket.ID, BattleMusicPacket::decode, BattleMusicHandler))
|
||||
list.add(PacketRegisterInfo(BattleChallengeExpiredPacket.ID, BattleChallengeExpiredPacket::decode, BattleChallengeExpiredHandler))
|
||||
list.add(PacketRegisterInfo(BattleReplacePokemonPacket.ID, BattleReplacePokemonPacket::decode, BattleReplacePokemonHandler))
|
||||
list.add(PacketRegisterInfo(BattleTransformPokemonPacket.ID, BattleTransformPokemonPacket::decode, BattleTransformPokemonHandler))
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
BattleChallengeExpiredPacket.ID,
|
||||
BattleChallengeExpiredPacket::decode,
|
||||
BattleChallengeExpiredHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
BattleReplacePokemonPacket.ID,
|
||||
BattleReplacePokemonPacket::decode,
|
||||
BattleReplacePokemonHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
BattleTransformPokemonPacket.ID,
|
||||
BattleTransformPokemonPacket::decode,
|
||||
BattleTransformPokemonHandler
|
||||
)
|
||||
)
|
||||
|
||||
// Camera packets
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraStartPacket.ID,
|
||||
CameraStartPacket::decode,
|
||||
CameraStartHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraEndPacket.ID,
|
||||
CameraEndPacket::decode,
|
||||
CameraEndHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraDebugPacket.ID,
|
||||
CameraDebugPacket::decode,
|
||||
CameraDebugHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraTransformPacket.ID,
|
||||
CameraTransformPacket::decode,
|
||||
CameraTransformHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraFocusPacket.ID,
|
||||
CameraFocusPacket::decode,
|
||||
CameraFocusHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CameraStatePacket.ID,
|
||||
CameraStatePacket::decode,
|
||||
CameraStateHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
SplitScreenPacket.ID,
|
||||
SplitScreenPacket::decode,
|
||||
SplitScreenHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
CompositorPacket.ID,
|
||||
CompositorPacket::decode,
|
||||
CompositorHandler
|
||||
)
|
||||
)
|
||||
|
||||
// MultiBattleTeam Packets
|
||||
list.add(PacketRegisterInfo(TeamRequestNotificationPacket.ID, TeamRequestNotificationPacket::decode, TeamRequestNotificationHandler))
|
||||
@@ -425,7 +513,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(DexEvolutionDisplaySyncPacket.ID, DexEvolutionDisplaySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
@@ -444,7 +531,11 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Molang UI packets
|
||||
list.add(PacketRegisterInfo(MolangUiOpenPacket.ID, MolangUiOpenPacket::decode, MolangUiOpenHandler))
|
||||
list.add(PacketRegisterInfo(MolangUiClosePacket.ID, MolangUiClosePacket::decode, MolangUiCloseHandler))
|
||||
list.add(PacketRegisterInfo(MolangUiUpdatePacket.ID, MolangUiUpdatePacket::decode, MolangUiUpdateHandler))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
@@ -475,8 +566,23 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(OpenPasturePacket.ID, OpenPasturePacket::decode, OpenPastureHandler))
|
||||
list.add(PacketRegisterInfo(ClosePasturePacket.ID, ClosePasturePacket::decode, ClosePastureHandler))
|
||||
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
|
||||
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
|
||||
list.add(PacketRegisterInfo(UpdatePastureConflictFlagPacket.ID, UpdatePastureConflictFlagPacket::decode, UpdatePastureConflictFlagHandler))
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
PokemonUnpasturedPacket.ID,
|
||||
PokemonUnpasturedPacket::decode,
|
||||
PokemonUnpasturedHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
UpdatePastureConflictFlagPacket.ID,
|
||||
UpdatePastureConflictFlagPacket::decode,
|
||||
UpdatePastureConflictFlagHandler
|
||||
)
|
||||
)
|
||||
|
||||
// Stadium
|
||||
list.add(PacketRegisterInfo(OpenStadiumConfigPacket.ID, OpenStadiumConfigPacket::decode, OpenStadiumConfigHandler))
|
||||
|
||||
// Orientation
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, ClientboundUpdateOrientationHandler))
|
||||
@@ -507,18 +613,16 @@ object CobblemonNetwork {
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
|
||||
|
||||
// Riding
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundSeatAssignmentPacket.ID, ClientboundSeatAssignmentPacket::decode, ClientboundSeatAssignmentPacketHandler))
|
||||
|
||||
// Habitat blocks
|
||||
list.add(PacketRegisterInfo(OpenHabitatBlockEditorPacket.ID, OpenHabitatBlockEditorPacket::decode, OpenHabitatBlockEditorHandler))
|
||||
|
||||
// Debug
|
||||
list.add(PacketRegisterInfo(OpenRidingStatsDebugGUIPacket.ID, OpenRidingStatsDebugGUIPacket::decode, OpenRidingStatsDebugGUIHandler))
|
||||
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -598,7 +702,6 @@ object CobblemonNetwork {
|
||||
|
||||
// Block entity
|
||||
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
|
||||
list.add(PacketRegisterInfo(TMMachineTeachMovePacket.ID, TMMachineTeachMovePacket::decode, TMMachineTeachMoveHandler))
|
||||
|
||||
// Move select packets
|
||||
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
|
||||
@@ -631,26 +734,39 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRiderRotationPacket.ID, ServerboundUpdateRiderRotationPacket::decode, ServerboundUpdateRiderRotationHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdatePassengerPositionPacket.ID, ServerboundUpdatePassengerPositionPacket::decode, ServerboundUpdatePassengerPositionHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
// TM Machine
|
||||
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
|
||||
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
SetEntityBehaviourPacket.ID,
|
||||
SetEntityBehaviourPacket::decode,
|
||||
SetEntityBehaviourHandler
|
||||
)
|
||||
)
|
||||
list.add(
|
||||
PacketRegisterInfo(
|
||||
DamageOnCollisionPacket.ID,
|
||||
DamageOnCollisionPacket::decode,
|
||||
DamageOnCollisionPacketHandler
|
||||
)
|
||||
)
|
||||
|
||||
// Habitat blocks
|
||||
list.add(PacketRegisterInfo(SaveHabitatBlockSettingsPacket.ID, SaveHabitatBlockSettingsPacket::decode, SaveHabitatBlockSettingsHandler))
|
||||
// Molang UI packets
|
||||
list.add(PacketRegisterInfo(MolangUiActionPacket.ID, MolangUiActionPacket::decode, MolangUiActionHandler))
|
||||
list.add(PacketRegisterInfo(MolangUiClosedPacket.ID, MolangUiClosedPacket::decode, MolangUiClosedHandler))
|
||||
list.add(PacketRegisterInfo(MolangUiVersionPacket.ID, MolangUiVersionPacket::decode, MolangUiVersionHandler))
|
||||
|
||||
// Debug / cheats
|
||||
// Stadium
|
||||
list.add(PacketRegisterInfo(UpdateStadiumBlockPacket.ID, UpdateStadiumBlockPacket::decode, UpdateStadiumBlockHandler))
|
||||
list.add(PacketRegisterInfo(RequestStadiumPlacementWandPacket.ID, RequestStadiumPlacementWandPacket::decode, RequestStadiumPlacementWandHandler))
|
||||
list.add(PacketRegisterInfo(StadiumPlacementSetPacket.ID, StadiumPlacementSetPacket::decode, StadiumPlacementSetHandler))
|
||||
|
||||
// Debug
|
||||
list.add(PacketRegisterInfo(RequestOpenRidingStatsDebugGUIPacket.ID, RequestOpenRidingStatsDebugGUIPacket::decode, RequestOpenRidingStatsDebugGUIHandler))
|
||||
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.npc.PartyComposition
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonPartyCompositions : JsonDataRegistry<PartyComposition> {
|
||||
override val id: ResourceLocation = cobblemonResource("party_compositions")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val typeToken: TypeToken<PartyComposition> = TypeToken.get(PartyComposition::class.java)
|
||||
override val resourcePath: String = "party_compositions"
|
||||
override val observable = SimpleObservable<CobblemonPartyCompositions>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
val partyCompositions = mutableMapOf<ResourceLocation, PartyComposition>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// probably worth syncing which exist, yeah. the deeper details probably not. I'll get to this eventually.
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PartyComposition>) {
|
||||
data.entries.forEach { it.value.id = it.key }
|
||||
partyCompositions.clear()
|
||||
partyCompositions.putAll(data)
|
||||
observable.emit(this)
|
||||
Cobblemon.LOGGER.info("Loaded ${data.size} party compositions.")
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.MovesetBuilder
|
||||
import com.cobblemon.mod.common.api.npc.PartyPool
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.ScriptableIntRange
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.MovesetBuilderReferenceAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.PokemonPropertiesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ScriptableIntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object CobblemonPartyPools : JsonDataRegistry<PartyPool> {
|
||||
override val id: ResourceLocation = cobblemonResource("party_pools")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val typeToken: TypeToken<PartyPool> = TypeToken.get(PartyPool::class.java)
|
||||
override val resourcePath: String = "party_pools"
|
||||
override val observable = SimpleObservable<CobblemonPartyPools>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, PokemonPropertiesAdapter(saveLong = false))
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(ScriptableIntRange::class.java, ScriptableIntRangeAdapter)
|
||||
.registerTypeAdapter(MovesetBuilder::class.java, MovesetBuilderReferenceAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
val partyPools = mutableMapOf<ResourceLocation, PartyPool>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// probably worth syncing. Knowing the details might help exploit the server? lot of effort for little though.
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PartyPool>) {
|
||||
data.entries.forEach { it.value.id = it.key }
|
||||
partyPools.clear()
|
||||
partyPools.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,6 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
@JvmField
|
||||
val HABITAT_BLOCK_KEY: ResourceKey<PoiType> = createKey("habitat_block", CobblemonBlocks.HABITAT_BLOCK, 0, 1)
|
||||
@JvmField
|
||||
val HABITAT_BLOCK = create(HABITAT_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.HABITAT_BLOCK), 0, 1))
|
||||
|
||||
private fun createKey(string: String, block: Block, maxTickets: Int, validRange: Int ): ResourceKey<PoiType> =
|
||||
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string)).also { resourceKey ->
|
||||
|
||||
@@ -37,9 +37,6 @@ object CobblemonSensors {
|
||||
@JvmField
|
||||
val POKEMON_DEFEND_OWNER = register("pokemon_owner_under_attack", ::DefendOwnerSensor)
|
||||
|
||||
@JvmField
|
||||
val ALPHA_LEVEL_MATCHING = register("alpha_level_matching", ::AlphaLevelMatchingSensor)
|
||||
|
||||
@JvmField
|
||||
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
|
||||
|
||||
|
||||
@@ -108,9 +108,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val LEVELUP = this.create("gui.levelup")
|
||||
|
||||
@JvmField
|
||||
val MOVE_LEARN = this.create("gui.move_learn")
|
||||
|
||||
@JvmField
|
||||
val PVN_BATTLE = this.create("battle.pvn.default")
|
||||
@JvmField
|
||||
@@ -139,18 +136,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val MULCH_REMOVE = this.create("block.mulch.remove")
|
||||
|
||||
@JvmField
|
||||
val MONITOR_BREAK = this.create("block.monitor.break")
|
||||
@JvmField
|
||||
val MONITOR_INSERT = this.create("block.monitor.insert")
|
||||
@JvmField
|
||||
val MONITOR_LOADING = this.create("block.monitor.loading")
|
||||
@JvmField
|
||||
val MONITOR_GLITCHING = this.create("block.monitor.glitching")
|
||||
|
||||
@JvmField
|
||||
val BRIGHT_POWDER_USE = this.create("block.bright_powder.use")
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_ACTIVATE = this.create("block.fossil_machine.activate")
|
||||
@JvmField
|
||||
@@ -215,28 +200,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val TM_USE = this.create("item.tm.use")
|
||||
@JvmField
|
||||
val TM_MACHINE_BURN_LOOP = this.create("block.tm_machine.burn_loop")
|
||||
@JvmField
|
||||
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
|
||||
@JvmField
|
||||
val TM_MACHINE_START = this.create("block.tm_machine.start")
|
||||
@JvmField
|
||||
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
|
||||
@JvmField
|
||||
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
|
||||
@JvmField
|
||||
val TM_MACHINE_TM_INSERT = this.create("block.tm_machine.tm_insert")
|
||||
@JvmField
|
||||
val TM_MACHINE_TM_RETRIEVE = this.create("block.tm_machine.tm_retrieve")
|
||||
@JvmField
|
||||
val TM_MACHINE_ITEM_INSERT = this.create("block.tm_machine.item_insert")
|
||||
@JvmField
|
||||
val TM_MACHINE_ITEM_RETRIEVE = this.create("block.tm_machine.item_retrieve")
|
||||
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
|
||||
@JvmField
|
||||
@@ -275,9 +238,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
|
||||
|
||||
@JvmField
|
||||
val APRICORN_HARVEST = this.create("block.apricorn.harvest")
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_BREAK = this.create("block.big_root.break")
|
||||
@JvmField
|
||||
@@ -352,13 +312,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val RIDE_LOOP_LEATHER_MONO = create("ride.loop.leather.mono")
|
||||
val RIDE_LOOP_LEATHER = create("ride.loop.leather")
|
||||
@JvmField
|
||||
val RIDE_LOOP_PLUMAGE_MONO = create("ride.loop.plumage.mono")
|
||||
@JvmField
|
||||
val RIDE_LOOP_LEATHER_STEREO = create("ride.loop.leather.stereo")
|
||||
@JvmField
|
||||
val RIDE_LOOP_PLUMAGE_STEREO = create("ride.loop.plumage.stereo")
|
||||
val RIDE_LOOP_PLUMAGE = create("ride.loop.plumage")
|
||||
|
||||
@JvmField
|
||||
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
|
||||
@@ -424,35 +380,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_block.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_cluster.break")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_HIT = this.create("block.type_gem_block.hit")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PLACE = this.create("block.type_gem_block.place")
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEP = this.create("block.type_gem_block.step")
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.1f,
|
||||
TYPE_GEM_CLUSTER_BREAK,
|
||||
TYPE_GEM_BLOCK_STEP,
|
||||
TYPE_GEM_BLOCK_PLACE,
|
||||
TYPE_GEM_BLOCK_HIT,
|
||||
TYPE_GEM_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
TYPE_GEM_BLOCK_BREAK,
|
||||
TYPE_GEM_BLOCK_STEP,
|
||||
TYPE_GEM_BLOCK_PLACE,
|
||||
TYPE_GEM_BLOCK_HIT,
|
||||
TYPE_GEM_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
EVOLUTION_STONE_BLOCK_BREAK,
|
||||
@@ -634,121 +561,5 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
CAMPFIRE_POT_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_PLACE = this.create("block.item_block.paper_small.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_BREAK = this.create("block.item_block.paper_small.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_SMALL_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_PAPER_SMALL_BREAK,
|
||||
SoundEvents.WOOL_STEP,
|
||||
ITEM_BLOCK_PAPER_SMALL_PLACE,
|
||||
SoundEvents.WOOL_HIT,
|
||||
SoundEvents.WOOL_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_PLACE = this.create("block.item_block.paper_large.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_BREAK = this.create("block.item_block.paper_large.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_PAPER_LARGE_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_PAPER_LARGE_BREAK,
|
||||
SoundEvents.WOOL_STEP,
|
||||
ITEM_BLOCK_PAPER_LARGE_PLACE,
|
||||
SoundEvents.WOOL_HIT,
|
||||
SoundEvents.WOOL_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_PLACE = this.create("block.item_block.powder.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_BREAK = this.create("block.item_block.powder.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_STEP = this.create("block.item_block.powder.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_POWDER_BREAK,
|
||||
ITEM_BLOCK_POWDER_STEP,
|
||||
ITEM_BLOCK_POWDER_PLACE,
|
||||
ITEM_BLOCK_POWDER_STEP,
|
||||
ITEM_BLOCK_POWDER_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_PLACE = this.create("block.item_block.bottle_liquid.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_BREAK = this.create("block.item_block.bottle_liquid.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_STEP = this.create("block.item_block.bottle_liquid.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_BREAK,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_STEP,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_PLACE = this.create("block.item_block.bottle_pills.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_BREAK = this.create("block.item_block.bottle_pills.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_STEP = this.create("block.item_block.bottle_pills.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_BREAK,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_STEP,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_PLACE = this.create("block.item_block.dice.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_BREAK = this.create("block.item_block.dice.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_STEP = this.create("block.item_block.dice.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_DICE_BREAK,
|
||||
ITEM_BLOCK_DICE_STEP,
|
||||
ITEM_BLOCK_DICE_PLACE,
|
||||
ITEM_BLOCK_DICE_STEP,
|
||||
ITEM_BLOCK_DICE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_METAL_CAN_PLACE = this.create("block.item_block.metal_can.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_METAL_CAN_BREAK = this.create("block.item_block.metal_can.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_METAL_CAN_STEP = this.create("block.item_block.metal_can.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_METAL_CAN_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_METAL_CAN_BREAK,
|
||||
ITEM_BLOCK_METAL_CAN_STEP,
|
||||
ITEM_BLOCK_METAL_CAN_PLACE,
|
||||
ITEM_BLOCK_METAL_CAN_STEP,
|
||||
ITEM_BLOCK_METAL_CAN_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_JAR_GLASS_LIQUID_PLACE = this.create("block.item_block.jar_glass_liquid.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_JAR_GLASS_LIQUID_BREAK = this.create("block.item_block.jar_glass_liquid.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_JAR_GLASS_LIQUID_STEP = this.create("block.item_block.jar_glass_liquid.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_JAR_GLASS_LIQUID_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_JAR_GLASS_LIQUID_BREAK,
|
||||
ITEM_BLOCK_JAR_GLASS_LIQUID_STEP,
|
||||
ITEM_BLOCK_JAR_GLASS_LIQUID_PLACE,
|
||||
ITEM_BLOCK_JAR_GLASS_LIQUID_STEP,
|
||||
ITEM_BLOCK_JAR_GLASS_LIQUID_STEP
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
@JvmField
|
||||
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val CATCH_ALPHA_POKEMON = this.create("catch_alpha_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val EGG_COLLECT = this.create("eggs_collected", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
@@ -81,20 +78,8 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
@JvmField
|
||||
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val PLANT_TYPE_GEM = this.create("plant_type_gem", SimpleCriterionTrigger(PlantTypeGemCriterion.CODEC))
|
||||
|
||||
val RIDING_STAT_BOOST = this.create("riding_stat_boost", SimpleCriterionTrigger(RidingStatBoostCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_TM = this.create("has_learn_specific_tm", SimpleCriterionTrigger(LearnTMCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_ALL_TM = this.create("has_learn_all_tm", SimpleCriterionTrigger(LearnAllTMCriterion.CODEC))
|
||||
|
||||
@JvmField
|
||||
val LEARN_TM_COUNT = this.create("tms_learned", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
override val registry = BuiltInRegistries.TRIGGER_TYPES
|
||||
override val resourceKey = Registries.TRIGGER_TYPE
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Optional
|
||||
|
||||
class LearnTMContext(val tm: ResourceLocation)
|
||||
|
||||
class LearnTMCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val tm: String
|
||||
) : SimpleCriterionCondition<LearnTMContext>(playerCtx) {
|
||||
companion object {
|
||||
val CODEC: Codec<LearnTMCriterion> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(LearnTMCriterion::playerCtx),
|
||||
Codec.STRING.optionalFieldOf("tm", "any").forGetter(LearnTMCriterion::tm)
|
||||
).apply(it, ::LearnTMCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: LearnTMContext): Boolean {
|
||||
return tm == "any" || context.tm == tm.asIdentifierDefaultingNamespace()
|
||||
}
|
||||
}
|
||||
|
||||
class LearnAllTMContext
|
||||
|
||||
class LearnAllTMCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>
|
||||
) : SimpleCriterionCondition<LearnAllTMContext>(playerCtx) {
|
||||
companion object {
|
||||
val CODEC: Codec<LearnAllTMCriterion> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(LearnAllTMCriterion::playerCtx)
|
||||
).apply(it, ::LearnAllTMCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: LearnAllTMContext) = true
|
||||
}
|
||||
+39
-3
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
import java.util.Optional
|
||||
|
||||
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
|
||||
|
||||
@@ -29,6 +29,42 @@ class PlantTumblestoneCriterion(
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
|
||||
return context.block.canGrow(context.pos, player.level())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///**
|
||||
// * A criterion condition for when a tumblestone block is planted
|
||||
// * @param id The identifier of the criterion
|
||||
// * @param predicate The predicate for the criterion
|
||||
// *
|
||||
// * @author Aethen
|
||||
// * @since 02/28/2024
|
||||
// */
|
||||
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
// override fun toJson(json: JsonObject) {
|
||||
// // Add properties to json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// override fun fromJson(json: JsonObject) {
|
||||
// // Parse properties from json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks if the tumblestone block can grow at the given position
|
||||
// * @param player The player that planted the tumblestone block
|
||||
// * @param context The context of the criterion
|
||||
// * @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
// */
|
||||
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * The context of the [PlantTumblestoneCriterionCondition]
|
||||
// * @param pos The position of the tumblestone block
|
||||
// * @param tumbleStoneBlock The tumblestone block
|
||||
// */
|
||||
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
||||
import com.cobblemon.mod.common.block.TypeGemCoreBlock
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
|
||||
class PlantTypeGemContext(val pos: BlockPos, val block: TypeGemClusterBlock)
|
||||
|
||||
class PlantTypeGemCriterion(
|
||||
playerCtx: Optional<ContextAwarePredicate>
|
||||
): SimpleCriterionCondition<PlantTypeGemContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<PlantTypeGemCriterion> = RecordCodecBuilder.create { it.group(
|
||||
ContextAwarePredicate.CODEC.optionalFieldOf("player").forGetter(PlantTypeGemCriterion::playerCtx)
|
||||
).apply(it, ::PlantTypeGemCriterion) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTypeGemContext): Boolean {
|
||||
return player.level().getBlockState(context.pos).block is TypeGemCoreBlock
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -48,7 +48,7 @@ class RidingStatBoostCriterion(
|
||||
val max = context.pokemon.pokemon.getMaxRideBoost(rideStat).toDouble()
|
||||
val min = context.pokemon.pokemon.getBaseRideStat(rideStat).toDouble()
|
||||
val value = context.pokemon.getRideStat(rideStat, ridingStyle, min, max)
|
||||
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canApplyRideBoost(rideStat)
|
||||
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canAddRideBoost(rideStat)
|
||||
if (isSufficient && this.rideStat == "any") {
|
||||
return true
|
||||
} else if (!isSufficient && this.rideStat == "all") {
|
||||
|
||||
+7
-31
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
@@ -18,7 +17,6 @@ import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
@@ -36,8 +34,6 @@ import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
class HateEntityTaskConfig : SingleTaskConfig {
|
||||
val entityCondition: Expression = "true".asExpression()
|
||||
val range: ExpressionOrEntityVariable = Either.left("24".asExpression())
|
||||
val chance: Float = Cobblemon.config.predationChance
|
||||
private val coercedChance = chance.coerceIn(0f, 1f)
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
|
||||
override fun createTask(
|
||||
@@ -47,44 +43,24 @@ class HateEntityTaskConfig : SingleTaskConfig {
|
||||
val range = range.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
|
||||
MemoryModuleType.ANGRY_AT,
|
||||
MemoryModuleType.ATTACK_TARGET
|
||||
MemoryModuleType.ANGRY_AT
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
|
||||
return BehaviorBuilder.create { instance ->
|
||||
instance.group(
|
||||
instance.present(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES),
|
||||
instance.registered(MemoryModuleType.ANGRY_AT),
|
||||
instance.registered(MemoryModuleType.ATTACK_TARGET),
|
||||
).apply(instance) { entities, angryAt, attackTarget ->
|
||||
instance.registered(MemoryModuleType.ANGRY_AT)
|
||||
).apply(instance) { entities, angryAt ->
|
||||
Trigger { world, entity, _ ->
|
||||
// bail if gamerule is disabled
|
||||
if (!world.gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_PREDATION)) return@Trigger false
|
||||
val target = instance.get(entities).findClosest { hated ->
|
||||
mainThreadRuntime.withQueryValue("entity", hated.asMostSpecificMoLangValue())
|
||||
val target = instance.get(entities).findClosest {
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
return@findClosest mainThreadRuntime.resolveBoolean(entityCondition)
|
||||
}.orElse(null)
|
||||
|
||||
var shouldTrigger = false
|
||||
if (target != null && target.distanceTo(entity) <= range) {
|
||||
shouldTrigger = when {
|
||||
// set always attacking if activity chance is equal to 1.0
|
||||
coercedChance == 1f -> {
|
||||
angryAt.set(target.uuid)
|
||||
attackTarget.set(target)
|
||||
true
|
||||
}
|
||||
// roll the dice in other way
|
||||
coercedChance != 1f && world.random.nextFloat() < coercedChance -> {
|
||||
angryAt.set(target.uuid)
|
||||
attackTarget.set(target)
|
||||
true
|
||||
}
|
||||
// fallback to peaceful behavior in unattended cases
|
||||
else -> false
|
||||
}
|
||||
angryAt.set(target.uuid)
|
||||
}
|
||||
return@Trigger shouldTrigger
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model
|
||||
|
||||
/**
|
||||
* How a [PokemonBattle] came to an end.
|
||||
*/
|
||||
enum class BattleEndResult {
|
||||
/** One side defeated the other. */
|
||||
VICTORY,
|
||||
/** A wild Pokémon was captured, ending the battle. */
|
||||
CAPTURE,
|
||||
/** An actor forfeited, giving the victory to the other side. */
|
||||
FORFEIT,
|
||||
/** A player fled from a wild Pokémon. */
|
||||
FLED,
|
||||
/** The battle was stopped without a winner, such as by a command, a player disconnecting or an error. */
|
||||
STOPPED
|
||||
}
|
||||
+16
-20
@@ -21,7 +21,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleEndedEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFleeAttemptEvent
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
@@ -86,7 +85,9 @@ open class PokemonBattle(
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = !CobblemonBuildDetails.SNAPSHOT
|
||||
val struct = this.asMoLangValue()
|
||||
val runtime = MoLangRuntime().also { it.environment.query = struct }
|
||||
val runtime = MoLangRuntime().also {
|
||||
it.environment.query = struct
|
||||
}
|
||||
|
||||
val onEndHandlers: MutableList<(PokemonBattle) -> Unit> = mutableListOf()
|
||||
|
||||
@@ -125,12 +126,6 @@ open class PokemonBattle(
|
||||
var winners = listOf<BattleActor>()
|
||||
var losers = listOf<BattleActor>()
|
||||
var ended = false
|
||||
/** How the battle ended, or null if it hasn't ended yet. */
|
||||
var endResult: BattleEndResult? = null
|
||||
/** The actor that forfeited the battle, if any. */
|
||||
var forfeitedBy: BattleActor? = null
|
||||
/** Whether a player fled from the battle. */
|
||||
var fled = false
|
||||
// TEMP battle showcase stuff
|
||||
var announcingRules = false
|
||||
var turn: Int = 0
|
||||
@@ -140,10 +135,11 @@ open class PokemonBattle(
|
||||
set(value) { field = value.coerceAtMost(Int.MAX_VALUE) } // go outside
|
||||
|
||||
/** The current duration of the battle in seconds. */
|
||||
val time: Int get() = ticks / 20
|
||||
val time: Int get() = ticks % 20
|
||||
|
||||
var dispatchResult = GO
|
||||
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
|
||||
val afterDispatches = mutableListOf<() -> Unit>()
|
||||
|
||||
val captureActions = mutableListOf<BattleCaptureAction>()
|
||||
|
||||
@@ -258,14 +254,6 @@ open class PokemonBattle(
|
||||
|
||||
fun end() {
|
||||
ended = true
|
||||
val result = when {
|
||||
winners.isNotEmpty() && showdownMessages.any { "capture" in it } -> BattleEndResult.CAPTURE
|
||||
winners.isNotEmpty() && forfeitedBy != null -> BattleEndResult.FORFEIT
|
||||
winners.isNotEmpty() -> BattleEndResult.VICTORY
|
||||
fled -> BattleEndResult.FLED
|
||||
else -> BattleEndResult.STOPPED
|
||||
}
|
||||
endResult = result
|
||||
val awardToFainted = Cobblemon.config.awardExperienceToFaintedPokemon
|
||||
val awardOnLoss = Cobblemon.config.awardExperienceOnBattleLoss
|
||||
this.actors.forEach { actor ->
|
||||
@@ -347,7 +335,6 @@ open class PokemonBattle(
|
||||
}
|
||||
}
|
||||
}
|
||||
CobblemonEvents.BATTLE_ENDED.post(BattleEndedEvent(this, result, forfeitedBy.takeIf { result == BattleEndResult.FORFEIT }))
|
||||
sendUpdate(BattleEndPacket())
|
||||
BattleRegistry.closeBattle(this)
|
||||
}
|
||||
@@ -415,10 +402,12 @@ open class PokemonBattle(
|
||||
|
||||
fun dispatch(dispatcher: () -> DispatchResult) {
|
||||
dispatches.add(BattleDispatch { dispatcher() })
|
||||
|
||||
}
|
||||
|
||||
fun dispatchToFront(dispatcher: () -> DispatchResult) {
|
||||
dispatches.addFirst(BattleDispatch { dispatcher() })
|
||||
|
||||
}
|
||||
|
||||
fun dispatchWaitingToFront(delaySeconds: Float = 1F, dispatcher: () -> Unit) {
|
||||
@@ -469,12 +458,21 @@ open class PokemonBattle(
|
||||
dispatches.addFirst(dispatcher)
|
||||
}
|
||||
|
||||
fun doWhenClear(action: () -> Unit) {
|
||||
afterDispatches.add(action)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
try {
|
||||
while (dispatchResult.canProceed()) {
|
||||
val dispatch = dispatches.poll() ?: break
|
||||
dispatchResult = dispatch(this)
|
||||
}
|
||||
|
||||
if (dispatches.isEmpty()) {
|
||||
afterDispatches.toList().forEach { it() }
|
||||
afterDispatches.clear()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Exception while ticking a battle. Saving battle log.", e)
|
||||
val message = battleLang("crash").red()
|
||||
@@ -518,7 +516,6 @@ open class PokemonBattle(
|
||||
.forEach{it.pokemon.heal()}
|
||||
CobblemonEvents.BATTLE_FLED.post(BattleFledEvent(this, actors.asSequence().filterIsInstance<PlayerBattleActor>().iterator().next()))
|
||||
actors.filterIsInstance<EntityBackedBattleActor<*>>().mapNotNull { it.entity }.forEach { it.sendSystemMessage(battleLang("flee").yellow()) }
|
||||
fled = true
|
||||
stop()
|
||||
}
|
||||
}
|
||||
@@ -556,7 +553,6 @@ open class PokemonBattle(
|
||||
private fun checkForfeit(): Boolean {
|
||||
val forfeit = actors.find { it.responses.any { it is ForfeitActionResponse } }
|
||||
return forfeit?.let {
|
||||
forfeitedBy = it
|
||||
this.dispatchWaiting { this.broadcastChatMessage(battleLang("forfeit", it.getName()).red()) }
|
||||
writeShowdownAction(">forcelose ${it.showdownId}")
|
||||
true
|
||||
|
||||
+12
-9
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
@@ -24,8 +26,9 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacke
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.UUID
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
abstract class BattleActor(
|
||||
val uuid: UUID,
|
||||
@@ -52,6 +55,11 @@ abstract class BattleActor(
|
||||
var itemsUsed = mutableListOf<BagItem>()
|
||||
/** For when battles start, it's the number of Pokémon that are still in the process of being sent out (animation wise) */
|
||||
var stillSendingOutCount = 0
|
||||
/**
|
||||
* Optional override for send-out positions. When present the indexes follow the
|
||||
* actor's active slot order (e.g. slot `a` -> index 0).
|
||||
*/
|
||||
var stadiumSendOutPositions: List<Vec3>? = null
|
||||
|
||||
abstract val type: ActorType
|
||||
|
||||
@@ -74,7 +82,7 @@ abstract class BattleActor(
|
||||
fun turn() {
|
||||
val request = request ?: return
|
||||
responses.clear()
|
||||
if (activePokemon.any { it.isAlive() }) {
|
||||
if(activePokemon.any { it.isAlive() }) {
|
||||
mustChoose = true
|
||||
sendUpdate(BattleMakeChoicePacket())
|
||||
}
|
||||
@@ -88,15 +96,15 @@ abstract class BattleActor(
|
||||
}
|
||||
}
|
||||
|
||||
fun postUpdate() {
|
||||
fun upkeep() {
|
||||
val request = request ?: return
|
||||
val forceSwitchPokemon = request.forceSwitch.mapIndexedNotNull { index, b -> if (b) activePokemon[index] else null }
|
||||
if (forceSwitchPokemon.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
mustChoose = true // This must happen before the sendUpdate, not after, otherwise AI-driven actors will get locked (they may reply instantly)
|
||||
sendUpdate(BattleMakeChoicePacket())
|
||||
mustChoose = true
|
||||
}
|
||||
|
||||
fun setActionResponses(responses: List<ShowdownActionResponse>) {
|
||||
@@ -152,11 +160,6 @@ abstract class BattleActor(
|
||||
open fun sendMessage(component: Component) {
|
||||
sendUpdate(BattleMessagePacket(component))
|
||||
}
|
||||
/**
|
||||
* Called when this actor's Pokemon uses a move. Default implementation does nothing.
|
||||
* Subclasses (NPC-backed actors, players, etc) may override to trigger animations or UI.
|
||||
*/
|
||||
open fun onUseMove(user: BattlePokemon, target: BattlePokemon?) {}
|
||||
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
|
||||
open fun sendUpdate(packet: NetworkPacket<*>) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.chunk.LevelChunk
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus
|
||||
import java.util.ArrayDeque
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Handles chunk forceloading for cameras.
|
||||
* Chunks are queued and processed in small batches each tick to avoid overwhelming the server.
|
||||
*/
|
||||
object CameraChunkLoader {
|
||||
private const val MAX_CHUNKS_PER_TICK = 16
|
||||
|
||||
private data class CameraState(
|
||||
var dimension: ResourceKey<Level>,
|
||||
val chunks: MutableSet<Long>
|
||||
)
|
||||
|
||||
private data class ChunkRequest(
|
||||
val dimension: ResourceKey<Level>,
|
||||
val chunkPos: ChunkPos,
|
||||
val load: Boolean
|
||||
)
|
||||
|
||||
private val cameraStates = mutableMapOf<UUID, CameraState>()
|
||||
private val chunkOwners = mutableMapOf<ResourceKey<Level>, MutableMap<Long, MutableSet<UUID>>>()
|
||||
private val pendingRequests: ArrayDeque<ChunkRequest> = ArrayDeque()
|
||||
private val pendingAsyncLoads = mutableSetOf<Pair<ResourceKey<Level>, Long>>()
|
||||
|
||||
fun ensureArea(level: ServerLevel, cameraId: UUID, centerChunk: ChunkPos, radius: Int) {
|
||||
val dimension = level.dimension()
|
||||
val state = cameraStates[cameraId]?.takeIf { it.dimension == dimension } ?: run {
|
||||
cameraStates.remove(cameraId)?.let { oldState ->
|
||||
oldState.chunks.forEach { chunkLong ->
|
||||
removeOwnership(oldState.dimension, chunkLong, cameraId)
|
||||
}
|
||||
}
|
||||
|
||||
CameraState(dimension, mutableSetOf()).also { cameraStates[cameraId] = it }
|
||||
}
|
||||
|
||||
val newChunks = buildChunkSet(centerChunk, radius)
|
||||
val currentChunks = state.chunks
|
||||
|
||||
val toRemove = currentChunks.minus(newChunks)
|
||||
val toAdd = newChunks.minus(currentChunks)
|
||||
|
||||
if (toRemove.isNotEmpty()) {
|
||||
toRemove.forEach { chunkLong ->
|
||||
removeOwnership(dimension, chunkLong, cameraId)
|
||||
currentChunks.remove(chunkLong)
|
||||
}
|
||||
}
|
||||
|
||||
if (toAdd.isNotEmpty()) {
|
||||
toAdd.forEach { chunkLong ->
|
||||
addOwnership(dimension, chunkLong, cameraId)
|
||||
currentChunks.add(chunkLong)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear(cameraId: UUID) {
|
||||
val state = cameraStates.remove(cameraId) ?: return
|
||||
val dimension = state.dimension
|
||||
state.chunks.forEach { chunkLong ->
|
||||
removeOwnership(dimension, chunkLong, cameraId)
|
||||
}
|
||||
}
|
||||
|
||||
fun tick(server: MinecraftServer) {
|
||||
if (pendingRequests.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
var processed = 0
|
||||
while (processed < MAX_CHUNKS_PER_TICK && pendingRequests.isNotEmpty()) {
|
||||
val request = pendingRequests.removeFirst()
|
||||
val level = server.getLevel(request.dimension) ?: continue
|
||||
|
||||
level.setChunkForced(request.chunkPos.x, request.chunkPos.z, request.load)
|
||||
|
||||
if (request.load) {
|
||||
scheduleAsyncLoad(level, request.dimension, request.chunkPos)
|
||||
}
|
||||
|
||||
processed++
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOwnership(dimension: ResourceKey<Level>, chunkLong: Long, cameraId: UUID) {
|
||||
val ownersForDimension = chunkOwners.computeIfAbsent(dimension) { mutableMapOf() }
|
||||
val owners = ownersForDimension.computeIfAbsent(chunkLong) { mutableSetOf() }
|
||||
|
||||
if (owners.add(cameraId) && owners.size == 1) {
|
||||
pendingRequests.add(
|
||||
ChunkRequest(
|
||||
dimension,
|
||||
ChunkPos(chunkLong),
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeOwnership(dimension: ResourceKey<Level>, chunkLong: Long, cameraId: UUID) {
|
||||
val ownersForDimension = chunkOwners[dimension] ?: return
|
||||
val owners = ownersForDimension[chunkLong] ?: return
|
||||
|
||||
if (!owners.remove(cameraId)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (owners.isEmpty()) {
|
||||
ownersForDimension.remove(chunkLong)
|
||||
if (ownersForDimension.isEmpty()) {
|
||||
chunkOwners.remove(dimension)
|
||||
}
|
||||
|
||||
pendingRequests.add(
|
||||
ChunkRequest(
|
||||
dimension,
|
||||
ChunkPos(chunkLong),
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scheduleAsyncLoad(level: ServerLevel, dimension: ResourceKey<Level>, chunkPos: ChunkPos) {
|
||||
val key = dimension to chunkPos.toLong()
|
||||
|
||||
if (!pendingAsyncLoads.add(key)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Cobblemon.LOGGER.isDebugEnabled) {
|
||||
Cobblemon.LOGGER.debug(
|
||||
"[CobblemonCamera] Scheduling async load for chunk {} in {}",
|
||||
chunkPos,
|
||||
dimension.location()
|
||||
)
|
||||
}
|
||||
|
||||
level.chunkSource
|
||||
.getChunkFuture(chunkPos.x, chunkPos.z, ChunkStatus.FULL, true)
|
||||
.thenAcceptAsync({ result ->
|
||||
result.ifSuccess { chunkAccess ->
|
||||
if (chunkAccess is LevelChunk && Cobblemon.LOGGER.isDebugEnabled) {
|
||||
Cobblemon.LOGGER.debug(
|
||||
"[CobblemonCamera] Async loaded chunk {} (light={}, entities={})",
|
||||
chunkPos,
|
||||
chunkAccess.isLightCorrect,
|
||||
chunkAccess.blockEntities.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pendingAsyncLoads.remove(key)
|
||||
}, level.server)
|
||||
.exceptionally { throwable ->
|
||||
level.server.execute {
|
||||
pendingAsyncLoads.remove(key)
|
||||
Cobblemon.LOGGER.warn(
|
||||
"[CobblemonCamera] Exception while async loading chunk {}: {}",
|
||||
chunkPos,
|
||||
throwable.message
|
||||
)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildChunkSet(centerChunk: ChunkPos, radius: Int): MutableSet<Long> {
|
||||
val chunks = mutableSetOf<Long>()
|
||||
|
||||
for (x in centerChunk.x - radius..centerChunk.x + radius) {
|
||||
for (z in centerChunk.z - radius..centerChunk.z + radius) {
|
||||
chunks.add(ChunkPos.asLong(x, z))
|
||||
}
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.integration.server.CameraExecutor
|
||||
import com.cobblemon.mod.common.api.camera.integration.server.CameraInstance
|
||||
import com.cobblemon.mod.common.api.camera.integration.server.CameraNetworkSync
|
||||
import com.cobblemon.mod.common.api.camera.registry.TimelineRegistry
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.server.level.ChunkTrackingView
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Server-side camera controller.
|
||||
*
|
||||
* Orchestrates camera instance, executor, network sync, and chunk/viewer management.
|
||||
* Delegates responsibilities to focused components.
|
||||
*/
|
||||
class CameraController(
|
||||
override val id: UUID = UUID.randomUUID(),
|
||||
val context: Map<String, MoValue> = emptyMap(),
|
||||
override val origin: Vec3 = Vec3.ZERO,
|
||||
val queryFunctions: Map<String, java.util.function.Function<MoParams, Any>> = emptyMap()
|
||||
) : ICameraController {
|
||||
private val instance: CameraInstance
|
||||
private val runtime: MoLangRuntime
|
||||
private lateinit var executor: CameraExecutor
|
||||
private lateinit var networkSync: CameraNetworkSync
|
||||
|
||||
private val viewers = mutableSetOf<UUID>()
|
||||
private val viewerChunkStates = mutableMapOf<UUID, ViewerChunkState>()
|
||||
private var level: ServerLevel? = null
|
||||
|
||||
init {
|
||||
runtime = MoLangRuntime().setup()
|
||||
runtime.environment.query.functions.putAll(queryFunctions)
|
||||
instance = CameraInstance(id, origin, context, queryFunctions)
|
||||
executor = CameraExecutor(instance, runtime, context)
|
||||
networkSync = CameraNetworkSync(id, viewers)
|
||||
Cobblemon.LOGGER.debug("[CameraController] Initialized $id")
|
||||
}
|
||||
|
||||
private data class ViewerChunkState(
|
||||
val viewDistance: Int,
|
||||
var centerChunk: ChunkPos,
|
||||
var chunkView: ChunkTrackingView
|
||||
)
|
||||
|
||||
override fun startTimeline(timelineId: String): Boolean {
|
||||
val timeline = TimelineRegistry.getTimeline(timelineId)
|
||||
if (timeline == null) {
|
||||
Cobblemon.LOGGER.error("[CameraController] Timeline '$timelineId' not found")
|
||||
return false
|
||||
}
|
||||
|
||||
return if (instance.startTimeline(timeline)) {
|
||||
Cobblemon.LOGGER.info("[CameraController] Started '$timelineId'")
|
||||
true
|
||||
} else {
|
||||
Cobblemon.LOGGER.error("[CameraController] Engine start failed for '$timelineId'")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
val currentLevel = level ?: return
|
||||
val stillActive = executor.tick(currentLevel, networkSync) { chunkPos ->
|
||||
updateViewerChunkViews(chunkPos)
|
||||
}
|
||||
|
||||
// If timeline ended, stop the camera and notify all viewers
|
||||
if (!stillActive && viewers.isNotEmpty()) {
|
||||
Cobblemon.LOGGER.info("[CameraController] Timeline ended naturally, stopping camera")
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViewerChunkViews(chunkPos: ChunkPos) {
|
||||
viewerChunkStates.forEach { (viewerId, state) ->
|
||||
if (state.centerChunk == chunkPos) return@forEach
|
||||
|
||||
state.centerChunk = chunkPos
|
||||
val newView = ChunkTrackingView.of(chunkPos, state.viewDistance)
|
||||
state.chunkView = newView
|
||||
CameraManager.registerViewerChunkView(viewerId, newView)
|
||||
}
|
||||
|
||||
val serverLevel = level
|
||||
if (serverLevel != null && viewerChunkStates.isNotEmpty()) {
|
||||
val maxDistance = viewerChunkStates.values.maxOf { it.viewDistance }
|
||||
CameraChunkLoader.ensureArea(serverLevel, id, chunkPos, maxDistance)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addViewer(player: ServerPlayer) {
|
||||
viewers.add(player.uuid)
|
||||
registerViewerChunkView(player, origin)
|
||||
networkSync.sendStart(origin)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player as a feed viewer without sending CameraStartPacket.
|
||||
*
|
||||
* The player receives transform/focus updates like a normal viewer, but
|
||||
* the camera will NOT become the primary camera on the client. This is
|
||||
* used for secondary cameras (e.g. Camera B in split-screen) that render
|
||||
* to an off-screen RenderTarget via CameraFeedHandler.
|
||||
*/
|
||||
fun addFeedViewer(player: ServerPlayer) {
|
||||
viewers.add(player.uuid)
|
||||
registerViewerChunkView(player, origin)
|
||||
// No sendStart() — intentionally skipped so the client doesn't set this as primary
|
||||
}
|
||||
|
||||
private fun registerViewerChunkView(player: ServerPlayer, centerPosition: Vec3) {
|
||||
val playerLevel = player.serverLevel()
|
||||
if (level == null || level != playerLevel) {
|
||||
level?.let { CameraChunkLoader.clear(id) }
|
||||
level = playerLevel
|
||||
}
|
||||
|
||||
val maxViewDistance = player.server.playerList.viewDistance
|
||||
val viewDistance = Mth.clamp(player.requestedViewDistance(), 2, maxViewDistance)
|
||||
val chunkPos = chunkPosFrom(centerPosition)
|
||||
val chunkView = ChunkTrackingView.of(chunkPos, viewDistance)
|
||||
|
||||
viewerChunkStates[player.uuid] = ViewerChunkState(viewDistance, chunkPos, chunkView)
|
||||
CameraManager.registerViewerChunkView(player.uuid, chunkView)
|
||||
|
||||
val maxDistance = viewerChunkStates.values.maxOfOrNull { it.viewDistance } ?: viewDistance
|
||||
CameraChunkLoader.ensureArea(playerLevel, id, chunkPos, maxDistance)
|
||||
}
|
||||
|
||||
override fun removeViewer(player: ServerPlayer) {
|
||||
// Send end packet to THIS player specifically
|
||||
networkSync.sendEndToPlayer(player)
|
||||
|
||||
viewers.remove(player.uuid)
|
||||
viewerChunkStates.remove(player.uuid)
|
||||
CameraManager.clearViewerChunkView(player.uuid)
|
||||
|
||||
if (viewers.isEmpty()) {
|
||||
CameraChunkLoader.clear(id)
|
||||
level = null
|
||||
} else {
|
||||
val serverLevel = level
|
||||
if (serverLevel != null && viewerChunkStates.isNotEmpty()) {
|
||||
val state = viewerChunkStates.values.first()
|
||||
val maxDistance = viewerChunkStates.values.maxOf { it.viewDistance }
|
||||
CameraChunkLoader.ensureArea(serverLevel, id, state.centerChunk, maxDistance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
instance.stop()
|
||||
networkSync.sendEnd()
|
||||
viewerChunkStates.keys.toList().forEach { CameraManager.clearViewerChunkView(it) }
|
||||
viewerChunkStates.clear()
|
||||
CameraChunkLoader.clear(id)
|
||||
level = null
|
||||
viewers.clear()
|
||||
}
|
||||
|
||||
override fun isActive() = instance.isActive()
|
||||
|
||||
fun setFlag(name: String, value: Boolean) = setVariable(name, if (value) 1.0 else 0.0)
|
||||
|
||||
override fun setVariable(name: String, value: Double) = instance.setVariable(name, value)
|
||||
|
||||
override fun setFocusOverride(position: Vec3?) {
|
||||
instance.focusOverride = position
|
||||
}
|
||||
|
||||
override fun triggerInstantTransition() = instance.triggerInstantTransition()
|
||||
|
||||
override fun addQueryFunctions(functions: Map<String, java.util.function.Function<MoParams, Any>>) {
|
||||
runtime.environment.query.functions.putAll(functions)
|
||||
instance.addQueryFunctions(functions)
|
||||
}
|
||||
|
||||
private fun chunkPosFrom(position: Vec3): ChunkPos {
|
||||
val chunkX = SectionPos.blockToSectionCoord(Mth.floor(position.x))
|
||||
val chunkZ = SectionPos.blockToSectionCoord(Mth.floor(position.z))
|
||||
return ChunkPos(chunkX, chunkZ)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.components.CameraChunkManager
|
||||
import com.cobblemon.mod.common.api.camera.components.CameraLifecycleManager
|
||||
import com.cobblemon.mod.common.api.camera.components.CameraViewerManager
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.camera.CameraStartedEvent
|
||||
import com.cobblemon.mod.common.api.events.camera.CameraStoppedEvent
|
||||
import com.cobblemon.mod.common.api.events.camera.CameraViewerAddedEvent
|
||||
import com.cobblemon.mod.common.api.events.camera.CameraViewerRemovedEvent
|
||||
import com.cobblemon.mod.common.battles.stadium.StadiumBattleCameraCoordinator
|
||||
import net.minecraft.server.level.ChunkTrackingView
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import java.util.function.Function
|
||||
|
||||
object CameraManager : ICameraManager {
|
||||
|
||||
private val lifecycleManager = CameraLifecycleManager()
|
||||
private val viewerManager = CameraViewerManager()
|
||||
private val chunkManager = CameraChunkManager()
|
||||
|
||||
override fun startCamera(
|
||||
timelineId: String,
|
||||
context: Map<String, MoValue>,
|
||||
viewers: List<ServerPlayer>,
|
||||
origin: Vec3,
|
||||
queryFunctions: Map<String, Function<MoParams, Any>>
|
||||
): UUID? {
|
||||
val camera = CameraController(
|
||||
context = context,
|
||||
origin = origin,
|
||||
queryFunctions = queryFunctions
|
||||
)
|
||||
|
||||
val preEvent = CameraStartedEvent.Pre(camera.id, camera, timelineId, viewers)
|
||||
CobblemonEvents.CAMERA_STARTED_PRE.emit(preEvent)
|
||||
if (preEvent.isCanceled) {
|
||||
Cobblemon.LOGGER.info("[CobblemonCamera] Camera start canceled by event listener")
|
||||
return null
|
||||
}
|
||||
|
||||
if (!camera.startTimeline(timelineId)) {
|
||||
Cobblemon.LOGGER.warn("[CobblemonCamera] Failed to start timeline '$timelineId'")
|
||||
return null
|
||||
}
|
||||
|
||||
lifecycleManager.registerCamera(camera)
|
||||
|
||||
viewers.forEach { player ->
|
||||
camera.addViewer(player)
|
||||
viewerManager.assignViewerToCamera(player.uuid, camera.id)
|
||||
|
||||
CobblemonEvents.CAMERA_VIEWER_ADDED.emit(
|
||||
CameraViewerAddedEvent(camera.id, camera, player)
|
||||
)
|
||||
}
|
||||
|
||||
CobblemonEvents.CAMERA_STARTED_POST.emit(
|
||||
CameraStartedEvent.Post(camera.id, camera, timelineId, viewers)
|
||||
)
|
||||
|
||||
Cobblemon.LOGGER.info("[CobblemonCamera] Started camera ${camera.id} with timeline '$timelineId' for ${viewers.size} viewers")
|
||||
return camera.id
|
||||
}
|
||||
|
||||
override fun addViewerToCamera(player: ServerPlayer, cameraId: UUID): Boolean {
|
||||
val camera = lifecycleManager.getCamera(cameraId) ?: return false
|
||||
|
||||
camera.addViewer(player)
|
||||
viewerManager.assignViewerToCamera(player.uuid, camera.id)
|
||||
|
||||
CobblemonEvents.CAMERA_VIEWER_ADDED.emit(
|
||||
CameraViewerAddedEvent(camera.id, camera, player)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun stopCamera(cameraId: UUID) {
|
||||
lifecycleManager.getCamera(cameraId)?.let { camera ->
|
||||
CobblemonEvents.CAMERA_STOPPED_PRE.emit(
|
||||
CameraStoppedEvent.Pre(cameraId, camera, CameraStoppedEvent.StopReason.MANUAL)
|
||||
)
|
||||
|
||||
camera.stop()
|
||||
lifecycleManager.unregisterCamera(cameraId)
|
||||
viewerManager.removeViewersForCamera(cameraId)
|
||||
|
||||
CobblemonEvents.CAMERA_STOPPED_POST.emit(
|
||||
CameraStoppedEvent.Post(cameraId, camera, CameraStoppedEvent.StopReason.MANUAL)
|
||||
)
|
||||
|
||||
Cobblemon.LOGGER.info("[CobblemonCamera] Stopped camera $cameraId")
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopCameraForPlayer(player: ServerPlayer) {
|
||||
viewerManager.getCameraForPlayer(player.uuid)?.let { cameraId ->
|
||||
lifecycleManager.getCamera(cameraId)?.let { camera ->
|
||||
camera.removeViewer(player)
|
||||
viewerManager.removeViewer(player.uuid)
|
||||
|
||||
CobblemonEvents.CAMERA_VIEWER_REMOVED.emit(
|
||||
CameraViewerRemovedEvent(cameraId, camera, player)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPlayerCamera(player: ServerPlayer): ICameraController? {
|
||||
val cameraId = viewerManager.getCameraForPlayer(player.uuid) ?: return null
|
||||
return lifecycleManager.getCamera(cameraId)
|
||||
}
|
||||
|
||||
override fun getCamera(cameraId: UUID): ICameraController? {
|
||||
return lifecycleManager.getCamera(cameraId)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
lifecycleManager.tick()
|
||||
StadiumBattleCameraCoordinator.tickSecondaryCameras()
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
lifecycleManager.clear()
|
||||
viewerManager.clear()
|
||||
chunkManager.clear()
|
||||
}
|
||||
|
||||
internal fun registerViewerChunkView(playerId: UUID, newView: ChunkTrackingView) {
|
||||
chunkManager.registerViewerChunkView(playerId, newView)
|
||||
}
|
||||
|
||||
internal fun clearViewerChunkView(playerId: UUID) {
|
||||
chunkManager.clearViewerChunkView(playerId)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getPlayerChunkView(player: ServerPlayer): ChunkTrackingView? {
|
||||
return chunkManager.getPlayerChunkView(player.uuid)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun consumeChunkViewUpdate(player: ServerPlayer): ChunkViewUpdate? {
|
||||
return chunkManager.consumeChunkViewUpdate(player.uuid)?.let {
|
||||
ChunkViewUpdate(it.previous, it.current)
|
||||
}
|
||||
}
|
||||
|
||||
data class ChunkViewUpdate(val previous: ChunkTrackingView?, val current: ChunkTrackingView?)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
/**
|
||||
* Represents a camera rotation in 3D space.
|
||||
*
|
||||
* @property yaw Horizontal rotation (left/right) in degrees
|
||||
* @property pitch Vertical rotation (up/down) in degrees
|
||||
*/
|
||||
data class CameraRotation(
|
||||
val yaw: Float,
|
||||
val pitch: Float
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.model.CameraTrack
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
object CameraTrackRegistry {
|
||||
private val trackDeserializers = mutableMapOf<String, (JsonObject) -> CameraTrack>()
|
||||
|
||||
fun registerTrackType(trackType: String, deserializer: (JsonObject) -> CameraTrack) {
|
||||
if (trackDeserializers.containsKey(trackType)) {
|
||||
Cobblemon.LOGGER.warn("[CameraTrackRegistry] Track type '$trackType' is already registered, overwriting")
|
||||
}
|
||||
trackDeserializers[trackType] = deserializer
|
||||
Cobblemon.LOGGER.debug("[CameraTrackRegistry] Registered camera track type: $trackType")
|
||||
}
|
||||
|
||||
fun deserializeTrack(trackType: String, json: JsonObject): CameraTrack? {
|
||||
val deserializer = trackDeserializers[trackType]
|
||||
if (deserializer == null) {
|
||||
Cobblemon.LOGGER.warn("[CameraTrackRegistry] Unknown track type: $trackType")
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
deserializer(json)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[CameraTrackRegistry] Failed to deserialize track type '$trackType': ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getRegisteredTrackTypes(): Set<String> = trackDeserializers.keys.toSet()
|
||||
|
||||
fun isTrackTypeRegistered(trackType: String): Boolean = trackDeserializers.containsKey(trackType)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import java.util.function.Function
|
||||
|
||||
interface ICameraController {
|
||||
val id: UUID
|
||||
val origin: Vec3
|
||||
fun startTimeline(timelineId: String): Boolean
|
||||
fun tick()
|
||||
fun stop()
|
||||
fun isActive(): Boolean
|
||||
fun addViewer(player: ServerPlayer)
|
||||
fun removeViewer(player: ServerPlayer)
|
||||
fun setVariable(name: String, value: Double)
|
||||
fun setFocusOverride(position: Vec3?)
|
||||
/** Triggers an instant camera transition (no client-side interpolation) on the next tick. */
|
||||
fun triggerInstantTransition()
|
||||
/** Injects additional MoLang query functions into the camera's runtime after creation. */
|
||||
fun addQueryFunctions(functions: Map<String, Function<MoParams, Any>>)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
interface ICameraCoordinator {
|
||||
fun bind(cameraId: UUID)
|
||||
fun release()
|
||||
fun getCameraId(): UUID?
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import java.util.function.Function
|
||||
|
||||
interface ICameraManager {
|
||||
fun startCamera(
|
||||
timelineId: String,
|
||||
context: Map<String, MoValue> = emptyMap(),
|
||||
viewers: List<ServerPlayer> = emptyList(),
|
||||
origin: Vec3 = Vec3.ZERO,
|
||||
queryFunctions: Map<String, Function<MoParams, Any>> = emptyMap()
|
||||
): UUID?
|
||||
|
||||
fun addViewerToCamera(player: ServerPlayer, cameraId: UUID): Boolean
|
||||
fun stopCamera(cameraId: UUID)
|
||||
fun stopCameraForPlayer(player: ServerPlayer)
|
||||
fun getPlayerCamera(player: ServerPlayer): ICameraController?
|
||||
fun getCamera(cameraId: UUID): ICameraController?
|
||||
fun tick()
|
||||
fun clear()
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.components
|
||||
|
||||
import net.minecraft.server.level.ChunkTrackingView
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class CameraChunkManager {
|
||||
private val playerChunkViews = ConcurrentHashMap<UUID, ChunkTrackingView>()
|
||||
private val chunkViewUpdates = ConcurrentHashMap<UUID, ChunkViewUpdate>()
|
||||
|
||||
data class ChunkViewUpdate(val previous: ChunkTrackingView?, val current: ChunkTrackingView?)
|
||||
|
||||
fun registerViewerChunkView(playerId: UUID, newView: ChunkTrackingView) {
|
||||
val previous = playerChunkViews.put(playerId, newView)
|
||||
|
||||
if (previous == null || previous != newView) {
|
||||
chunkViewUpdates[playerId] = ChunkViewUpdate(previous, newView)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearViewerChunkView(playerId: UUID) {
|
||||
val previous = playerChunkViews.remove(playerId)
|
||||
|
||||
if (previous != null) {
|
||||
chunkViewUpdates[playerId] = ChunkViewUpdate(previous, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPlayerChunkView(playerId: UUID): ChunkTrackingView? = playerChunkViews[playerId]
|
||||
|
||||
fun consumeChunkViewUpdate(playerId: UUID): ChunkViewUpdate? = chunkViewUpdates.remove(playerId)
|
||||
|
||||
fun clear() {
|
||||
playerChunkViews.clear()
|
||||
chunkViewUpdates.clear()
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.components
|
||||
|
||||
import com.cobblemon.mod.common.api.camera.ICameraController
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.camera.CameraStoppedEvent
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class CameraLifecycleManager {
|
||||
private val cameras = ConcurrentHashMap<UUID, ICameraController>()
|
||||
|
||||
fun registerCamera(camera: ICameraController) {
|
||||
cameras[camera.id] = camera
|
||||
}
|
||||
|
||||
fun unregisterCamera(cameraId: UUID): ICameraController? {
|
||||
return cameras.remove(cameraId)
|
||||
}
|
||||
|
||||
fun getCamera(cameraId: UUID): ICameraController? = cameras[cameraId]
|
||||
|
||||
fun getAllCameras(): Collection<ICameraController> = cameras.values
|
||||
|
||||
fun tick() {
|
||||
cameras.values.forEach { it.tick() }
|
||||
|
||||
cameras.entries.removeIf { (id, camera) ->
|
||||
if (!camera.isActive()) {
|
||||
CobblemonEvents.CAMERA_STOPPED_POST.emit(
|
||||
CameraStoppedEvent.Post(id, camera, CameraStoppedEvent.StopReason.COMPLETED)
|
||||
)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
cameras.values.forEach {
|
||||
it.stop()
|
||||
CobblemonEvents.CAMERA_STOPPED_POST.emit(
|
||||
CameraStoppedEvent.Post(it.id, it, CameraStoppedEvent.StopReason.MANUAL)
|
||||
)
|
||||
}
|
||||
cameras.clear()
|
||||
}
|
||||
|
||||
fun size(): Int = cameras.size
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.components
|
||||
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class CameraViewerManager {
|
||||
private val playerCameras = ConcurrentHashMap<UUID, UUID>()
|
||||
|
||||
fun assignViewerToCamera(playerId: UUID, cameraId: UUID) {
|
||||
playerCameras[playerId] = cameraId
|
||||
}
|
||||
|
||||
fun removeViewer(playerId: UUID): UUID? {
|
||||
return playerCameras.remove(playerId)
|
||||
}
|
||||
|
||||
fun getCameraForPlayer(playerId: UUID): UUID? {
|
||||
return playerCameras[playerId]
|
||||
}
|
||||
|
||||
fun removeViewersForCamera(cameraId: UUID) {
|
||||
playerCameras.entries.removeIf { it.value == cameraId }
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
playerCameras.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.engine
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.CameraRotation
|
||||
import com.cobblemon.mod.common.api.camera.model.*
|
||||
import com.cobblemon.mod.common.api.camera.result.ValidationError
|
||||
import com.cobblemon.mod.common.api.camera.track.EventTrack
|
||||
import com.cobblemon.mod.common.api.camera.track.FiredGuiAction
|
||||
import com.cobblemon.mod.common.api.camera.track.GuiTrack
|
||||
import com.cobblemon.mod.common.api.tween.EasingRegistry
|
||||
import com.cobblemon.mod.common.client.camera.CameraInterpolationUtils
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Thread-safe timeline execution engine.
|
||||
*
|
||||
* Uses immutable state snapshots for thread safety. MoLang evaluation is NOT
|
||||
* thread-safe - callers must ensure tick() is called from the same thread.
|
||||
*
|
||||
* @param id Unique identifier for this engine instance
|
||||
* @param timeline The timeline to execute
|
||||
* @param origin The world position origin for relative coordinates
|
||||
* @param runtime The MoLang runtime for expression evaluation
|
||||
* @param providedContext Initial context values (parameter values) to inject into MoLang
|
||||
*/
|
||||
class TimelineEngine(
|
||||
val id: UUID,
|
||||
private val timeline: Timeline,
|
||||
val origin: Vec3,
|
||||
private val runtime: MoLangRuntime,
|
||||
private val providedContext: Map<String, MoValue> = emptyMap()
|
||||
) {
|
||||
@Volatile
|
||||
private var state: ExecutionState = ExecutionState.Stopped
|
||||
|
||||
private var previousExecutionState: ExecutionState.Playing? = null
|
||||
|
||||
private var instantTransitionOccurred = false
|
||||
|
||||
private val customVariables = ConcurrentHashMap<String, Double>()
|
||||
|
||||
/** GUI actions fired during the current tick, consumed by the executor. */
|
||||
private val pendingGuiActions = mutableListOf<FiredGuiAction>()
|
||||
|
||||
private val timeOnCurrentPathValue = DoubleValue(0.0)
|
||||
private val totalTimeValue = DoubleValue(0.0)
|
||||
private val runnerIdValue = StringValue(id.toString())
|
||||
|
||||
fun start(): Boolean {
|
||||
if (state !is ExecutionState.Stopped) {
|
||||
Cobblemon.LOGGER.warn("[TimelineEngine] Cannot start: already active (id=$id)")
|
||||
return false
|
||||
}
|
||||
|
||||
val errors = timeline.validate()
|
||||
if (errors.any { it.severity == ValidationError.Severity.ERROR }) {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Timeline '${timeline.id}' validation failed: ${errors.joinToString { it.toString() }}")
|
||||
return false
|
||||
}
|
||||
|
||||
if (!validateAndInjectParameters()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val startState = timeline.getState(timeline.startStateId)
|
||||
if (startState == null) {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Start state '${timeline.startStateId}' not found in timeline '${timeline.id}'")
|
||||
return false
|
||||
}
|
||||
|
||||
state = ExecutionState.Playing(
|
||||
currentState = startState,
|
||||
stateTime = 0f,
|
||||
totalTime = 0f,
|
||||
lastTickNanos = System.nanoTime(),
|
||||
transitionState = TransitionState.None,
|
||||
progressMemory = emptyMap(),
|
||||
executedEvents = emptySet()
|
||||
)
|
||||
|
||||
Cobblemon.LOGGER.info("[TimelineEngine] Started timeline '${timeline.id}' in state '${startState.id}'")
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all required parameters are provided and injects parameters
|
||||
* (including defaults) into the MoLang runtime.
|
||||
*/
|
||||
private fun validateAndInjectParameters(): Boolean {
|
||||
if (timeline.parameters.isEmpty()) return true
|
||||
|
||||
val providedVarNames = providedContext.keys
|
||||
val missingParams = timeline.validateParameters(providedVarNames)
|
||||
if (missingParams.isNotEmpty()) {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Timeline '${timeline.id}' missing required parameters: ${missingParams.joinToString(", ")}")
|
||||
return false
|
||||
}
|
||||
|
||||
providedContext.forEach { (name, value) ->
|
||||
runtime.environment.variable.map[name] = value
|
||||
}
|
||||
|
||||
for ((paramName, definition) in timeline.parameters) {
|
||||
val default = definition.default ?: continue
|
||||
|
||||
when (default) {
|
||||
is ParameterDefault.NumberDefault -> {
|
||||
if (paramName !in providedVarNames) {
|
||||
runtime.environment.variable.map[paramName] = DoubleValue(default.value)
|
||||
Cobblemon.LOGGER.debug("[TimelineEngine] Using default for parameter '$paramName': ${default.value}")
|
||||
}
|
||||
}
|
||||
is ParameterDefault.Vec3Default -> {
|
||||
val varNames = definition.getVariableNames(paramName)
|
||||
val xVar = varNames[0]
|
||||
val yVar = varNames[1]
|
||||
val zVar = varNames[2]
|
||||
|
||||
if (xVar !in providedVarNames) {
|
||||
runtime.environment.variable.map[xVar] = DoubleValue(default.x)
|
||||
}
|
||||
if (yVar !in providedVarNames) {
|
||||
runtime.environment.variable.map[yVar] = DoubleValue(default.y)
|
||||
}
|
||||
if (zVar !in providedVarNames) {
|
||||
runtime.environment.variable.map[zVar] = DoubleValue(default.z)
|
||||
}
|
||||
|
||||
if (xVar !in providedVarNames || yVar !in providedVarNames || zVar !in providedVarNames) {
|
||||
Cobblemon.LOGGER.debug("[TimelineEngine] Using default for parameter '$paramName': (${default.x}, ${default.y}, ${default.z})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun tick(): CameraTransform? {
|
||||
val current = state
|
||||
if (current !is ExecutionState.Playing) {
|
||||
return null
|
||||
}
|
||||
|
||||
previousExecutionState = current
|
||||
instantTransitionOccurred = false
|
||||
|
||||
val nowNanos = System.nanoTime()
|
||||
val deltaNanos = nowNanos - current.lastTickNanos
|
||||
val deltaSeconds = (deltaNanos / 1_000_000_000.0).toFloat()
|
||||
.coerceIn(0f, 0.5f)
|
||||
|
||||
val newStateTime = current.stateTime + deltaSeconds * current.currentState.speed
|
||||
val newTotalTime = current.totalTime + deltaSeconds
|
||||
|
||||
val transitionResult = checkTransitions(current, newStateTime, newTotalTime)
|
||||
if (transitionResult is TransitionCheckResult.TransitionNow) {
|
||||
val targetState = timeline.getState(transitionResult.targetStateId)
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Transition target state '${transitionResult.targetStateId}' not found")
|
||||
return null
|
||||
}
|
||||
|
||||
state = when (transitionResult.type) {
|
||||
TransitionType.INSTANT -> {
|
||||
instantTransitionOccurred = true
|
||||
|
||||
val savedSnapshot = if (targetState.rememberProgress) {
|
||||
current.progressMemory[targetState.id]
|
||||
} else null
|
||||
|
||||
val restoredTime = savedSnapshot?.normalizedProgress?.let {
|
||||
it * targetState.getEffectiveDuration()
|
||||
} ?: 0f
|
||||
|
||||
val restoredFocus = savedSnapshot?.lastFocus
|
||||
|
||||
val currentSnapshot = StateSnapshot(
|
||||
normalizedProgress = normalizeProgress(current.stateTime, current.currentState),
|
||||
lastFocus = current.lastEvaluatedFocus
|
||||
)
|
||||
|
||||
ExecutionState.Playing(
|
||||
currentState = targetState,
|
||||
stateTime = restoredTime,
|
||||
totalTime = newTotalTime,
|
||||
lastTickNanos = nowNanos,
|
||||
transitionState = TransitionState.None,
|
||||
progressMemory = current.progressMemory + (current.currentState.id to currentSnapshot),
|
||||
executedEvents = if (targetState.rememberProgress) current.executedEvents else emptySet(),
|
||||
lastEvaluatedFocus = restoredFocus
|
||||
)
|
||||
}
|
||||
TransitionType.BLEND -> {
|
||||
val currentTransform = evaluateState(current.currentState, current.stateTime)
|
||||
ExecutionState.Playing(
|
||||
currentState = current.currentState,
|
||||
stateTime = newStateTime,
|
||||
totalTime = newTotalTime,
|
||||
lastTickNanos = nowNanos,
|
||||
transitionState = TransitionState.Blending(
|
||||
targetState = targetState,
|
||||
startTime = newTotalTime,
|
||||
duration = transitionResult.blendDuration,
|
||||
startTransform = currentTransform
|
||||
),
|
||||
progressMemory = current.progressMemory,
|
||||
executedEvents = current.executedEvents
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val effectiveDuration = current.currentState.getEffectiveDuration()
|
||||
val (finalStateTime, finalEvents) = if (current.currentState.isLooping && effectiveDuration > 0f && newStateTime >= effectiveDuration) {
|
||||
newStateTime % effectiveDuration to emptySet<EventKey>()
|
||||
} else {
|
||||
val clampedTime = if (!current.currentState.isLooping && effectiveDuration > 0f) {
|
||||
newStateTime.coerceAtMost(effectiveDuration)
|
||||
} else {
|
||||
newStateTime
|
||||
}
|
||||
clampedTime to current.executedEvents
|
||||
}
|
||||
|
||||
state = current.copy(
|
||||
stateTime = finalStateTime,
|
||||
totalTime = newTotalTime,
|
||||
lastTickNanos = nowNanos,
|
||||
executedEvents = finalEvents
|
||||
)
|
||||
}
|
||||
|
||||
val updatedState = state
|
||||
if (updatedState is ExecutionState.Playing && updatedState.transitionState is TransitionState.Blending) {
|
||||
val blend = updatedState.transitionState
|
||||
val blendProgress = if (blend.duration > 0f) {
|
||||
((updatedState.totalTime - blend.startTime) / blend.duration).coerceIn(0f, 1f)
|
||||
} else 1f
|
||||
|
||||
if (blendProgress >= 1f) {
|
||||
val savedSnapshot = updatedState.progressMemory[blend.targetState.id]
|
||||
val restoredTime = if (blend.targetState.rememberProgress && savedSnapshot != null) {
|
||||
savedSnapshot.normalizedProgress * blend.targetState.getEffectiveDuration()
|
||||
} else 0f
|
||||
|
||||
val currentSnapshot = StateSnapshot(
|
||||
normalizedProgress = normalizeProgress(updatedState.stateTime, updatedState.currentState),
|
||||
lastFocus = null
|
||||
)
|
||||
|
||||
state = ExecutionState.Playing(
|
||||
currentState = blend.targetState,
|
||||
stateTime = restoredTime,
|
||||
totalTime = updatedState.totalTime,
|
||||
lastTickNanos = nowNanos,
|
||||
transitionState = TransitionState.None,
|
||||
progressMemory = updatedState.progressMemory + (updatedState.currentState.id to currentSnapshot),
|
||||
executedEvents = if (blend.targetState.rememberProgress) updatedState.executedEvents else emptySet()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
executeEvents()
|
||||
executeGuiActions()
|
||||
return calculateTransform()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
state = ExecutionState.Stopped
|
||||
customVariables.clear()
|
||||
|
||||
// Clear MoLang runtime to prevent memory leaks
|
||||
runtime.environment.variable.map.clear()
|
||||
runtime.environment.query.functions.clear()
|
||||
}
|
||||
|
||||
fun setVariable(name: String, value: Double) {
|
||||
customVariables[name] = value
|
||||
}
|
||||
|
||||
/** Triggers an instant transition on the next update (no client-side interpolation). */
|
||||
fun triggerInstantTransition() {
|
||||
instantTransitionOccurred = true
|
||||
}
|
||||
|
||||
fun isActive(): Boolean = state is ExecutionState.Playing
|
||||
|
||||
fun getCurrentStateId(): String? = (state as? ExecutionState.Playing)?.currentState?.id
|
||||
|
||||
fun getCurrentRenderPlayerBody(): Boolean = (state as? ExecutionState.Playing)?.currentState?.renderPlayerBody ?: true
|
||||
|
||||
fun getCurrentHiddenHudElements(): Set<HudElement> = (state as? ExecutionState.Playing)?.currentState?.hiddenHudElements ?: emptySet()
|
||||
|
||||
fun consumeInstantTransitionFlag(): Boolean {
|
||||
val result = instantTransitionOccurred
|
||||
instantTransitionOccurred = false
|
||||
return result
|
||||
}
|
||||
|
||||
private fun checkTransitions(
|
||||
current: ExecutionState.Playing,
|
||||
newStateTime: Float,
|
||||
newTotalTime: Float
|
||||
): TransitionCheckResult {
|
||||
if (current.transitionState !is TransitionState.None) {
|
||||
return TransitionCheckResult.NoTransition
|
||||
}
|
||||
|
||||
val context = createMoLangContext(current.currentState, newStateTime, newTotalTime)
|
||||
|
||||
for (transition in timeline.getTransitionsFrom(current.currentState.id)) {
|
||||
try {
|
||||
val shouldTransition = runtime.resolveBoolean(transition.condition, context)
|
||||
if (shouldTransition) {
|
||||
return TransitionCheckResult.TransitionNow(
|
||||
targetStateId = transition.targetStateId,
|
||||
type = transition.type,
|
||||
blendDuration = transition.blendDuration
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Error evaluating transition condition: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
return TransitionCheckResult.NoTransition
|
||||
}
|
||||
|
||||
private fun executeEvents() {
|
||||
val current = state as? ExecutionState.Playing ?: return
|
||||
val previousState = previousExecutionState ?: return
|
||||
|
||||
val context = createMoLangContext(current.currentState, current.stateTime, current.totalTime)
|
||||
val previousTime = previousState.stateTime
|
||||
val currentTime = current.stateTime
|
||||
|
||||
current.currentState.tracks.filterIsInstance<EventTrack>().forEach { track ->
|
||||
track.events.forEach { timedEvent ->
|
||||
val eventKey = EventKey(current.currentState.id, timedEvent.eventId, timedEvent.time.toInt())
|
||||
|
||||
if (eventKey in current.executedEvents) return@forEach
|
||||
|
||||
val crossedEvent = if (previousTime <= currentTime) {
|
||||
timedEvent.time in previousTime..currentTime
|
||||
} else {
|
||||
timedEvent.time <= currentTime
|
||||
}
|
||||
|
||||
if (crossedEvent) {
|
||||
try {
|
||||
runtime.resolve(timedEvent.expression, context)
|
||||
|
||||
state = current.copy(
|
||||
executedEvents = current.executedEvents + eventKey
|
||||
)
|
||||
|
||||
if (timedEvent.eventId != null) {
|
||||
Cobblemon.LOGGER.debug("[TimelineEngine] Executed event '${timedEvent.eventId}' at time ${timedEvent.time}s")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[TimelineEngine] Failed to execute event: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks GUI tracks for actions whose time has been crossed and queues them.
|
||||
* Follows the same pattern as [executeEvents].
|
||||
*/
|
||||
private fun executeGuiActions() {
|
||||
val current = state as? ExecutionState.Playing ?: return
|
||||
val previousState = previousExecutionState ?: return
|
||||
|
||||
val previousTime = previousState.stateTime
|
||||
val currentTime = current.stateTime
|
||||
|
||||
current.currentState.tracks.filterIsInstance<GuiTrack>().forEach { track ->
|
||||
track.actions.forEach { guiAction ->
|
||||
val actionKey = EventKey(current.currentState.id, "gui_${guiAction.guiId}_${guiAction.action}", guiAction.time.toInt())
|
||||
|
||||
if (actionKey in current.executedEvents) return@forEach
|
||||
|
||||
val crossedAction = if (previousTime <= currentTime) {
|
||||
guiAction.time in previousTime..currentTime
|
||||
} else {
|
||||
guiAction.time <= currentTime
|
||||
}
|
||||
|
||||
if (crossedAction) {
|
||||
pendingGuiActions.add(FiredGuiAction(
|
||||
action = guiAction.action,
|
||||
guiId = guiAction.guiId,
|
||||
args = guiAction.args,
|
||||
argsExpressions = guiAction.argsExpressions
|
||||
))
|
||||
|
||||
state = current.copy(
|
||||
executedEvents = current.executedEvents + actionKey
|
||||
)
|
||||
|
||||
Cobblemon.LOGGER.debug("[TimelineEngine] Fired GUI action ${guiAction.action} for '${guiAction.guiId}' at time ${guiAction.time}s")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes and returns all pending GUI actions since the last tick.
|
||||
* Called by the CameraExecutor after tick().
|
||||
*/
|
||||
fun consumeGuiActions(): List<FiredGuiAction> {
|
||||
if (pendingGuiActions.isEmpty()) return emptyList()
|
||||
val actions = pendingGuiActions.toList()
|
||||
pendingGuiActions.clear()
|
||||
return actions
|
||||
}
|
||||
|
||||
private fun calculateTransform(): CameraTransform {
|
||||
val current = state as? ExecutionState.Playing ?: return CameraTransform(origin, null, null)
|
||||
|
||||
return when (val transition = current.transitionState) {
|
||||
is TransitionState.None -> {
|
||||
evaluateState(current.currentState, current.stateTime)
|
||||
}
|
||||
is TransitionState.Blending -> {
|
||||
val targetTransform = evaluateState(transition.targetState, 0f)
|
||||
val progress = if (transition.duration > 0f) {
|
||||
((current.totalTime - transition.startTime) / transition.duration).coerceIn(0f, 1f)
|
||||
} else 1f
|
||||
|
||||
val easedProgress = EasingRegistry.easeOutQuad(progress)
|
||||
|
||||
blendTransforms(transition.startTransform, targetTransform, easedProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluateState(state: State, time: Float): CameraTransform {
|
||||
val context = createMoLangContext(state, time, (this.state as? ExecutionState.Playing)?.totalTime ?: 0f)
|
||||
|
||||
var position: Vec3? = null
|
||||
var focus: CameraFocus? = null
|
||||
var rotation: CameraRotation? = null
|
||||
|
||||
for (track in state.tracks) {
|
||||
val outputs = track.evaluate(time, context, runtime, origin)
|
||||
|
||||
for (output in outputs) {
|
||||
when (output) {
|
||||
is TrackOutput.Position -> {
|
||||
if (position == null) position = output.position
|
||||
}
|
||||
is TrackOutput.Focus -> {
|
||||
if (focus == null) focus = output.target
|
||||
}
|
||||
is TrackOutput.Rotation -> {
|
||||
if (rotation == null) rotation = output.rotation
|
||||
}
|
||||
is TrackOutput.Event -> {
|
||||
// Events handled separately
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CameraTransform(
|
||||
position = position ?: origin,
|
||||
focus = focus,
|
||||
rotation = rotation
|
||||
)
|
||||
}
|
||||
|
||||
private fun blendTransforms(
|
||||
from: CameraTransform,
|
||||
to: CameraTransform,
|
||||
progress: Float
|
||||
): CameraTransform {
|
||||
val blendedPosition = CameraInterpolationUtils.lerpPosition(from.position, to.position, progress)
|
||||
|
||||
val blendedRotation = if (from.rotation != null && to.rotation != null) {
|
||||
CameraInterpolationUtils.slerpRotation(from.rotation, to.rotation, progress)
|
||||
} else {
|
||||
to.rotation ?: from.rotation
|
||||
}
|
||||
|
||||
return CameraTransform(
|
||||
position = blendedPosition,
|
||||
focus = to.focus ?: from.focus,
|
||||
rotation = blendedRotation
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMoLangContext(
|
||||
state: State,
|
||||
stateTime: Float,
|
||||
totalTime: Float
|
||||
): Map<String, MoValue> {
|
||||
timeOnCurrentPathValue.value = stateTime.toDouble()
|
||||
totalTimeValue.value = totalTime.toDouble()
|
||||
|
||||
runtime.environment.variable.apply {
|
||||
map["time_on_current_path"] = timeOnCurrentPathValue
|
||||
map["total_time"] = totalTimeValue
|
||||
map["runner_id"] = runnerIdValue
|
||||
|
||||
customVariables.forEach { (name, value) ->
|
||||
map[name] = DoubleValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
return emptyMap() // Context passed via runtime.environment
|
||||
}
|
||||
|
||||
private fun normalizeProgress(time: Float, state: State): Float {
|
||||
val duration = state.getEffectiveDuration()
|
||||
return if (duration > 0f) {
|
||||
(time / duration).coerceIn(0f, 1f)
|
||||
} else 0f
|
||||
}
|
||||
}
|
||||
|
||||
data class CameraTransform(
|
||||
val position: Vec3,
|
||||
val focus: CameraFocus?,
|
||||
val rotation: CameraRotation?
|
||||
)
|
||||
|
||||
private sealed class ExecutionState {
|
||||
object Stopped : ExecutionState()
|
||||
|
||||
data class Playing(
|
||||
val currentState: State,
|
||||
val stateTime: Float,
|
||||
val totalTime: Float,
|
||||
val lastTickNanos: Long,
|
||||
val transitionState: TransitionState,
|
||||
val progressMemory: Map<String, StateSnapshot>,
|
||||
val executedEvents: Set<EventKey>,
|
||||
val lastEvaluatedFocus: CameraFocus? = null
|
||||
) : ExecutionState()
|
||||
}
|
||||
|
||||
private sealed class TransitionState {
|
||||
object None : TransitionState()
|
||||
|
||||
data class Blending(
|
||||
val targetState: State,
|
||||
val startTime: Float,
|
||||
val duration: Float,
|
||||
val startTransform: CameraTransform
|
||||
) : TransitionState()
|
||||
}
|
||||
|
||||
private sealed class TransitionCheckResult {
|
||||
object NoTransition : TransitionCheckResult()
|
||||
|
||||
data class TransitionNow(
|
||||
val targetStateId: String,
|
||||
val type: TransitionType,
|
||||
val blendDuration: Float
|
||||
) : TransitionCheckResult()
|
||||
}
|
||||
|
||||
private data class EventKey(
|
||||
val stateId: String,
|
||||
val eventId: String?,
|
||||
val timeMillis: Int
|
||||
)
|
||||
|
||||
private data class StateSnapshot(
|
||||
val normalizedProgress: Float,
|
||||
val lastFocus: CameraFocus?
|
||||
)
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.integration.server
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.model.CameraFocus
|
||||
import com.cobblemon.mod.common.api.camera.track.FiredGuiAction
|
||||
import com.cobblemon.mod.common.api.camera.track.GuiActionType
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Executes camera timeline and manages focus resolution.
|
||||
*
|
||||
* Coordinates between CameraInstance, viewer/chunk managers, and network sync.
|
||||
*/
|
||||
class CameraExecutor(
|
||||
private val instance: CameraInstance,
|
||||
private val runtime: MoLangRuntime,
|
||||
private val context: Map<String, MoValue>,
|
||||
private val guiActionHandler: ((FiredGuiAction) -> Unit)? = null
|
||||
) {
|
||||
companion object {
|
||||
private const val CHUNK_UPDATE_DISTANCE_SQ = (16 * 16).toDouble()
|
||||
}
|
||||
|
||||
private var lastCenterChunkUpdate: Vec3? = null
|
||||
|
||||
fun tick(
|
||||
level: ServerLevel,
|
||||
networkSync: CameraNetworkSync,
|
||||
onChunkUpdate: (ChunkPos) -> Unit
|
||||
): Boolean {
|
||||
if (!instance.isActive()) return false
|
||||
|
||||
val transform = instance.tick() ?: run {
|
||||
if (!instance.isActive()) instance.stop()
|
||||
return false
|
||||
}
|
||||
|
||||
val shouldUpdateCenter = lastCenterChunkUpdate?.let { last ->
|
||||
transform.position.distanceToSqr(last) > CHUNK_UPDATE_DISTANCE_SQ
|
||||
} ?: true
|
||||
|
||||
if (shouldUpdateCenter) {
|
||||
val chunkPos = chunkPosFrom(transform.position)
|
||||
onChunkUpdate(chunkPos)
|
||||
lastCenterChunkUpdate = transform.position
|
||||
}
|
||||
|
||||
val instantTransition = instance.consumeInstantTransitionFlag()
|
||||
|
||||
networkSync.sendTransform(transform, shouldUpdateCenter)
|
||||
|
||||
when (val focus = transform.focus) {
|
||||
null -> networkSync.sendFocusNone()
|
||||
is CameraFocus.Bone -> networkSync.sendFocusBone(focus, context, runtime)
|
||||
else -> {
|
||||
val resolvedFocus = resolveFocusPosition(focus)
|
||||
val finalFocusPos = instance.focusOverride ?: resolvedFocus
|
||||
networkSync.sendFocusPoint(finalFocusPos)
|
||||
}
|
||||
}
|
||||
|
||||
networkSync.sendState(
|
||||
instantTransition = instantTransition,
|
||||
renderPlayerBody = instance.getCurrentRenderPlayerBody(),
|
||||
hiddenHudElements = instance.getCurrentHiddenHudElements()
|
||||
)
|
||||
|
||||
if (guiActionHandler != null) {
|
||||
val guiActions = instance.consumeGuiActions()
|
||||
for (action in guiActions) {
|
||||
guiActionHandler.invoke(action)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun resolveFocusPosition(focus: CameraFocus): Vec3? {
|
||||
return when (focus) {
|
||||
is CameraFocus.Entity -> {
|
||||
val entity = CameraFocus.resolveEntity(focus.selector, context, runtime)
|
||||
if (entity != null) {
|
||||
val position = entity.position()
|
||||
instance.updateLastValidFocusPosition(position)
|
||||
position
|
||||
} else {
|
||||
val cached = instance.lastValidFocusPosition
|
||||
if (cached != null) {
|
||||
Cobblemon.LOGGER.debug("[CameraExecutor] Entity focus invalid, using last valid position")
|
||||
cached
|
||||
} else {
|
||||
Cobblemon.LOGGER.warn("[CameraExecutor] Entity focus invalid and no cached position, using origin")
|
||||
instance.origin
|
||||
}
|
||||
}
|
||||
}
|
||||
is CameraFocus.Point -> {
|
||||
val position = focus.position.toVec3(context, runtime, instance.origin, false)
|
||||
instance.updateLastValidFocusPosition(position)
|
||||
position
|
||||
}
|
||||
is CameraFocus.Bone -> {
|
||||
// Bone focus is resolved client-side, so return entity position as fallback
|
||||
CameraFocus.resolveEntity(focus.selector, context, runtime)?.position()
|
||||
?: instance.lastValidFocusPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun chunkPosFrom(position: Vec3): ChunkPos {
|
||||
val chunkX = SectionPos.blockToSectionCoord(Mth.floor(position.x))
|
||||
val chunkZ = SectionPos.blockToSectionCoord(Mth.floor(position.z))
|
||||
return ChunkPos(chunkX, chunkZ)
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.integration.server
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.camera.engine.TimelineEngine
|
||||
import com.cobblemon.mod.common.api.camera.model.State
|
||||
import com.cobblemon.mod.common.api.camera.model.Timeline
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Server-side camera instance - pure state and identity.
|
||||
*
|
||||
* Represents a single camera with its configuration and execution engine.
|
||||
* Does NOT handle networking, chunk loading, or viewer management.
|
||||
*
|
||||
* @param id Unique identifier for this camera instance
|
||||
* @param origin The world position origin for relative coordinates
|
||||
* @param initialContext Initial context values (parameter values) to inject into MoLang.
|
||||
* These are validated against the timeline's declared parameters when starting.
|
||||
* @param queryFunctions Custom MoLang query functions to register
|
||||
*/
|
||||
class CameraInstance(
|
||||
val id: UUID = UUID.randomUUID(),
|
||||
val origin: Vec3,
|
||||
private val initialContext: Map<String, MoValue> = emptyMap(),
|
||||
queryFunctions: Map<String, java.util.function.Function<MoParams, Any>> = emptyMap()
|
||||
) {
|
||||
private val runtime: MoLangRuntime = MoLangRuntime().setup().apply {
|
||||
environment.query.functions.putAll(queryFunctions)
|
||||
}
|
||||
|
||||
private var engine: TimelineEngine = TimelineEngine(id, createStubTimeline(), origin, runtime, initialContext)
|
||||
|
||||
@Volatile
|
||||
var focusOverride: Vec3? = null
|
||||
|
||||
var lastValidFocusPosition: Vec3? = null
|
||||
private set
|
||||
|
||||
fun startTimeline(timeline: Timeline): Boolean {
|
||||
engine = TimelineEngine(id, timeline, origin, runtime, initialContext)
|
||||
return engine.start()
|
||||
}
|
||||
|
||||
fun tick() = engine.tick()
|
||||
|
||||
fun stop() = engine.stop()
|
||||
|
||||
fun isActive() = engine.isActive()
|
||||
|
||||
fun getCurrentRenderPlayerBody() = engine.getCurrentRenderPlayerBody()
|
||||
|
||||
fun getCurrentHiddenHudElements() = engine.getCurrentHiddenHudElements()
|
||||
|
||||
fun setVariable(name: String, value: Double) = engine.setVariable(name, value)
|
||||
|
||||
fun triggerInstantTransition() = engine.triggerInstantTransition()
|
||||
|
||||
fun addQueryFunctions(functions: Map<String, java.util.function.Function<MoParams, Any>>) {
|
||||
runtime.environment.query.functions.putAll(functions)
|
||||
}
|
||||
|
||||
fun consumeInstantTransitionFlag() = engine.consumeInstantTransitionFlag()
|
||||
|
||||
fun consumeGuiActions() = engine.consumeGuiActions()
|
||||
|
||||
fun updateLastValidFocusPosition(position: Vec3) {
|
||||
lastValidFocusPosition = position
|
||||
}
|
||||
|
||||
private fun createStubTimeline(): Timeline {
|
||||
return Timeline(
|
||||
id = "stub",
|
||||
startStateId = "stub",
|
||||
states = mapOf(
|
||||
"stub" to State(
|
||||
id = "stub",
|
||||
tracks = emptyList()
|
||||
)
|
||||
),
|
||||
transitions = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.integration.server
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.camera.engine.CameraTransform
|
||||
import com.cobblemon.mod.common.api.camera.model.CameraFocus
|
||||
import com.cobblemon.mod.common.api.camera.model.HudElement
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraFocusPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraStartPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CameraTransformPacket
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Handles camera packet transmission to clients.
|
||||
*
|
||||
* Manages synchronization of camera state with connected viewers.
|
||||
* Uses modular packets with change tracking to minimize network traffic.
|
||||
*/
|
||||
class CameraNetworkSync(
|
||||
private val cameraId: UUID,
|
||||
private val viewers: Set<UUID>
|
||||
) {
|
||||
private var lastFocusPosition: Vec3? = null
|
||||
private var lastRenderPlayerBody: Boolean = true
|
||||
private var lastHiddenHudElements: Set<HudElement> = emptySet()
|
||||
private var lastInstantTransition: Boolean = false
|
||||
|
||||
fun sendStart(startPosition: Vec3) {
|
||||
getPlayers().forEach { player ->
|
||||
player.sendPacket(CameraStartPacket(cameraId, startPosition))
|
||||
}
|
||||
|
||||
sendFocusNone(force = true)
|
||||
sendState(
|
||||
instantTransition = false,
|
||||
renderPlayerBody = true,
|
||||
hiddenHudElements = emptySet(),
|
||||
force = true
|
||||
)
|
||||
}
|
||||
|
||||
fun sendTransform(
|
||||
transform: CameraTransform,
|
||||
updateCenterChunk: Boolean
|
||||
) {
|
||||
val packet = CameraTransformPacket(
|
||||
cameraId = cameraId,
|
||||
position = transform.position,
|
||||
rotation = transform.rotation,
|
||||
updateCenterChunk = updateCenterChunk
|
||||
)
|
||||
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
}
|
||||
|
||||
fun sendFocusNone(force: Boolean = false) {
|
||||
if (!force && lastFocusPosition == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val packet = CameraFocusPacket(
|
||||
cameraId = cameraId,
|
||||
focus = CameraFocusPacket.FocusData.None
|
||||
)
|
||||
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
lastFocusPosition = null
|
||||
}
|
||||
|
||||
fun sendFocusPoint(focusPosition: Vec3?, force: Boolean = false) {
|
||||
if (!force && focusPosition == lastFocusPosition) {
|
||||
return
|
||||
}
|
||||
|
||||
val focusData = when (focusPosition) {
|
||||
null -> CameraFocusPacket.FocusData.None
|
||||
else -> CameraFocusPacket.FocusData.Point(focusPosition)
|
||||
}
|
||||
|
||||
val packet = CameraFocusPacket(
|
||||
cameraId = cameraId,
|
||||
focus = focusData
|
||||
)
|
||||
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
lastFocusPosition = focusPosition
|
||||
}
|
||||
|
||||
fun sendFocusBone(
|
||||
boneFocus: CameraFocus.Bone,
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
force: Boolean = false
|
||||
) {
|
||||
val entity = CameraFocus.resolveEntity(boneFocus.selector, context, runtime)
|
||||
|
||||
if (entity == null) {
|
||||
Cobblemon.LOGGER.debug("[CameraNetworkSync] Bone focus entity invalid")
|
||||
sendFocusNone(force)
|
||||
return
|
||||
}
|
||||
|
||||
val offset = boneFocus.offset.toVec3(context, runtime)
|
||||
|
||||
Cobblemon.LOGGER.debug("[CameraNetworkSync] Sending bone focus: entityId=${entity.id}, bone=${boneFocus.boneName}, offset=$offset, smooth=${boneFocus.smooth}")
|
||||
|
||||
val focusData = CameraFocusPacket.FocusData.Bone(
|
||||
entityId = entity.id,
|
||||
boneName = boneFocus.boneName,
|
||||
offset = offset,
|
||||
smooth = boneFocus.smooth
|
||||
)
|
||||
|
||||
val packet = CameraFocusPacket(
|
||||
cameraId = cameraId,
|
||||
focus = focusData
|
||||
)
|
||||
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
|
||||
// Invalidate lastFocusPosition when switching to bone focus.
|
||||
// This ensures that when transitioning back to a point focus,
|
||||
// the packet will always be sent (not incorrectly deduplicated).
|
||||
lastFocusPosition = null
|
||||
}
|
||||
|
||||
fun sendState(
|
||||
instantTransition: Boolean,
|
||||
renderPlayerBody: Boolean,
|
||||
hiddenHudElements: Set<HudElement>,
|
||||
force: Boolean = false
|
||||
) {
|
||||
if (!force &&
|
||||
instantTransition == lastInstantTransition &&
|
||||
renderPlayerBody == lastRenderPlayerBody &&
|
||||
hiddenHudElements == lastHiddenHudElements) {
|
||||
return
|
||||
}
|
||||
|
||||
val packet = CameraStatePacket(
|
||||
cameraId = cameraId,
|
||||
instantTransition = instantTransition,
|
||||
renderPlayerBody = renderPlayerBody,
|
||||
hiddenHudElements = hiddenHudElements
|
||||
)
|
||||
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
lastInstantTransition = instantTransition
|
||||
lastRenderPlayerBody = renderPlayerBody
|
||||
lastHiddenHudElements = hiddenHudElements
|
||||
}
|
||||
|
||||
fun sendEnd() {
|
||||
val packet = CameraEndPacket(cameraId)
|
||||
getPlayers().forEach { it.sendPacket(packet) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Send end packet to a specific player.
|
||||
* Used when removing a single viewer without stopping the entire camera.
|
||||
*/
|
||||
fun sendEndToPlayer(player: ServerPlayer) {
|
||||
player.sendPacket(CameraEndPacket(cameraId))
|
||||
}
|
||||
|
||||
private fun getPlayers(): List<ServerPlayer> {
|
||||
val server = Cobblemon.implementation.server() ?: return emptyList()
|
||||
return viewers.mapNotNull { server.playerList.getPlayer(it) }
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.integration.server
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.client.render.camera.compositor.CompositorLayout
|
||||
import com.cobblemon.mod.common.net.messages.client.camera.CompositorPacket
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
class CompositorSession(
|
||||
val compositorId: UUID = UUID.randomUUID(),
|
||||
private val viewers: () -> Collection<ServerPlayer>,
|
||||
val fullscreen: Boolean = true,
|
||||
val width: Int = 1920,
|
||||
val height: Int = 1080
|
||||
) {
|
||||
fun sendCreate(layout: CompositorLayout) {
|
||||
val packet = CompositorPacket.create(compositorId, layout, fullscreen, width, height)
|
||||
viewers().forEach { it.sendPacket(packet) }
|
||||
}
|
||||
|
||||
fun sendLayoutUpdate(layout: CompositorLayout) {
|
||||
val packet = CompositorPacket.updateLayout(compositorId, layout)
|
||||
viewers().forEach { it.sendPacket(packet) }
|
||||
}
|
||||
|
||||
fun sendDestroy() {
|
||||
val packet = CompositorPacket.destroy(compositorId)
|
||||
viewers().forEach { it.sendPacket(packet) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.model
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Represents where the camera should look.
|
||||
*/
|
||||
sealed class CameraFocus {
|
||||
abstract fun resolve(
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
fallback: Vec3?
|
||||
): Vec3?
|
||||
|
||||
data class Point(val position: PositionExpression) : CameraFocus() {
|
||||
override fun resolve(
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
fallback: Vec3?
|
||||
): Vec3 {
|
||||
return position.toVec3(context, runtime)
|
||||
}
|
||||
}
|
||||
|
||||
data class Entity(val selector: ExpressionLike) : CameraFocus() {
|
||||
override fun resolve(
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
fallback: Vec3?
|
||||
): Vec3? {
|
||||
return resolveEntity(selector, context, runtime)?.position() ?: fallback
|
||||
}
|
||||
}
|
||||
|
||||
data class Bone(
|
||||
val selector: ExpressionLike,
|
||||
val boneName: String,
|
||||
val offset: PositionExpression = PositionExpression(
|
||||
CoordinateValue.Static(0.0),
|
||||
CoordinateValue.Static(0.0),
|
||||
CoordinateValue.Static(0.0)
|
||||
),
|
||||
val smooth: Boolean = true
|
||||
) : CameraFocus() {
|
||||
override fun resolve(
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
fallback: Vec3?
|
||||
): Vec3? {
|
||||
// Server-side: just resolve the entity position as fallback
|
||||
// Client-side bone resolution happens in BoneFocusResolver
|
||||
return resolveEntity(selector, context, runtime)?.position() ?: fallback
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Resolves a MoLang entity selector to a live Entity, or null if the selector
|
||||
* doesn't resolve, the entity is removed, or the entity is dead.
|
||||
*/
|
||||
fun resolveEntity(
|
||||
selector: ExpressionLike,
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime
|
||||
): net.minecraft.world.entity.Entity? {
|
||||
return try {
|
||||
val result = runtime.resolve(selector, context)
|
||||
if (result is ObjectValue<*>) {
|
||||
val entity = result.obj as? net.minecraft.world.entity.Entity
|
||||
if (entity != null && !entity.isRemoved && entity.isAlive) entity else null
|
||||
} else null
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[CameraFocus] Failed to resolve entity from selector", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun fromJson(obj: JsonObject): CameraFocus {
|
||||
val type = obj.get("type")?.asString
|
||||
?: throw IllegalArgumentException("CameraFocus missing 'type' field")
|
||||
|
||||
return when (type) {
|
||||
"entity" -> {
|
||||
val selector = obj.get("selector")?.asString
|
||||
?: throw IllegalArgumentException("Entity focus missing 'selector' field")
|
||||
Entity(selector.asExpressionLike())
|
||||
}
|
||||
"position", "point" -> {
|
||||
val posElement = obj.get("position")
|
||||
?: obj // If no nested position, use the object itself
|
||||
Point(PositionExpression.fromJson(posElement))
|
||||
}
|
||||
"bone" -> {
|
||||
val selector = obj.get("selector")?.asString
|
||||
?: throw IllegalArgumentException("Bone focus missing 'selector' field")
|
||||
val boneName = obj.get("bone")?.asString
|
||||
?: obj.get("boneName")?.asString
|
||||
?: throw IllegalArgumentException("Bone focus missing 'bone' field")
|
||||
val offset = if (obj.has("offset")) {
|
||||
PositionExpression.fromJson(obj.get("offset"))
|
||||
} else {
|
||||
PositionExpression(
|
||||
CoordinateValue.Static(0.0),
|
||||
CoordinateValue.Static(0.0),
|
||||
CoordinateValue.Static(0.0)
|
||||
)
|
||||
}
|
||||
val smooth = obj.get("smooth")?.asBoolean ?: true
|
||||
|
||||
Bone(selector.asExpressionLike(), boneName, offset, smooth)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unknown CameraFocus type: $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Position defined by three coordinates (x, y, z), each either static or dynamic.
|
||||
*/
|
||||
data class PositionExpression(
|
||||
val x: CoordinateValue,
|
||||
val y: CoordinateValue,
|
||||
val z: CoordinateValue
|
||||
) {
|
||||
fun toVec3(
|
||||
context: Map<String, MoValue> = emptyMap(),
|
||||
runtime: MoLangRuntime,
|
||||
origin: Vec3 = Vec3.ZERO,
|
||||
isRelative: Boolean = false
|
||||
): Vec3 {
|
||||
val base = Vec3(
|
||||
x.evaluate(context, runtime),
|
||||
y.evaluate(context, runtime),
|
||||
z.evaluate(context, runtime)
|
||||
)
|
||||
|
||||
return if (isRelative) base.add(origin) else base
|
||||
}
|
||||
|
||||
fun isFinite(context: Map<String, MoValue>, runtime: MoLangRuntime): Boolean {
|
||||
val vec = toVec3(context, runtime)
|
||||
return vec.x.isFinite() && vec.y.isFinite() && vec.z.isFinite()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromJson(element: JsonElement): PositionExpression {
|
||||
return when {
|
||||
element.isJsonObject -> {
|
||||
val obj = element.asJsonObject
|
||||
PositionExpression(
|
||||
x = deserializeCoordinate(obj.get("x")),
|
||||
y = deserializeCoordinate(obj.get("y")),
|
||||
z = deserializeCoordinate(obj.get("z"))
|
||||
)
|
||||
}
|
||||
element.isJsonArray -> {
|
||||
val arr = element.asJsonArray
|
||||
if (arr.size() != 3) {
|
||||
throw IllegalArgumentException("Position array must have exactly 3 elements [x, y, z], got ${arr.size()}")
|
||||
}
|
||||
PositionExpression(
|
||||
x = deserializeCoordinate(arr[0]),
|
||||
y = deserializeCoordinate(arr[1]),
|
||||
z = deserializeCoordinate(arr[2])
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Position must be either an object {x, y, z} or array [x, y, z]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeCoordinate(element: JsonElement?): CoordinateValue {
|
||||
if (element == null) {
|
||||
return CoordinateValue.Static(0.0)
|
||||
}
|
||||
|
||||
return when {
|
||||
element.isJsonPrimitive && element.asJsonPrimitive.isNumber -> {
|
||||
CoordinateValue.Static(element.asDouble)
|
||||
}
|
||||
element.isJsonPrimitive && element.asJsonPrimitive.isString -> {
|
||||
// String is treated as expression
|
||||
CoordinateValue.Dynamic(element.asString.asExpressionLike())
|
||||
}
|
||||
element.isJsonObject -> {
|
||||
val obj = element.asJsonObject
|
||||
val type = obj.get("type")?.asString
|
||||
val expression = obj.get("expression")?.asString
|
||||
|
||||
when (type) {
|
||||
"expression" -> {
|
||||
if (expression == null) {
|
||||
throw IllegalArgumentException("Expression coordinate missing 'expression' field")
|
||||
}
|
||||
CoordinateValue.Dynamic(expression.asExpressionLike())
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unknown coordinate type: $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Invalid coordinate value: $element")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class CoordinateValue {
|
||||
data class Static(val value: Double) : CoordinateValue()
|
||||
data class Dynamic(val expression: ExpressionLike) : CoordinateValue()
|
||||
|
||||
fun evaluate(
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime
|
||||
): Double {
|
||||
return when (this) {
|
||||
is Static -> value
|
||||
is Dynamic -> {
|
||||
try {
|
||||
runtime.resolve(expression, context).asDouble()
|
||||
} catch (e: Exception) {
|
||||
// Log error but don't crash - coordinate expressions shouldn't break cameras
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.model
|
||||
|
||||
enum class HudElement(val id: String) {
|
||||
CROSSHAIR("crosshair"),
|
||||
HOTBAR("hotbar"),
|
||||
EXPERIENCE_BAR("experience_bar"),
|
||||
PLAYER_HEALTH("player_health"),
|
||||
ARMOR("armor"),
|
||||
FOOD("food"),
|
||||
AIR("air"),
|
||||
EFFECTS("effects"),
|
||||
BOSS_BAR("boss_bar"),
|
||||
SCOREBOARD("scoreboard"),
|
||||
CHAT("chat"),
|
||||
PLAYER_LIST("player_list"),
|
||||
SELECTED_ITEM_NAME("selected_item_name"),
|
||||
COBBLEMON_OVERLAY("cobblemon_overlay");
|
||||
|
||||
companion object {
|
||||
val ALL: Set<HudElement> = entries.toSet()
|
||||
|
||||
private val BY_ID = entries.associateBy { it.id }
|
||||
|
||||
fun fromId(id: String): HudElement? = BY_ID[id]
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.model
|
||||
|
||||
/**
|
||||
* Defines a parameter that can be passed to a camera timeline.
|
||||
*
|
||||
* Parameters allow timelines to be reusable with different values.
|
||||
* For example, an arena camera can accept `arena_center` as a Vec3 parameter
|
||||
* to position the camera relative to any arena location.
|
||||
*
|
||||
* @property type The type of the parameter (NUMBER or VEC3)
|
||||
* @property description Optional description for documentation
|
||||
* @property default Optional default value. If null, the parameter is required.
|
||||
*/
|
||||
data class ParameterDefinition(
|
||||
val type: ParameterType,
|
||||
val description: String? = null,
|
||||
val default: ParameterDefault? = null
|
||||
) {
|
||||
/**
|
||||
* Whether this parameter must be provided when starting the timeline.
|
||||
* Parameters with defaults are optional.
|
||||
*/
|
||||
val isRequired: Boolean get() = default == null
|
||||
|
||||
/**
|
||||
* Gets the variable names this parameter will inject into the MoLang runtime.
|
||||
* - NUMBER type: returns just the parameter name
|
||||
* - VEC3 type: returns name_x, name_y, name_z
|
||||
*/
|
||||
fun getVariableNames(paramName: String): List<String> {
|
||||
return when (type) {
|
||||
ParameterType.NUMBER -> listOf(paramName)
|
||||
ParameterType.VEC3 -> listOf("${paramName}_x", "${paramName}_y", "${paramName}_z")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of parameters supported by camera timelines.
|
||||
*/
|
||||
enum class ParameterType {
|
||||
/**
|
||||
* A single numeric value (Double).
|
||||
* Accessible in MoLang as `variable.param_name`
|
||||
*/
|
||||
NUMBER,
|
||||
|
||||
/**
|
||||
* A 3D vector value.
|
||||
* Expands to three variables in MoLang:
|
||||
* - `variable.param_name_x`
|
||||
* - `variable.param_name_y`
|
||||
* - `variable.param_name_z`
|
||||
*/
|
||||
VEC3
|
||||
}
|
||||
|
||||
/**
|
||||
* Default value for a parameter.
|
||||
*/
|
||||
sealed class ParameterDefault {
|
||||
/**
|
||||
* Default value for a NUMBER parameter.
|
||||
*/
|
||||
data class NumberDefault(val value: Double) : ParameterDefault()
|
||||
|
||||
/**
|
||||
* Default value for a VEC3 parameter.
|
||||
*/
|
||||
data class Vec3Default(val x: Double, val y: Double, val z: Double) : ParameterDefault()
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.model
|
||||
|
||||
import com.cobblemon.mod.common.api.camera.result.ValidationError
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
|
||||
/**
|
||||
* Immutable timeline definition - a state machine of camera animations with transitions.
|
||||
*
|
||||
* Timelines can declare required and optional parameters that must be provided when
|
||||
* starting the camera. Parameters are injected into the MoLang runtime as variables,
|
||||
* allowing tracks to use dynamic values like `variable.arena_center_x`.
|
||||
*
|
||||
* @property id Unique identifier for this timeline
|
||||
* @property startStateId The ID of the initial state when the timeline starts
|
||||
* @property parameters Map of parameter names to their definitions. Parameters without
|
||||
* defaults are required and must be provided when starting the camera.
|
||||
* @property states Map of state IDs to state definitions
|
||||
* @property transitions List of transitions between states
|
||||
*/
|
||||
data class Timeline(
|
||||
val id: String,
|
||||
val startStateId: String,
|
||||
val parameters: Map<String, ParameterDefinition> = emptyMap(),
|
||||
val states: Map<String, State>,
|
||||
val transitions: List<Transition>
|
||||
) {
|
||||
fun getState(stateId: String): State? = states[stateId]
|
||||
|
||||
fun getTransitionsFrom(stateId: String): List<Transition> =
|
||||
transitions.filter { it.sourceStateId == stateId }
|
||||
|
||||
/**
|
||||
* Gets the names of all required parameters (those without defaults).
|
||||
*/
|
||||
fun getRequiredParameterNames(): Set<String> =
|
||||
parameters.filter { it.value.isRequired }.keys
|
||||
|
||||
/**
|
||||
* Gets all variable names that this timeline's parameters will inject.
|
||||
* For NUMBER parameters, returns the parameter name.
|
||||
* For VEC3 parameters, returns name_x, name_y, name_z.
|
||||
*/
|
||||
fun getAllParameterVariableNames(): Set<String> =
|
||||
parameters.flatMap { (name, def) -> def.getVariableNames(name) }.toSet()
|
||||
|
||||
/**
|
||||
* Validates that all required parameters are provided in the given context.
|
||||
*
|
||||
* @param providedVariables Set of variable names provided in the context
|
||||
* @return List of missing parameter names, empty if all required params are present
|
||||
*/
|
||||
fun validateParameters(providedVariables: Set<String>): List<String> {
|
||||
val missingParams = mutableListOf<String>()
|
||||
|
||||
for ((paramName, definition) in parameters) {
|
||||
if (!definition.isRequired) continue
|
||||
|
||||
val requiredVars = definition.getVariableNames(paramName)
|
||||
val allProvided = requiredVars.all { it in providedVariables }
|
||||
|
||||
if (!allProvided) {
|
||||
missingParams.add(paramName)
|
||||
}
|
||||
}
|
||||
|
||||
return missingParams
|
||||
}
|
||||
|
||||
fun validate(): List<ValidationError> {
|
||||
val errors = mutableListOf<ValidationError>()
|
||||
|
||||
// Check start state exists
|
||||
if (startStateId !in states) {
|
||||
errors.add(ValidationError(
|
||||
path = "startStateId",
|
||||
message = "Start state '$startStateId' not found in states map"
|
||||
))
|
||||
}
|
||||
|
||||
// Check all transition states exist
|
||||
transitions.forEachIndexed { index, transition ->
|
||||
if (transition.sourceStateId !in states) {
|
||||
errors.add(ValidationError(
|
||||
path = "transitions[$index].sourceStateId",
|
||||
message = "Source state '${transition.sourceStateId}' not found"
|
||||
))
|
||||
}
|
||||
if (transition.targetStateId !in states) {
|
||||
errors.add(ValidationError(
|
||||
path = "transitions[$index].targetStateId",
|
||||
message = "Target state '${transition.targetStateId}' not found"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
states.forEach { (stateId, state) ->
|
||||
if (state.tracks.isEmpty()) {
|
||||
errors.add(ValidationError(
|
||||
path = "states[$stateId].tracks",
|
||||
message = "State has no tracks defined",
|
||||
severity = ValidationError.Severity.WARNING
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
val reachableStates = findReachableStates()
|
||||
states.keys.forEach { stateId ->
|
||||
if (stateId !in reachableStates) {
|
||||
errors.add(ValidationError(
|
||||
path = "states[$stateId]",
|
||||
message = "State is unreachable from start state '$startStateId'",
|
||||
severity = ValidationError.Severity.WARNING
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
val cycles = detectCycles()
|
||||
if (cycles.isNotEmpty()) {
|
||||
errors.add(ValidationError(
|
||||
path = "transitions",
|
||||
message = "Detected transition cycles: ${cycles.joinToString(", ") { it.joinToString(" -> ") }}",
|
||||
severity = ValidationError.Severity.WARNING
|
||||
))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
private fun findReachableStates(): Set<String> {
|
||||
val reachable = mutableSetOf<String>()
|
||||
val queue = ArrayDeque<String>()
|
||||
|
||||
if (startStateId in states) {
|
||||
queue.add(startStateId)
|
||||
reachable.add(startStateId)
|
||||
}
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
val current = queue.removeFirst()
|
||||
transitions
|
||||
.filter { it.sourceStateId == current }
|
||||
.forEach { transition ->
|
||||
if (transition.targetStateId !in reachable && transition.targetStateId in states) {
|
||||
reachable.add(transition.targetStateId)
|
||||
queue.add(transition.targetStateId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reachable
|
||||
}
|
||||
|
||||
private fun detectCycles(): List<List<String>> {
|
||||
val cycles = mutableListOf<List<String>>()
|
||||
val visited = mutableSetOf<String>()
|
||||
val recStack = mutableSetOf<String>()
|
||||
|
||||
fun dfs(node: String, path: MutableList<String>): Boolean {
|
||||
if (node !in states) return false
|
||||
|
||||
visited.add(node)
|
||||
recStack.add(node)
|
||||
path.add(node)
|
||||
|
||||
for (transition in getTransitionsFrom(node)) {
|
||||
val target = transition.targetStateId
|
||||
if (target !in visited) {
|
||||
if (dfs(target, path)) return true
|
||||
} else if (target in recStack) {
|
||||
val cycleStart = path.indexOf(target)
|
||||
if (cycleStart >= 0) {
|
||||
cycles.add(path.subList(cycleStart, path.size) + target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path.removeAt(path.size - 1)
|
||||
recStack.remove(node)
|
||||
return false
|
||||
}
|
||||
|
||||
states.keys.forEach { stateId ->
|
||||
if (stateId !in visited) {
|
||||
dfs(stateId, mutableListOf())
|
||||
}
|
||||
}
|
||||
|
||||
return cycles
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable camera state definition - a collection of tracks that play together.
|
||||
*/
|
||||
data class State(
|
||||
val id: String,
|
||||
val tracks: List<CameraTrack>,
|
||||
val duration: Float? = null,
|
||||
val isLooping: Boolean = false,
|
||||
val speed: Float = 1.0f,
|
||||
val rememberProgress: Boolean = false,
|
||||
val renderPlayerBody: Boolean = true,
|
||||
val hiddenHudElements: Set<HudElement> = emptySet()
|
||||
) {
|
||||
init {
|
||||
require(speed > 0f) { "State speed must be positive, got $speed" }
|
||||
duration?.let { d ->
|
||||
require(d >= 0f) { "State duration must be non-negative, got $d" }
|
||||
}
|
||||
}
|
||||
|
||||
fun getEffectiveDuration(): Float {
|
||||
return duration ?: tracks.maxOfOrNull { it.duration } ?: 0f
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable transition definition - defines when and how to move from one state to another.
|
||||
*/
|
||||
data class Transition(
|
||||
val sourceStateId: String,
|
||||
val targetStateId: String,
|
||||
val condition: ExpressionLike,
|
||||
val type: TransitionType = TransitionType.BLEND,
|
||||
val blendDuration: Float = 0.4f
|
||||
) {
|
||||
init {
|
||||
require(blendDuration >= 0f) { "Blend duration must be non-negative, got $blendDuration" }
|
||||
}
|
||||
}
|
||||
|
||||
enum class TransitionType {
|
||||
INSTANT,
|
||||
BLEND
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.model
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.camera.CameraRotation
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Type-safe track evaluation result.
|
||||
*/
|
||||
sealed class TrackOutput {
|
||||
data class Position(val position: Vec3) : TrackOutput()
|
||||
data class Focus(val target: CameraFocus) : TrackOutput()
|
||||
data class Rotation(val rotation: CameraRotation) : TrackOutput()
|
||||
data class Event(val expression: ExpressionLike, val eventId: String?) : TrackOutput()
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera track interface - produces outputs over time.
|
||||
*/
|
||||
interface CameraTrack {
|
||||
val trackType: String
|
||||
val duration: Float
|
||||
|
||||
fun evaluate(
|
||||
time: Float,
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
origin: Vec3
|
||||
): List<TrackOutput>
|
||||
}
|
||||
|
||||
enum class TrackCapability {
|
||||
POSITION,
|
||||
FOCUS,
|
||||
ROTATION,
|
||||
EVENTS
|
||||
}
|
||||
|
||||
fun CameraTrack.capabilities(): Set<TrackCapability> {
|
||||
val outputs = evaluate(0f, emptyMap(), MoLangRuntime(), Vec3.ZERO)
|
||||
return outputs.mapTo(mutableSetOf()) { output ->
|
||||
when (output) {
|
||||
is TrackOutput.Position -> TrackCapability.POSITION
|
||||
is TrackOutput.Focus -> TrackCapability.FOCUS
|
||||
is TrackOutput.Rotation -> TrackCapability.ROTATION
|
||||
is TrackOutput.Event -> TrackCapability.EVENTS
|
||||
}
|
||||
}
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.registry
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.CameraTrackRegistry
|
||||
import com.cobblemon.mod.common.api.camera.model.*
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.google.gson.*
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* JSON deserializer for Timeline (V2 format).
|
||||
*
|
||||
* Deserializes directly into the immutable Timeline model without legacy conversion.
|
||||
*/
|
||||
object TimelineDeserializer : JsonDeserializer<Timeline> {
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): Timeline {
|
||||
val obj = json.asJsonObject
|
||||
|
||||
val id = obj.get("timelineId")?.asString
|
||||
?: throw JsonParseException("Timeline missing 'timelineId' field")
|
||||
|
||||
val startStateId = obj.get("startStateId")?.asString
|
||||
?: throw JsonParseException("Timeline '$id' missing 'startStateId' field")
|
||||
|
||||
val statesObj = obj.get("states")?.asJsonObject
|
||||
?: throw JsonParseException("Timeline '$id' missing 'states' object")
|
||||
|
||||
val transitionsArray = obj.get("transitions")?.asJsonArray ?: JsonArray()
|
||||
|
||||
// Parse parameters (optional)
|
||||
val parametersObj = obj.get("parameters")?.asJsonObject
|
||||
val parameters = parametersObj?.entrySet()?.associate { (paramName, paramValue) ->
|
||||
try {
|
||||
paramName to deserializeParameter(paramValue.asJsonObject, paramName, id)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[Timeline] Error parsing parameter '$paramName' in timeline '$id': ${e.message}")
|
||||
throw JsonParseException("Invalid parameter '$paramName' in timeline '$id'", e)
|
||||
}
|
||||
} ?: emptyMap()
|
||||
|
||||
val states = statesObj.entrySet().associate { (key, value) ->
|
||||
try {
|
||||
key to deserializeState(value.asJsonObject, id)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[Timeline] Error parsing state '$key' in timeline '$id': ${e.message}")
|
||||
throw JsonParseException("Invalid state '$key' in timeline '$id'", e)
|
||||
}
|
||||
}
|
||||
|
||||
val transitions = transitionsArray.mapIndexed { index, element ->
|
||||
try {
|
||||
deserializeTransition(element.asJsonObject, id)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[Timeline] Error parsing transition $index in timeline '$id': ${e.message}")
|
||||
throw JsonParseException("Invalid transition $index in timeline '$id'", e)
|
||||
}
|
||||
}
|
||||
|
||||
return Timeline(
|
||||
id = id,
|
||||
startStateId = startStateId,
|
||||
parameters = parameters,
|
||||
states = states,
|
||||
transitions = transitions
|
||||
)
|
||||
}
|
||||
|
||||
private fun deserializeParameter(obj: JsonObject, paramName: String, timelineId: String): ParameterDefinition {
|
||||
val typeStr = obj.get("type")?.asString
|
||||
?: throw JsonParseException("Parameter '$paramName' missing 'type' field")
|
||||
|
||||
val type = when (typeStr.lowercase()) {
|
||||
"number" -> ParameterType.NUMBER
|
||||
"vec3" -> ParameterType.VEC3
|
||||
else -> throw JsonParseException("Parameter '$paramName' has unknown type '$typeStr'. Valid types: number, vec3")
|
||||
}
|
||||
|
||||
val description = obj.get("description")?.asString
|
||||
|
||||
val default: ParameterDefault? = when {
|
||||
!obj.has("default") -> null
|
||||
type == ParameterType.NUMBER -> {
|
||||
val defaultValue = obj.get("default")
|
||||
if (defaultValue.isJsonPrimitive && defaultValue.asJsonPrimitive.isNumber) {
|
||||
ParameterDefault.NumberDefault(defaultValue.asDouble)
|
||||
} else {
|
||||
throw JsonParseException("Parameter '$paramName' default must be a number")
|
||||
}
|
||||
}
|
||||
type == ParameterType.VEC3 -> {
|
||||
val defaultObj = obj.get("default")
|
||||
if (defaultObj.isJsonObject) {
|
||||
val vec = defaultObj.asJsonObject
|
||||
val x = vec.get("x")?.asDouble
|
||||
?: throw JsonParseException("Parameter '$paramName' vec3 default missing 'x' field")
|
||||
val y = vec.get("y")?.asDouble
|
||||
?: throw JsonParseException("Parameter '$paramName' vec3 default missing 'y' field")
|
||||
val z = vec.get("z")?.asDouble
|
||||
?: throw JsonParseException("Parameter '$paramName' vec3 default missing 'z' field")
|
||||
ParameterDefault.Vec3Default(x, y, z)
|
||||
} else {
|
||||
throw JsonParseException("Parameter '$paramName' vec3 default must be an object with x, y, z fields")
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
return ParameterDefinition(
|
||||
type = type,
|
||||
description = description,
|
||||
default = default
|
||||
)
|
||||
}
|
||||
|
||||
private fun deserializeState(obj: JsonObject, timelineId: String): State {
|
||||
val stateId = obj.get("stateId")?.asString
|
||||
?: throw JsonParseException("State missing 'stateId' field")
|
||||
|
||||
val tracksArray = obj.get("tracks")?.asJsonArray
|
||||
?: throw JsonParseException("State '$stateId' missing 'tracks' array")
|
||||
|
||||
val tracks = tracksArray.mapIndexedNotNull { index, element ->
|
||||
try {
|
||||
deserializeTrack(element.asJsonObject)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[Timeline] Error parsing track $index in state '$stateId': ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val duration = obj.get("duration")?.asFloat
|
||||
val isLooping = obj.get("isLooping")?.asBoolean ?: false
|
||||
val speed = obj.get("speed")?.asFloat ?: 1.0f
|
||||
val rememberProgress = obj.get("rememberProgress")?.asBoolean ?: false
|
||||
val renderPlayerBody = obj.get("renderPlayerBody")?.asBoolean ?: true
|
||||
|
||||
val hiddenHudElements = when (val hideHudElement = obj.get("hideHud")) {
|
||||
null -> emptySet()
|
||||
is JsonPrimitive -> {
|
||||
if (hideHudElement.isBoolean) {
|
||||
if (hideHudElement.asBoolean) HudElement.ALL else emptySet()
|
||||
} else {
|
||||
Cobblemon.LOGGER.warn("[Timeline] Invalid hideHud value in state '$stateId', expected boolean or array")
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
is JsonArray -> {
|
||||
hideHudElement.mapNotNull { element ->
|
||||
val id = element.asString
|
||||
HudElement.fromId(id) ?: run {
|
||||
Cobblemon.LOGGER.warn("[Timeline] Unknown HUD element '$id' in state '$stateId'")
|
||||
null
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
else -> {
|
||||
Cobblemon.LOGGER.warn("[Timeline] Invalid hideHud value in state '$stateId', expected boolean or array")
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
return State(
|
||||
id = stateId,
|
||||
tracks = tracks,
|
||||
duration = duration,
|
||||
isLooping = isLooping,
|
||||
speed = speed,
|
||||
rememberProgress = rememberProgress,
|
||||
renderPlayerBody = renderPlayerBody,
|
||||
hiddenHudElements = hiddenHudElements
|
||||
)
|
||||
}
|
||||
|
||||
private fun deserializeTrack(obj: JsonObject): CameraTrack {
|
||||
val trackType = obj.get("trackType")?.asString
|
||||
?: throw JsonParseException("Track missing 'trackType' field")
|
||||
|
||||
return CameraTrackRegistry.deserializeTrack(trackType, obj)
|
||||
?: throw JsonParseException("Unknown track type: $trackType")
|
||||
}
|
||||
|
||||
private fun deserializeTransition(obj: JsonObject, timelineId: String): Transition {
|
||||
val sourceStateId = obj.get("sourceStateId")?.asString
|
||||
?: throw JsonParseException("Transition missing 'sourceStateId' field")
|
||||
|
||||
val targetStateId = obj.get("targetStateId")?.asString
|
||||
?: throw JsonParseException("Transition missing 'targetStateId' field")
|
||||
|
||||
val conditionStr = obj.get("condition")?.asString
|
||||
?: throw JsonParseException("Transition from '$sourceStateId' to '$targetStateId' missing 'condition' field")
|
||||
|
||||
val blendDuration = obj.get("blendDuration")?.asFloat ?: 0.4f
|
||||
|
||||
val transitionType = obj.get("transitionType")?.asString?.let { typeStr ->
|
||||
when (typeStr.lowercase()) {
|
||||
"blend" -> TransitionType.BLEND
|
||||
"instant" -> TransitionType.INSTANT
|
||||
else -> {
|
||||
Cobblemon.LOGGER.warn("[Timeline] Unknown transition type '$typeStr' in timeline '$timelineId', using BLEND")
|
||||
TransitionType.BLEND
|
||||
}
|
||||
}
|
||||
} ?: TransitionType.BLEND
|
||||
|
||||
return Transition(
|
||||
sourceStateId = sourceStateId,
|
||||
targetStateId = targetStateId,
|
||||
condition = conditionStr.asExpressionLike(),
|
||||
type = transitionType,
|
||||
blendDuration = blendDuration
|
||||
)
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.registry
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.model.Timeline
|
||||
import com.cobblemon.mod.common.api.camera.result.ValidationError
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.InputStreamReader
|
||||
|
||||
/**
|
||||
* Registry for loading Timeline (V2 format) from JSON files.
|
||||
*
|
||||
* Loads timelines directly into the new immutable Timeline model
|
||||
* without legacy conversion.
|
||||
*/
|
||||
object TimelineRegistry : DataRegistry {
|
||||
override val id = cobblemonResource("camera_timelines")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<TimelineRegistry>()
|
||||
|
||||
private val gson = GsonBuilder()
|
||||
.registerTypeAdapter(Timeline::class.java, TimelineDeserializer)
|
||||
.create()
|
||||
|
||||
private val timelines = mutableMapOf<String, Timeline>()
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
timelines.clear()
|
||||
|
||||
val resources = manager.listResources("camera_timelines") { path ->
|
||||
path.path.endsWith(".json")
|
||||
}
|
||||
|
||||
for ((identifier, resource) in resources) {
|
||||
try {
|
||||
resource.open().use { stream ->
|
||||
val reader = InputStreamReader(stream)
|
||||
val timeline = gson.fromJson(reader, Timeline::class.java)
|
||||
|
||||
val errors = timeline.validate()
|
||||
val hasErrors = errors.any { it.severity == ValidationError.Severity.ERROR }
|
||||
|
||||
if (hasErrors) {
|
||||
Cobblemon.LOGGER.error("[TimelineRegistry] Validation failed for '$identifier':")
|
||||
errors.forEach { error ->
|
||||
val level = if (error.severity == ValidationError.Severity.ERROR) "ERROR" else "WARN"
|
||||
Cobblemon.LOGGER.error(" [$level] ${error.path}: ${error.message}")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (errors.isNotEmpty()) {
|
||||
Cobblemon.LOGGER.warn("[TimelineRegistry] Warnings for timeline '${timeline.id}':")
|
||||
errors.forEach { Cobblemon.LOGGER.warn(" ${it.path}: ${it.message}") }
|
||||
}
|
||||
|
||||
if (timelines.containsKey(timeline.id)) {
|
||||
Cobblemon.LOGGER.warn("[TimelineRegistry] Duplicate timeline ID '${timeline.id}' in $identifier, overwriting")
|
||||
}
|
||||
|
||||
timelines[timeline.id] = timeline
|
||||
Cobblemon.LOGGER.debug("[TimelineRegistry] Loaded timeline '${timeline.id}' from $identifier")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("[TimelineRegistry] Error loading timeline from $identifier", e)
|
||||
}
|
||||
}
|
||||
|
||||
Cobblemon.LOGGER.info("[TimelineRegistry] Loaded ${timelines.size} timelines")
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// Timelines are server-driven, no sync needed
|
||||
}
|
||||
|
||||
fun getTimeline(id: String): Timeline? {
|
||||
val timeline = timelines[id]
|
||||
if (timeline == null) {
|
||||
Cobblemon.LOGGER.error("[TimelineRegistry] Timeline '$id' not found")
|
||||
}
|
||||
return timeline
|
||||
}
|
||||
|
||||
fun hasTimeline(id: String): Boolean = timelines.containsKey(id)
|
||||
|
||||
fun registerTimeline(timeline: Timeline) {
|
||||
val errors = timeline.validate()
|
||||
if (errors.any { it.severity == ValidationError.Severity.ERROR }) {
|
||||
Cobblemon.LOGGER.error("[TimelineRegistry] Cannot register invalid timeline '${timeline.id}':")
|
||||
errors.forEach { Cobblemon.LOGGER.error(" ${it.path}: ${it.message}") }
|
||||
return
|
||||
}
|
||||
|
||||
if (timelines.containsKey(timeline.id)) {
|
||||
Cobblemon.LOGGER.warn("[TimelineRegistry] Timeline '${timeline.id}' already registered, overwriting")
|
||||
}
|
||||
|
||||
timelines[timeline.id] = timeline
|
||||
Cobblemon.LOGGER.debug("[TimelineRegistry] Registered timeline '${timeline.id}' programmatically")
|
||||
}
|
||||
|
||||
fun getAllTimelines(): Collection<Timeline> = timelines.values
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.result
|
||||
|
||||
/**
|
||||
* Validation error with location context.
|
||||
*/
|
||||
data class ValidationError(
|
||||
val path: String,
|
||||
val message: String,
|
||||
val severity: Severity = Severity.ERROR
|
||||
) {
|
||||
enum class Severity { WARNING, ERROR }
|
||||
|
||||
override fun toString(): String = "[$severity] $path: $message"
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.track
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.camera.CameraTrackRegistry
|
||||
import com.cobblemon.mod.common.api.tween.InterpolationMethod
|
||||
import com.cobblemon.mod.common.api.camera.model.CameraFocus
|
||||
import com.cobblemon.mod.common.api.camera.model.PositionExpression
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
object CameraTrackDeserializers {
|
||||
|
||||
/** Should be called during mod initialization. */
|
||||
fun registerBuiltinTracks() {
|
||||
CameraTrackRegistry.registerTrackType("position", ::deserializePositionTrack)
|
||||
CameraTrackRegistry.registerTrackType("focus", ::deserializeFocusTrack)
|
||||
CameraTrackRegistry.registerTrackType("event", ::deserializeEventTrack)
|
||||
CameraTrackRegistry.registerTrackType("reference", ::deserializeReferenceTrack)
|
||||
CameraTrackRegistry.registerTrackType("gui", ::deserializeGuiTrack)
|
||||
|
||||
Cobblemon.LOGGER.debug("[CameraTrackDeserializers] Registered built-in track types: position, focus, event, reference, gui")
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a position track from JSON.
|
||||
*
|
||||
* Expected JSON format:
|
||||
* ```json
|
||||
* {
|
||||
* "trackType": "position",
|
||||
* "keyframes": [
|
||||
* {
|
||||
* "time": 0.0,
|
||||
* "position": { "x": 10.0, "y": 64.0, "z": 10.0 },
|
||||
* "easing": "ease_in_out_quad"
|
||||
* }
|
||||
* ],
|
||||
* "interpolationType": "bezier",
|
||||
* "easing": "linear",
|
||||
* "isRelative": false
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private fun deserializePositionTrack(json: JsonObject): PositionTrack {
|
||||
val keyframesArray = json.getAsJsonArray("keyframes")
|
||||
?: throw IllegalArgumentException("Position track missing 'keyframes' array")
|
||||
|
||||
val keyframes = keyframesArray.map { element ->
|
||||
val kfObj = element.asJsonObject
|
||||
val time = kfObj.get("time")?.asFloat
|
||||
?: throw IllegalArgumentException("Position keyframe missing 'time' field")
|
||||
|
||||
val positionElement = kfObj.get("position")
|
||||
?: throw IllegalArgumentException("Position keyframe missing 'position' field")
|
||||
|
||||
val position = PositionExpression.fromJson(positionElement)
|
||||
val easing = kfObj.get("easing")?.asString
|
||||
|
||||
PositionKeyframe(time, position, easing)
|
||||
}
|
||||
|
||||
val interpolationTypeStr = json.get("interpolationType")?.asString ?: "linear"
|
||||
val interpolationType = when (interpolationTypeStr.lowercase()) {
|
||||
"linear" -> InterpolationMethod.LINEAR
|
||||
"bezier", "cubic_bezier" -> InterpolationMethod.BEZIER
|
||||
"hermite", "catmull_rom" -> InterpolationMethod.BEZIER // Map legacy types to available ones
|
||||
else -> {
|
||||
Cobblemon.LOGGER.warn("[CameraTrackDeserializers] Unknown interpolation type '$interpolationTypeStr', using LINEAR")
|
||||
InterpolationMethod.LINEAR
|
||||
}
|
||||
}
|
||||
|
||||
val defaultEasing = json.get("easing")?.asString ?: "linear"
|
||||
val isRelative = json.get("isRelative")?.asBoolean ?: false
|
||||
|
||||
return PositionTrack(keyframes, interpolationType, defaultEasing, isRelative)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a focus track from JSON.
|
||||
*
|
||||
* Expected JSON format:
|
||||
* ```json
|
||||
* {
|
||||
* "trackType": "focus",
|
||||
* "keyframes": [
|
||||
* {
|
||||
* "time": 0.0,
|
||||
* "target": { "type": "position", "x": 20.0, "y": 60.0, "z": 20.0 },
|
||||
* "easing": "linear"
|
||||
* }
|
||||
* ],
|
||||
* "easing": "linear"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private fun deserializeFocusTrack(json: JsonObject): FocusTrack {
|
||||
val keyframesArray = json.getAsJsonArray("keyframes")
|
||||
?: throw IllegalArgumentException("Focus track missing 'keyframes' array")
|
||||
|
||||
val keyframes = keyframesArray.map { element ->
|
||||
val kfObj = element.asJsonObject
|
||||
val time = kfObj.get("time")?.asFloat
|
||||
?: throw IllegalArgumentException("Focus keyframe missing 'time' field")
|
||||
|
||||
val targetObj = kfObj.getAsJsonObject("target")
|
||||
?: throw IllegalArgumentException("Focus keyframe missing 'target' field")
|
||||
|
||||
val target = CameraFocus.fromJson(targetObj)
|
||||
val easing = kfObj.get("easing")?.asString
|
||||
|
||||
FocusKeyframe(time, target, easing)
|
||||
}
|
||||
|
||||
val defaultEasing = json.get("easing")?.asString ?: "linear"
|
||||
|
||||
return FocusTrack(keyframes, defaultEasing)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes an event track from JSON.
|
||||
*
|
||||
* Expected JSON format:
|
||||
* ```json
|
||||
* {
|
||||
* "trackType": "event",
|
||||
* "events": [
|
||||
* {
|
||||
* "time": 0.5,
|
||||
* "expression": "q.play_sound('cobblemon:poke_ball.open')",
|
||||
* "eventId": "ball_open"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private fun deserializeEventTrack(json: JsonObject): EventTrack {
|
||||
val eventsArray = json.getAsJsonArray("events")
|
||||
?: throw IllegalArgumentException("Event track missing 'events' array")
|
||||
|
||||
val events = eventsArray.map { element ->
|
||||
val eventObj = element.asJsonObject
|
||||
val time = eventObj.get("time")?.asFloat
|
||||
?: throw IllegalArgumentException("Event missing 'time' field")
|
||||
|
||||
val expressionStr = eventObj.get("expression")?.asString
|
||||
?: throw IllegalArgumentException("Event missing 'expression' field")
|
||||
|
||||
val eventId = eventObj.get("eventId")?.asString
|
||||
|
||||
TimedEvent(time, expressionStr.asExpressionLike(), eventId)
|
||||
}
|
||||
|
||||
return EventTrack(events)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a reference track from JSON.
|
||||
*
|
||||
* Expected JSON format:
|
||||
* ```json
|
||||
* {
|
||||
* "trackType": "reference",
|
||||
* "trackId": "intro_camera_movement"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private fun deserializeReferenceTrack(json: JsonObject): ReferenceTrack {
|
||||
val trackId = json.get("trackId")?.asString
|
||||
?: throw IllegalArgumentException("Reference track missing 'trackId' field")
|
||||
|
||||
return ReferenceTrack(trackId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a GUI track from JSON.
|
||||
*
|
||||
* Expected JSON format:
|
||||
* ```json
|
||||
* {
|
||||
* "trackType": "gui",
|
||||
* "actions": [
|
||||
* {
|
||||
* "time": 0.0,
|
||||
* "action": "OPEN",
|
||||
* "guiId": "cobblemon:battle_intro",
|
||||
* "args": { "title": "Stadium Battle" },
|
||||
* "argsExpressions": {
|
||||
* "player_party_count": "variable.player_party_count"
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* "time": 5.0,
|
||||
* "action": "CLOSE",
|
||||
* "guiId": "cobblemon:battle_intro"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private fun deserializeGuiTrack(json: JsonObject): GuiTrack {
|
||||
val actionsArray = json.getAsJsonArray("actions")
|
||||
?: throw IllegalArgumentException("GUI track missing 'actions' array")
|
||||
|
||||
val actions = actionsArray.map { element ->
|
||||
val actionObj = element.asJsonObject
|
||||
val time = actionObj.get("time")?.asFloat
|
||||
?: throw IllegalArgumentException("GUI action missing 'time' field")
|
||||
val actionType = actionObj.get("action")?.asString
|
||||
?: throw IllegalArgumentException("GUI action missing 'action' field")
|
||||
val guiId = actionObj.get("guiId")?.asString
|
||||
?: throw IllegalArgumentException("GUI action missing 'guiId' field")
|
||||
|
||||
val args = mutableMapOf<String, String>()
|
||||
actionObj.getAsJsonObject("args")?.entrySet()?.forEach { (key, value) ->
|
||||
args[key] = value.asString
|
||||
}
|
||||
|
||||
val argsExpressions = mutableMapOf<String, String>()
|
||||
actionObj.getAsJsonObject("argsExpressions")?.entrySet()?.forEach { (key, value) ->
|
||||
argsExpressions[key] = value.asString
|
||||
}
|
||||
|
||||
TimedGuiAction(
|
||||
time = time,
|
||||
action = GuiActionType.valueOf(actionType.uppercase()),
|
||||
guiId = guiId,
|
||||
args = args,
|
||||
argsExpressions = argsExpressions
|
||||
)
|
||||
}
|
||||
|
||||
return GuiTrack(actions)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.track
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.camera.model.CameraTrack
|
||||
import com.cobblemon.mod.common.api.camera.model.TrackOutput
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Track that executes MoLang expressions at specific times.
|
||||
*/
|
||||
data class EventTrack(
|
||||
val events: List<TimedEvent>
|
||||
) : CameraTrack {
|
||||
override val trackType = "event"
|
||||
|
||||
override val duration: Float
|
||||
get() = events.maxOfOrNull { it.time } ?: 0f
|
||||
|
||||
override fun evaluate(
|
||||
time: Float,
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
origin: Vec3
|
||||
): List<TrackOutput> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
init {
|
||||
require(events.zipWithNext().all { (a, b) -> a.time <= b.time }) {
|
||||
"EventTrack events must be sorted by time"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timed event - execute expression at specific time.
|
||||
*/
|
||||
data class TimedEvent(
|
||||
val time: Float,
|
||||
val expression: ExpressionLike,
|
||||
val eventId: String? = null
|
||||
) {
|
||||
init {
|
||||
require(time >= 0f) { "Event time must be non-negative, got $time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.camera.track
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.tween.EasingRegistry
|
||||
import com.cobblemon.mod.common.api.tween.Interpolator
|
||||
import com.cobblemon.mod.common.api.tween.Keyframe
|
||||
import com.cobblemon.mod.common.api.camera.model.*
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Track that animates camera look-at target over time.
|
||||
* Point targets interpolate smoothly; entity targets switch instantly.
|
||||
*/
|
||||
data class FocusTrack(
|
||||
val keyframes: List<FocusKeyframe>,
|
||||
val defaultEasing: String = "linear"
|
||||
) : CameraTrack {
|
||||
override val trackType = "focus"
|
||||
|
||||
override val duration: Float
|
||||
get() = keyframes.maxOfOrNull { it.time } ?: 0f
|
||||
|
||||
override fun evaluate(
|
||||
time: Float,
|
||||
context: Map<String, MoValue>,
|
||||
runtime: MoLangRuntime,
|
||||
origin: Vec3
|
||||
): List<TrackOutput> {
|
||||
if (keyframes.isEmpty()) return emptyList()
|
||||
|
||||
val segment = findFocusSegment(time) ?: return emptyList()
|
||||
|
||||
val focus = when {
|
||||
// Both are Point targets - interpolate
|
||||
segment.start is CameraFocus.Point && segment.end is CameraFocus.Point -> {
|
||||
val startPos = segment.start.position.toVec3(context, runtime)
|
||||
val endPos = segment.end.position.toVec3(context, runtime)
|
||||
|
||||
// Interpolate positions
|
||||
val easedProgress = EasingRegistry.getEasingFunction(segment.easing)(segment.progress)
|
||||
val interpolatedPos = Vec3(
|
||||
Mth.lerp(easedProgress.toDouble(), startPos.x, endPos.x),
|
||||
Mth.lerp(easedProgress.toDouble(), startPos.y, endPos.y),
|
||||
Mth.lerp(easedProgress.toDouble(), startPos.z, endPos.z)
|
||||
)
|
||||
|
||||
CameraFocus.Point(
|
||||
PositionExpression(
|
||||
CoordinateValue.Static(interpolatedPos.x),
|
||||
CoordinateValue.Static(interpolatedPos.y),
|
||||
CoordinateValue.Static(interpolatedPos.z)
|
||||
)
|
||||
)
|
||||
}
|
||||
// Entity targets or mixed - use instant switching
|
||||
else -> {
|
||||
if (segment.progress < 0.5f) segment.start else segment.end
|
||||
}
|
||||
}
|
||||
|
||||
return listOf(TrackOutput.Focus(focus))
|
||||
}
|
||||
|
||||
private fun findFocusSegment(time: Float): FocusSegment? {
|
||||
if (keyframes.isEmpty()) return null
|
||||
if (keyframes.size == 1) {
|
||||
val kf = keyframes[0]
|
||||
return FocusSegment(kf.target, kf.target, kf.time, kf.time, 1f, defaultEasing)
|
||||
}
|
||||
|
||||
for (i in 0 until keyframes.size - 1) {
|
||||
val start = keyframes[i]
|
||||
val end = keyframes[i + 1]
|
||||
|
||||
if (time <= end.time) {
|
||||
val duration = end.time - start.time
|
||||
val progress = if (duration > 0f) {
|
||||
((time - start.time) / duration).coerceIn(0f, 1f)
|
||||
} else 1f
|
||||
|
||||
val easing = end.easing ?: defaultEasing
|
||||
|
||||
return FocusSegment(start.target, end.target, start.time, end.time, progress, easing)
|
||||
}
|
||||
}
|
||||
|
||||
val last = keyframes.last()
|
||||
return FocusSegment(last.target, last.target, last.time, last.time, 1f, defaultEasing)
|
||||
}
|
||||
|
||||
init {
|
||||
require(keyframes.isNotEmpty()) { "FocusTrack must have at least one keyframe" }
|
||||
require(keyframes.zipWithNext().all { (a, b) -> a.time <= b.time }) {
|
||||
"FocusTrack keyframes must be sorted by time"
|
||||
}
|
||||
}
|
||||
|
||||
private data class FocusSegment(
|
||||
val start: CameraFocus,
|
||||
val end: CameraFocus,
|
||||
val startTime: Float,
|
||||
val endTime: Float,
|
||||
val progress: Float,
|
||||
val easing: String
|
||||
)
|
||||
}
|
||||
|
||||
data class FocusKeyframe(
|
||||
val time: Float,
|
||||
val target: CameraFocus,
|
||||
val easing: String? = null
|
||||
) {
|
||||
init {
|
||||
require(time >= 0f) { "Keyframe time must be non-negative, got $time" }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user