Compare commits

..
Author SHA1 Message Date
Guilherme Pereira 1986cb480b add moves tooltips 2024-12-15 22:47:47 -03:00
1812 changed files with 40861 additions and 71333 deletions
+28 -147
View File
@@ -3,155 +3,23 @@ image: amazoncorretto:21
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
# TODO add client reference test process, search for import com.cobblemon.mod.common.client
stages:
- preprocessing
- packsquash
- replaceresources
- build
- deploy
check_assets_cache:
stage: preprocessing
script:
- echo "Checking if cached hash matches current hash"
# 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
- echo "Current hash:"
- cat current_hash.txt
# Check for the existence of the previous hash
- if [[ -f assets_data_hash.txt ]]; then
echo "Previous cached hash found:";
cat assets_data_hash.txt;
if cmp -s assets_data_hash.txt current_hash.txt; then
echo "Hashes match, skipping PackSquash";
rm -f run_packsquash.flag || true;
else
echo "Hashes differ, PackSquash needed";
touch run_packsquash.flag;
fi;
else
echo "No cached hash found, marking PackSquash as required";
touch run_packsquash.flag;
fi
# Save the current hash for future runs
- echo "Saving current hash to cache";
- cp current_hash.txt assets_data_hash.txt
artifacts:
paths:
- current_hash.txt
- assets_data_hash.txt
- run_packsquash.flag
expire_in: 7 days
cache:
key: "cache-${CI_COMMIT_REF_NAME}"
paths:
- assets_data_hash.txt
- ./compressed_pack.zip
setup_resource_pack:
stage: preprocessing
script:
- echo "Setting up fake resource pack"
- mkdir ./fake_resource_pack
- cp -r common/src/main/resources/assets common/src/main/resources/data ./fake_resource_pack/
- echo '{"pack":{"pack_format":34,"description":"Fake Cobblemon Pack"}}' > ./fake_resource_pack/pack.mcmeta
artifacts:
paths:
- ./fake_resource_pack/
expire_in: 7 days
setup_packsquash:
stage: preprocessing
script:
- echo "Installing wget & unzip"
- yum install -y wget unzip
- echo "Downloading PackSquash"
- 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
pack_directory = "./fake_resource_pack/"
output_file_path = "./compressed_pack.zip"
zip_spec_conformance_level = "high"
['**/*?.ogg']
transcode_ogg = false
two_pass_vorbis_optimization_and_validation = false
sampling_frequency = 999999
empty_audio_optimization=false
EOF
artifacts:
paths:
- ./packsquash_bin/
- ./options.toml
expire_in: 7 days
default:
artifacts:
expire_in: 7 days
run_packsquash:
stage: packsquash
dependencies:
- check_assets_cache
- setup_resource_pack
- setup_packsquash
script:
- ls -l
- if [[ ! -f ./compressed_pack.zip || -f run_packsquash.flag ]]; then
echo "Running PackSquash";
chmod +x ./packsquash_bin/packsquash;
./packsquash_bin/packsquash ./options.toml;
else
echo "Skipping PackSquash; no changes detected and compressed pack exists";
fi
cache:
key: "cache-${CI_COMMIT_REF_NAME}"
policy: pull-push
paths:
- assets_data_hash.txt
- ./compressed_pack.zip
artifacts:
paths:
- ./compressed_pack.zip
replace_resources:
stage: replaceresources
dependencies:
- run_packsquash
script:
- echo "Installing unzip"
- yum install -y unzip
- echo "Unzipping compressed pack"
- mkdir temp_unpack
- unzip ./compressed_pack.zip -d temp_unpack
- echo "Removing original assets and data"
- rm -rf common/src/main/resources/assets
- rm -rf common/src/main/resources/data
- echo "Replacing assets and data"
- mv temp_unpack/assets common/src/main/resources/
- mv temp_unpack/data common/src/main/resources/
artifacts:
paths:
- common/src/main/resources/
#TODO add client reference test process, search for import com.cobblemon.mod.common.client
build:
stage: build
dependencies:
- replace_resources
only:
- branches@cable-mc/cobblemon
script:
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew build
artifacts:
cache:
key: "$CI_COMMIT_REF_NAME"
policy: push
paths:
- neoforge/build/libs
- fabric/build/libs
@@ -160,26 +28,39 @@ publish:
stage: deploy
dependencies:
- build
cache:
key: "CI_COMMIT_REF_NAME"
policy: pull
paths:
- neoforge/build/libs/*.jar
- fabric/build/libs/*.jar
rules:
- if: '$CI_PROJECT_PATH == "cable-mc/cobblemon" && $CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew publish
artifacts:
paths:
- neoforge/build/libs/*.jar
- fabric/build/libs/*.jar
deploy_artifacts:
# Separate step to deploy the artifacts, save Norry the headache of keeping even more shit around
deploy:
stage: deploy
dependencies:
- build
script:
when: manual
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- neoforge/build/libs/*.jar
- fabric/build/libs/*.jar
script:
- 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 {} ./
- mv neoforge/build/libs/*+*.jar ./
- mv fabric/build/libs/*+*.jar ./
- for f in *.jar; do mv "$f" "$(echo "$f" | sed s/+/b$CI_PIPELINE_IID+/)"; done
artifacts:
expire_in: 28 days
paths:
- ./*.jar
# - neoforge/build/libs/*+*.jar
# - fabric/build/libs/*+*.jar
+96 -167
View File
@@ -1,142 +1,62 @@
# Changelog
## [1.6.1 (Release TBD)](#1-6-1)
### Additions
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added molang particle queries for getting distance to targeted entities.
- Added debug renderer for poseable entity locators.
- Added `baseScale` property to NPCs.
- Added the player to `PokerodReelEvent`.
- Pokédex and Dialogue API screens now close when the inventory keybind is pressed.
- Added Galarica Nuts, used for crafting Galarica Cuffs and Wreaths. Dropped from certain Pokémon.
- Added specific compatibility with Repurposed Structures. (Thank you, TelepathicGrunt!)
### Changes
- Pokémon will now be dynamically revealed to the Pokédex as they're seen instead of revealing entire parties at the end of battle regardless.
- Unseen wild Pokémon will update their name from '???' to their real name as soon as a battle starts to reflect the battle UI showing the actual species name.
- Pokémon under the illusion effect will reveal their disguise to the Pokédex first and then the base Pokémon once the disguise is broken.
### Fixes
- Fixed Pokédex sometimes crashing when switching forms.
- Fixed Pokédex interface not transitioning out when closed.
- Fixed texture dimensions for the interact interface.
- Fixed crash related to Tom's Simple Storage mod and Fossil Machine.
- Fixed not being able to retrieve a fossil from the Fossil Machine with an empty hand.
- Fixed Pokémon being collidable while being captured by a Poké Ball.
- Fixed `full_party`, `own_zangoose_seviper`, `use_revive` and `use_candy` Advancement triggers.
- Fixed `healing_machine` Advancement by using the correct 1.21 trigger.
- Fix Display Cases not dropping items if destroyed through explosions.
- Fixed an issue where the first Pokémon in the pastured Pokémon list clipped into the interface.
- Fixed all Pokémon facing South on spawn.
- Parametric particle motion now works.
- Event spawned particles now work.
- Particles can now have independent coordinate spaces.
- Fixed bait being consumed when not reeling in any Pokémon.
- Fixed Miltank milk magically disappearing out of your bucket.
- Fixed Pokémon nicknames migrating from 1.5.2 not being displayed properly.
- Fixed capitalization in one of our config fields, but allowing typo'd version to still be valid.
- Fixed Poké Rods not working if lure or luck of the sea enchantments get removed by other mods.
- Fixed crashes related to Pokémon when they are ready to evolve while holding an enchanted item.
- Fixed crashes that sometimes occur when evolving Nincada.
- Fixed plants not being compostible on NeoForge.
- Fixed hide UI (F1 key) not hiding the party overlay.
- Fixed NPC MoLang command `player_lose_command` not working.
- Corrected misaligned tooltips with editboxes in the NPC editor screen.
- Fixed Pokémon riding two boats when attempting to deploy a platform on water.
- Made berry trees be shearable by dispenser blocks.
- Fixed the summary screen showing there's experience to reach the next level when at level cap.
- Fixed Pokémon forgetting moves when evolving on specific cases.
- Fixed Adorn compatibility, including improvements when using JEI/REI (Apricorn items now show up under the collapsed entries rather than standalone).
- Fixed error message appearing on battle log when using Solar Beam with Sunny Day.
- Fixed Pokémon Model offsets for larger species.
- Fixed `/pokedex grant all` command not giving male/female/shininess completion for some Pokémon.
- Fixed `/pokedex grant only` and `/pokedex remove only` not respecting the form parameter passed.
- Fixed variant forms appearing incorrectly in the Pokédex when the normal form had not been unlocked.
- Fixed Pokémon occasionally being shot into the sky during battle.
- Fixed NPC editing GUI not updating aspects until a game restart.
- Fix some users being unable to open their PC if a Pokémon in said PC had a lot of pp raises beyond normal bounds
- Fix some color variants (dubwool, conkeldurr and undyed wooloo) missing in the Pokédex
- Fix invalid species or held_items causing Players to not be able to load into their world anymore (commonly happening after removing addons/mods)
- Fixed Wooloo variants not being automatically registered in the owner's Pokédex when dyed.
- Fixed Vivichoke Dip and Leek and Potato Stew not returning a bowl upon consumption.
- Fixed Fossil Restoration Machine not accepting Hay Bales.
- Fixed Potion items so that they no longer apply over double their intended healing value.
### Molang & Datapacks
- Added flows for:
- `forme_change`: Triggered when a Pokémon changes form in battle.
- `mega_evolution`: Triggered when a Pokémon mega evolves in battle. (Note: Third party mods are required for this feature currently)
- `zpower_used`: Triggered when a Pokémon uses a Z-Power move in battle. (Note: Third party mods are required for this feature currently)
- `terastallization`: Triggered when a Pokémon terastallizes in battle. (Note: Third party mods are required for this feature currently)
- `battle_fainted`: Triggered when a Pokémon faints in battle.
- `battle_fled`: Triggered when a Player flees from battle.
- `battle_started_pre`: Triggered when a battle starts. Cancelable!
- `battle_started_post`: Triggered when a battle starts.
- `apricorn_harvested`: Triggered when an apricorn is harvested.
- `thrown_pokeball_hit`: Triggered when a thrown Pokéball hits a Pokémon.
- `level_up`: Triggered when a Pokémon levels up.
- `pokemon_fainted`: Triggered when a Pokémon faints.
- `pokemon_gained`: Triggered when a Player gains a Pokémon.
- Added molang functions:
- For Pokémon:
- `pokemon.apply(PokemonProperties)`: Applies the given properties to the Pokémon.
- `pokemon.owner`: Returns the owner of the Pokémon or 0.0 if there is no owner or they are not online.
## [1.6.0 - The Record Catch Update (December 25th, 2024)](#1-6-0)
## [1.6.0 - The Record Catch Update (Month Xth, 2024)](#1-6-0)
#### "Now that there's a fishing mechanic, the mod is actually good!"
### Additions
- Added the Pokédex as a craftable item that can be placed on lecterns.
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod because it's cool.
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
- Added Repeat Ball functionality, increasing the catch rate of Pokémon that are already registered as caught in a player's Pokédex.
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
- Added flat level battling with options to set all Pokémon to level 50, 100, or 5 for the duration of a battle. No experience or EVs are granted for a flat battle.
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
- Added support for Double Battles, Triple Battles, and Multi-battles.
- Added raft platforms for non-swimming, non-flying Pokémon to stand on during battles that take place on the water's surface (Flying Pokémon will fly over water in battle, and water breathing Pokémon will swim in water during battle).
- Added smarter send-out positions for Pokémon in battle. They should get in your way less!
- CriticalCaptures and Pokédex progress capture multiplier now work with the Pokédex.
- Added shiny Pokémon particle effects and sounds to help find them.
- Added extra visuals to early berry growth stages.
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Added Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Added particles for moves: Watersport, Mud Sport, and Psychic.
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone storage blocks.
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Added `stonjourner_henge_ruins`, `luna_henge_ruins`, and `sol_henge_ruins` structures.
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle.
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle.
- Added battle log messages for switching out Pokémon.
- Added Evolution particles and sounds for Pokémon that are sent out when evolution is started.
- Added a Nurse profession that can be unlocked by having villagers claim a healing machine block.
- Added Pokémon Centers to all 5 village types.
- Added a `natural` block state property for the healing machine block; when property is set to true, the block will have a different texture and drop an iron ingot instead of itself.
- New config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label
- `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
- Added shiny Pokémon effects.
- Added effects for the burn status effect.
- Added effects for the moves: Psychic, Water Sport, and Mud Sport.
- Added a new universal locator called `top`.
- Added `moves` option to Pokémon properties, allowing you to set the moves of a Pokémon in commands and spawn files using comma-separated move names.
- Added shiny Pokémon effects.
- Added effects for the burn status effect.
- Added effects for the moves: Seismic Toss, Withdraw, Bite, Crunch, Super Fang, Hyper Fang, Pursuit, Mist, Haze, Lick, Kinesis, Psychic, Water Sport, and Mud Sport.
- Added extra visuals to early berry growth stages.
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone storage blocks.
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items.
- Added a full evolution particle effect for Pokémon that are sent out when evolution is started.
- Added a Nurse profession that can be unlocked by having villagers claim a healing machine block.
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
- Added a new universal locator called `top`.
- Added shiny Pokémon particles with sound effects.
- Added animation and sounds for trading.
- Added icons for pending trade, team-up, and battle requests from other players.
- Added `/spawnnpc` and `/spawnnpcat` commands.
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
- Quirk animations can now occur for Pokémon that are shoulder mounted.
- Added `fishing boat` structures that contain an Explorer Map leading to a shipwreck cove and a Poké Rod Smithing Template.
- Added `submerged_shipwreck_cove` and `lush_shipwreck_cove` structures.
- Added new sounds for Poké Balls bouncing off of Pokémon and landing on the ground during capture.
- Added a unique set of sounds for Ancient Poké Balls.
- Added a sound for using Exp Candy and Rare Candy items.
- Added revamped Poké Ball animation for Pokémon breaking out.
- Added battle log messages for switching out Pokémon.
- Added config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label.
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
- Added `moves` option to Pokémon properties, allowing you to set the moves of a Pokémon in commands and spawn files using comma-separated move names.
- Added a `natural` block state property for the healing machine block; when property is set to true, the block will have a different texture and drop an iron ingot instead of itself.
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle.
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle.
- Added a `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
- Added `/spawnnpc` and `/spawnnpcat` commands.
- Added raft platforms for non-swimming, non-flying Pokémon to stand on during battles that take place on the water's surface. (Flying Pokémon will fly over water in battle, and water breathing Pokémon will swim in water during battle.)
- Added `hiddenability=false` option to `/spawnpokemon` and `/pokemonedit` commands, allowing the Pokémon's ability to be reverted to a normal ability.
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
### Structures Added
- Fishing boat structure that contain an Explorer Map leading to a shipwreck cove and a Poké Rod Smithing Template.
- Stonjourner Henge Ruins, Luna Henge Ruins, and Sol Henge Ruins.
- Submerged Shipwreck Cove and Lush Shipwreck Cove.
- Pokémon Centers to all 5 village types.
### Pokémon Added
#### Gen 1
- Alola Bias Cubone (built-in resource pack)
- Alola Bias Cubone
- Alolan Marowak
#### Gen 2
@@ -151,7 +71,6 @@
- Octillery
- Mantine
- Smeargle
- Delibird
#### Gen 3
- Slakoth
@@ -209,6 +128,9 @@
#### Gen 9
- Wattrel
- Kilowattrel
- Wiglett
- Wugtrio
- Flamigo
- Veluza
- Farigiraf
- Klawf
@@ -220,8 +142,8 @@
- Dondozo
#### Unique Forms
- Magikarp Jump variants for Magikarp
- Magikarp Jump variants for Gyarados (built-in resource pack, enabled by default on Fabric)
- Magikarp Jump Variants
- Gyarados Jump Variants
### Added cries to the following Pokémon
- Sandshrew, Sandslash
@@ -328,7 +250,6 @@
### Model updates for the following Pokémon
- Bulbasaur, Ivysaur, Venusaur
- Charmander, Charmeleon, Charizard
- Caterpie
- Kakuna, Beedrill
- Lapras
- Porygon, Porygon2, Porygon-Z
@@ -364,11 +285,9 @@
- Phantump, Trevenant
- Litten, Torracat, Incineroar
- Popplio, Brionne, Primarina
- Mudbray
- Lurantis
- Scorbunny, Cinderace
- Quaxly
- Skeledirge
- Tatsugiri
### Changes
@@ -378,37 +297,43 @@
- Adjusted volumes of sounds made by Display Cases, Berry Bushes, Energy Root, Medicinal Leek, Vivichoke, Mints, Revival Herbs and Gilded Chests.
- Changes to mulch buffs/durations.
- Updated potion sprites.
- Changed Link Cable recipe to be much cheaper.
- Link Cable recipe has been redesigned.
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs.
- Updated UI sounds for clicking and evolving Pokémon.
- Updated cries for Chansey, Crobat, Hoothoot, Noctowl.
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
- Bulbasaur, Ivysaur, Venusaur, Kakuna, Beedrill, Snorlax, Munchlax, Krabby, Kingler, Lurantis, Horsea, Seadra, Kingdra, Litten, Torracat, Incineroar, Mudkip, Marshtomp, Swampert, Scorbunny, Cinderace, Prinplup, Empoleon, Relicanth, Goldeen, Seaking, Shellder, Cloyster, Tatsugiri, Igglybuff, Jigglypuff, Wigglytuff, Krookodile, Gible, Gabite, Garchomp, Rhyhorn, Rhydon, Rhyperior, and Carvanha received model updates.
- Vivillon wings will stop being clear when you remove any Vivillon related resourcepacks. The default pattern will be the meadow wings.
- Adjusted Pokémon sendout positions for single battles.
- Wild Pokémon interaction range increased to 12 blocks (from 10 blocks).
- Player trade range increased to 12 blocks (from 10 blocks).
- PvP battle range increased to 32 blocks (from 10 blocks).
- Spectate range increased to 64 blocks (from 10 blocks).
- The inventory key now closes our GUIs if they are open, mirroring how Minecraft screens usually work.
- The noise from the pasture and PC blocks being interacted with now activates Sculk sensors.
- Updated dialogue GUI assets with proper assets not made by a programmer. Well, it was still a programmer, but- someone who can also draw!
- Updated dialogue GUI assets with proper assets.
- Updated party switching interface when in battle.
- Relic coin pouches can now be waterlogged.
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won. You've probably figured that detail out by then.
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4. There sure are a lot of Pokémon these days.
- Pokémon name and level labels are now only visible when crouching by default. Set the `displayEntityLabelsWhenCrouchingOnly` config to `false` to always have them displayed.
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won.
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4.
- The pasture block model's screen is now off by default.
- The Poké Balls creative tab is now named Utility Items as it also encompasses Poké Rod and Pokédex items.
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
- Moves learned via Sketch now persist after battle.
- Updated potion sprites
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
- Hidden Power now displays its effective typing.
- Updated particles for moves: Confusion, Protect, Sand Attack, and Quick Attack.
- Updated particles for status effects: Paralysis, Poison, and Sleep.
- Updated particles on Gastly.
- Revamped stat buff and de-buff particles.
- Improved the performance of display cases that contain Pokémon photos.
- Updated sounds for Poké Balls closing, opening and for Pokémon breaking out.
- Updated sounds for Poké Balls.
- Updated the Poké Ball animation for Pokémon breaking out.
- Improved the performance of display cases that contain Pokémon Model items.
- Removed species Base Stats from the summary interface as it is now viewable within the Pokédex.
- Changed summary tab text labels to icons. You'll get used to them. Or else.
- Changed summary tab text labels to icons.
- Clicking the summary interface exit button while the swap moves or evolve screen is open will cause the interface to switch back to the party screen. The button will exit the interface otherwise.
- Ancient Poke Balls now jump once rather than shaking 3 times when capturing Pokémon.
- The jump height indicate the number of shakes that would have occurred.
@@ -418,15 +343,12 @@
- A short jump indicates that you caught the Pokémon.
- Pokémon sent out during battle will spawn facing their opponent.
- Pokémon sent out outside a battle will spawn facing their trainer.
- Wailord is now 25% bigger. ... [But I have an idea.](https://tenor.com/view/star-wars-more-gif-21856591)
- Region-biased Pokémon forms are now optional to a built-in resource pack. This pack is enabled by default for Fabric. For NeoForge users, all our built-in resource packs default to disabled because that's all it supports right now.
- Update item sprites for Cell Battery, Chipped Pot, Covert Cloak, Cracked Pot, Masterpiece Teacup, Red Card, Sachet, and Unremarkable Teacup.
### Fixes
- Fixed Ability Patches not reverting Hidden Abilities back to Normal Abilities.
- Fixed awarding Pokémon experience upon forfeiting battles.
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating.
- Fixed Cubone's cry not having a sound (as if that Pokémon needed to be more tragic).
- Fixed Cubone's cry not having a sound.
- Fixed the sendout sound erroneously playing when a wild Pokémon breaks out of a Poké ball.
- Flamethrower is no longer missing sounds.
- Fixed the Seafloor spawning context not being a usable context.
@@ -434,58 +356,64 @@
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
- Hidden Power no longer plays the water type action effect (It now plays the normal type action effect).
- Fixed Crumbling Arch structures not blending in with the world.
- Fixed Energy Root being usable on a fainted Pokémon.
- Fixed Pokémon not being sent out when starting a battle while recalling said Pokémon.
- Fixed Chimchar and Monferno T-posing whenever they sleep.
- Fixed Cetitan's cry breaking its walk and sleep animations.
- Fixed Crumbling Arch not blending in with the world.
- Fixed Energy Root applying being usable on a fainted Pokémon.
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon.
- Fixed species comparison not using namespace for both sides in `PokemonProperties.isSubsetOf`.
- Fixed `PokemonProperties#asString` prefixing nicknames with a '$'.
- Fixed PC not saving when Pokémon was changed outside.
- Fixed shearable Pokémon not dropping correct wool colors.
- Fixed pasture spawning Pokémon inside solid blocks when closest spawning position is blocked off.
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
- Improve error handling when loading spawn sets to ensure invalid configurations don't crash the server.
- Fixed empty `JsonPlayerData` files resulting in players being unable to join server/world.
- Sound for evolving Pokémon through the Summary Menu is now correctly playing again.
- Evolution notification and UI sound is now correctly playing (again).
- Fix Starter prompt not showing up.
- Fixed owned Poké balls floating upwards if pausing the game mid-transition (which looked absolutely hilarious).
- Fix evolution now granting Pokédex entries.
- Fix Pokédex crash when a form is looked up without ever seeing the "normal" form.
- Fixed owned poké balls floating upwards if pausing mid-transition.
- Fixed all mouse buttons working for Battle UI navigation (now it's only primary/left click).
- Fixed trading Pokémon setting their friendship to 0 instead of the base value.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes having a Tera type that is already a part of their natural typing.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
- Fixed PokemonProperty argument suggestions for `tera` and `tera_type` not suggesting `stellar`.
- Fixed being able to stack Relic Coin Pouches on top of each other.
- Fixed model loader generating misleading crash-reports when client is crashing - that resurrection tank error wasn't our fault! We're innocent, your honour!
- Fixed Model loader generating misleading crash-reports when client is crashing.
- Big Roots and Energy Roots now share the same sounds as intended.
- Fixed sounds made by blocks playing at a lower pitch than intended.
- Fixed Chimchar and Monferno comically T posing whenever they sleep.
- Fixed Cetitan's cry breaking its walk and sleep animations.
- Fixed Chimchar and Monferno T posing whenever they sleep.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
- Fixed Bellossom clipping into the player head when shoulder mounted.
- Fixed Shroomish's look range to prevent it from looking higher than it should.
- Fixed Maushold's faint animations not playing. It's even sadder than Tandemaus!
- Fixed Maushold's faint animations not playing. It's sadder than Tandemaus!
- Fixed Slowking's battle idle.
- Fixed Grafaiai's walk speed to prevent model sliding.
- Fixed Maushold's faint animations not playing. It's sadder than Tandemaus!
- Fixed Slowking's battle idle.
- Fixed Grafaiai's walk speed to prevent model sliding.
- Fixed Alolan Exeggutor's tail not showing in the party UI. It will now also stand in front of all Pokémon in the party menu :)
- Fixed Timburr duplicating its log while fainting.
- Fixed hitbox sizes for Grotle and Torterra being set to default values.
- Fixed Hisuian Sneasel using Johtonian Sneasel's cry. Johto-nian. Jotonion. Jotunheim?
- Fixed Trevenant T-posing after 8 seconds of sleeping. I also do that.
- Fixed Bewear T-posing for a bit if a wild one faints where you can see.
- Fixed Noctowl's placeholder fly animation.
- Corrected Lotad blinking animation.
- Fixed reviving items causing errors when used in battle.
- Fixed Hisuian Sneasel using Johtonian Sneasel's cry
- Fixed Trevenant t-posing after 8 seconds of sleep.
- Fixed Bewear t-posing for a bit if a wild one fainted.
- Fixed reviving items throwing an exception when used in battle.
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
- Fixed Crumbling Arch Ruins generating a giant cube of air and removed the chest.
- Fixed `crumbling_arch_ruins` from generating a giant cube of air and removed the chest.
- Fixed possible crash on large population servers due to concurrent access of data.
- Quickly sending out and recalling Pokémon now looks smoother and can no longer be spammed to cause desync issues.
- Pokémon are now invulnerable during sendout animation and intangible during recall animation.
- Pokémon are now invincible during sendout animation and intangible during recall animation.
- Scrolling with party keybinding now behaves properly when using high scroll speed or scroll sensitivity.
- Fixed battle log GUI flashing when Battle GUI is opened.
- Bag Items used during a turn will now be refunded if the battle ends before the next turn begins.
- Pokémon whose current owner is not its Original Trainer now gain extra experience.
- Pokémon whose current owner is not its Original Trainer now gains extra experience.
- Fixed Fossil Resurrection advancement not being granted.
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village! This has been broken for ages but no one noticed... Hmmm... We could just remove it from here...
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village!
- Fixed the Ice Face ability activation being displayed incorrectly in a battle.
- Fixed edge case of F1 and R locking you in battle with no R functionality (requiring ESC to get out).
- Fixed Poké Ball render orientation in battle interface when capturing.
@@ -498,23 +426,27 @@
- Fixed top black border rendering in scroll interfaces in summary UI.
- Fixed aspect tracking for Advancements.
- Fixed illusion not copying aspects or caught ball.
- Delay when successfully catching a Pokémon in an Ancient Poké Ball has been increased to account for the new sounds and particles.
- Prevent summary stats tab from making sounds when clicking on an already open tab.
- Fixed display case not being able to be fed items from underneath.
- Fixed Pokémon battling in water continuously sinking to the bottom.
- Fixed passive healing and wake from faint not disabling when their config values are set to 0.
- Fixed an issue where rebinding the R key to a mouse button prevented players from closing the battle interface using that button.
### Developer
- `SpawnCause` is now an implementation of `SpawningInfluence`.
- Many types related to `Pokemon` including itself now have dedicated `Codec`, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- `TeraTypes` now implements `Iterable`.
- `PokemonBattle` now starts on turn 0.
- SpawnCause is now an implementation of SpawningInfluence.
- Renamed Pokemon.hp to Pokemon.maxHealth to make it clearer. Backwards compatibility is provided but it is an active deprecation, please migrate away from it.
- Many types related to ``Pokemon`` including itself now have dedicated ``Codec``, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- Serialization of PokemonStores and Pokemon themselves now require registry access, which will break some sidemods. This sucked for us more than it will suck for you! If you have a world or player instance, you can get it from there.
- ``TeraTypes`` now implements ``Iterable``.
- PokemonBattle now starts on turn 0.
- Added `forcedAspects` to Pokémon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the `Pokemon.aspects` setter private. This could technically break side-mods but if you are affected by this then you were using it wrong! Use `Pokemon.forcedAspects` to fix it.
- Cobblemon's main logger is properly static now.
- `PokemonEntity.enablePoseTypeRecalculation` can be used to disable automatic pose type recalculation.
- PokemonEntity.enablePoseTypeRecalculation can be used to disable automatic pose type recalculation.
- Added CollectEggEvent and HatchEggEvent for compatibility usage.
- Added events for:
- Showdown Instructions
@@ -530,34 +462,32 @@
- Added HealingSource, an interface applied to all sources of healing from player actions, for easier tracking of healing sources.
- Move Change
- Shiny Chance Calculation Event (with player context)
- Dex Information Changed (Pre and Post); this event is fired when the Pokédex is updated with new information.
- Dex Information Changed; this event is fired when the Pokédex is updated with new information.
- Rebuilt large swaths of the model animation code to simplify it.
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
- Renamed StatelessAnimation to PoseAnimation.
- Renamed StatefulAnimation to ActiveAnimation.
- Documented the animation system.
- Allowed for SpawnSnowstormEntityParticleHandler to handle non-posable entities (due to being non-posable, locators are useless - to set the offset use the settings in the particle itself).
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
- Allowed for SpawnSnowstormEntityParticleHandler to handle non-posable entities (due to being non-posable, locators are useless, to set the offset use the settings in the particle itself)
### Data Pack & Resource Pack Creators
- Added experimental `flow` datapack directory for handling events using MoLang event handlers.
- Added support for "shedders" similar to Shedinja's evolution logic.
- Fixed the placeholder `WingFlapIdle` animation so the wings are not rotating opposite to each other.
- 'player' type dialogue faces can now be explicitly stated so that NPC mods that use fake players can show in dialogue portraits.
- Added `isLeftSide` field for dialogue faces. This determines what side of dialogue box the portrait is on.
- `sounds/attacks` directory has been renamed to `sounds/move`.
- "sounds/attacks" folder has been renamed to "sounds/move"
- Moves sharing generic sounds now have unique sound events, allowing them to be changed with resource packs.
- All move sound events have been renamed to `move.<NAME>.<SOURCE>` for consistency.
- Status moves have been moved out of the `attacks` directory and split into volatile and nonvolatile.
- `status.badlypoison.actor` sound event has been renamed to `status.toxpoison.actor`
- Mulch and berry harvesting sound events have been renamed and moved to their respective directories in `sounds/block`.
- All sounds related to evolving Pokémon have been moved to the `sounds/evolution` directory.
- Sound events for all blocks now start with `block`.
- All move sound events have been renamed to "move.NAME.SOURCE" for consistency.
- Status moves have been moved out of the 'attacks' folder and split into volatile and nonvolatile.
- 'status.badlypoison.actor' sound event has been renamed to "status.toxpoison.actor"
- Mulch and berry harvesting sound events have been renamed and moved to their respective folders in "sounds/block."
- All sounds related to evolving Pokémon have been moved to the "sounds/evolution" folder.
- Sound events for all blocks now start with "block."
- Gimmighoul chest and item interaction sounds have been moved to where its cry is.
- Unused sound files and sound events have been removed.
- Poké Ball sounds are now in their animation files, making them more flexible to edit.
- Added MoLang compatibility in the isVisible property for transformed parts.
- Added `q.has_aspect('some_aspect')` function to animations, posers, and entity particle effects.
- Added q.has_aspect('some_aspect') function to animations, posers, and entity particle effects.
- Added support for conditional pose animations.
- Added a new universal locator called "top".
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
@@ -566,7 +496,6 @@
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
- `isSubermegedInWater` has been changed to `isUnderWater`
### Localization
- Updated translations for:
- Czech, German, Greek, Spanish, Mexican Spanish, French, Canadian French, Hungarian, Italian, Japanese, Korean, Dutch, Polish, Portuguese, Brazilian Portuguese, Russian, Ukrainian, Simplified Chinese, and Traditional Chinese
-27
View File
@@ -1,27 +0,0 @@
plugins {
id("java-gradle-plugin")
kotlin("jvm") version "2.1.0"
}
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
}
dependencies {
implementation("net.neoforged:moddev-gradle:2.0.75")
implementation("dev.architectury:at:1.0.1")
implementation("net.fabricmc:access-widener:2.1.0")
}
gradlePlugin {
plugins {
create("cobblemongradle") {
id = "com.cobblemon.gradle"
implementationClass = "com.cobblemon.gradle.CobblemonGradle"
version = "1.0.0"
}
}
}
-7
View File
@@ -1,7 +0,0 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -1,85 +0,0 @@
package com.cobblemon.gradle
import com.cobblemon.gradle.extension.CobblemonGradleExtension
import com.cobblemon.gradle.extension.tasks.ConvertAccessWidenersTask
import com.cobblemon.gradle.extension.tasks.UnzipAccessWidenersTask
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.LibraryElements
import org.gradle.language.jvm.tasks.ProcessResources
class CobblemonGradle : Plugin<Project> {
override fun apply(project: Project) {
val cobblemonExtension = project.extensions.create(
CobblemonGradleExtension.EXTENSION_NAME,
CobblemonGradleExtension::class.java
)
//This exists because all of
// - com.cobblemon:mod
// - com.cobblemon:mod-resources
// - com.cobblemon:accesswidener
//technically point to the same dependency (the :common project). To pick the proper artifact from that project
//we set attributes in that project and check for them here.
val accessWideners = project.configurations.register("accessWidener") { configuration ->
configuration.isCanBeConsumed = false
configuration.isCanBeResolved = true
configuration.isCanBeDeclared = true
configuration.attributes {
it.attribute(
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements::class.java, LibraryElements.OBJECTS)
)
}
}
val includeRootResources = project.configurations.register("includeRootResources") { configuration ->
configuration.isCanBeConsumed = false
configuration.isCanBeResolved = true
configuration.isCanBeDeclared = true
configuration.attributes {
it.attribute(
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements::class.java, LibraryElements.RESOURCES)
)
}
}
val unzipAwTask = project.tasks.register("unzipAccessWideners", UnzipAccessWidenersTask::class.java) {
it.accessWidenerZips = accessWideners.get()
}
val convertAwTask = project.tasks.register(ConvertAccessWidenersTask.NAME, ConvertAccessWidenersTask::class.java) {
it.enabled = cobblemonExtension.convertAccessWideners
it.dependsOn(unzipAwTask)
it.accessWideners = project.files(unzipAwTask).plus(project.files(cobblemonExtension.additionalAccessWideners))
}
//Add root resources and access wideners to output jar
project.tasks.named("processResources", ProcessResources::class.java) { task ->
task.dependsOn(includeRootResources)
if (cobblemonExtension.convertAccessWideners) {
task.dependsOn(convertAwTask)
task.from(convertAwTask)
}
else {
task.dependsOn(unzipAwTask)
task.from(unzipAwTask)
}
task.doLast {
project.copy {
it.from(project.provider {
includeRootResources.get().resolve().map { project.zipTree(it) }
})
it.into(task.destinationDir)
}
}
}
project.tasks.withType(CreateMinecraftArtifacts::class.java) {
it.dependsOn(convertAwTask)
it.accessTransformers.from(convertAwTask)
}
}
}
@@ -1,12 +0,0 @@
package com.cobblemon.gradle.extension
import org.gradle.api.file.FileCollection
abstract class CobblemonGradleExtension {
var additionalAccessWideners: FileCollection? = null
var convertAccessWideners = true
companion object {
const val EXTENSION_NAME = "cobblemon"
}
}
@@ -1,82 +0,0 @@
package com.cobblemon.gradle.extension.tasks
import dev.architectury.at.AccessChange
import dev.architectury.at.AccessTransform
import dev.architectury.at.AccessTransformSet
import dev.architectury.at.ModifierChange
import dev.architectury.at.io.AccessTransformFormats
import net.fabricmc.accesswidener.AccessWidenerReader
import net.fabricmc.accesswidener.AccessWidenerVisitor
import org.cadixdev.bombe.type.signature.MethodSignature
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
import java.io.Writer
import java.nio.file.Files
abstract class ConvertAccessWidenersTask : DefaultTask() {
abstract var accessWideners: FileCollection
@InputFiles get
@Internal
val accessTransformerDir = project.layout.buildDirectory.dir("access-transformers")
@OutputFiles
val accessTransformers = project.files ({
accessWideners.files.map {
accessTransformerDir.get().file(it.name.replace("accesswidener", "accesstransformer.cfg"))
}
})
@TaskAction
fun convertAccessWideners() {
accessWideners.files.forEach { file ->
val awFile = Files.newBufferedReader(file.toPath())
val result = AccessTransformSet.create();
AccessWidenerReader(object : AccessWidenerVisitor {
override fun visitClass(name: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(name).merge(toAt(access));
}
override fun visitMethod(owner: String, name: String, descriptor: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), toAt(access));
}
override fun visitField(owner: String, name: String, descriptor: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(owner).mergeField(name, toAt(access));
}
}).read(awFile)
val outputFile = accessTransformerDir.get().file(
file.name.replace("accesswidener", "accesstransformer.cfg")
)
var outputWriter: Writer? = null
try {
outputWriter = outputFile.asFile.writer()
AccessTransformFormats.FML.write(outputWriter, result)
}
finally {
outputWriter?.flush()
outputWriter?.close()
}
}
}
private fun toAt(access: AccessWidenerReader.AccessType): AccessTransform {
return when (access) {
AccessWidenerReader.AccessType.ACCESSIBLE -> AccessTransform.of(AccessChange.PUBLIC)
AccessWidenerReader.AccessType.EXTENDABLE, AccessWidenerReader.AccessType.MUTABLE -> AccessTransform.of(
AccessChange.PUBLIC,
ModifierChange.REMOVE
)
}
}
companion object {
const val NAME = "convertAccessWideners"
}
}
@@ -1,33 +0,0 @@
package com.cobblemon.gradle.extension.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
abstract class UnzipAccessWidenersTask : DefaultTask() {
abstract var accessWidenerZips: FileCollection
@InputFiles get
@OutputFiles
val outputFiles = project.provider {
project.files(accessWidenerZips.flatMap { zip ->
project.zipTree(zip).files.map { OUTPUT_DIR.get().file(it.name) }
})
}
@Internal
val OUTPUT_DIR = project.layout.buildDirectory.dir("cobblemon-working/access-wideners")
@TaskAction
fun run() {
project.copy {
it.from(accessWidenerZips.map(project::zipTree))
it.into(OUTPUT_DIR)
}
}
}
+1 -3
View File
@@ -1,14 +1,12 @@
plugins {
//id("cobblemon.root-conventions")
id("cobblemon.root-conventions")
id ("net.nemerosa.versioning") version "3.1.0"
}
version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
/*
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
}
*/
+54 -90
View File
@@ -1,102 +1,68 @@
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.isSnapshot
import utilities.version
/*
*
* * 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/.
*
*/
plugins {
kotlin("jvm") version "2.1.0"
id ("net.neoforged.moddev") version "2.0.75"
id("net.kyori.blossom") version "2.1.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.cobblemon.gradle")
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "3.1.0"
}
cobblemon {
additionalAccessWideners = files(sourceSets.main.get().resources).filter { it.extension == "accesswidener" }
}
val rootResources = configurations.create("rootResources") {
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.RESOURCES))
}
}
val accessWidenerExport = configurations.create("accessWidenerExport") {
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.OBJECTS))
}
}
tasks.processResources {
exclude("*.accesswidener")
}
tasks.create<Zip>("zipResources") {
dependsOn("processResources")
from(tasks.processResources.get().destinationDir)
this.archiveBaseName = "mod-resources"
}
tasks.create<Zip>("zipAccessWideners") {
from(sourceSets.main.get().resources)
include("*.accesswidener")
this.archiveBaseName = "mod-accesswideners"
}
tasks.jar {
archiveBaseName = "mod"
manifest {
attributes(mapOf("FMLModType" to "GAMELIBRARY"))
}
exclude {
it.file in tasks.processResources.get().outputs.files
}
}
tasks.assemble {
dependsOn(tasks.named("zipResources"))
dependsOn(tasks.named("zipAccessWideners"))
}
artifacts {
add("rootResources", tasks.getByName<Zip>("zipResources"))
add("accessWidenerExport", tasks.getByName("zipAccessWideners"))
}
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
}
architectury {
common("neoforge", "fabric")
}
repositories {
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://maven.blamejared.com/")
maven("https://api.modrinth.com/maven")
maven(url = "../deps")
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
mavenLocal()
}
dependencies {
compileOnly(libs.bundles.mongo)
compileOnly(libs.bundles.kotlin)
compileOnly(libs.molang)
compileOnly(libs.graal)
compileOnly(libs.jei.api)
compileOnly(libs.bundles.fabric.integrations.compileOnly) {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
compileOnly("net.fabricmc:sponge-mixin:0.15.5+mixin.0.8.7")
compileOnly("io.github.llamalad7:mixinextras-fabric:0.4.1")
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
// Showdown
modCompileOnly(libs.graal)
// Data Storage
modCompileOnly(libs.bundles.mongo)
// Unit Testing
testImplementation(libs.bundles.unitTesting)
}
neoForge {
neoFormVersion = "1.21.1-20240808.144430"
interfaceInjectionData.from("src/main/resources/interfaces.json")
validateAccessTransformers = true
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
setEvents(listOf("failed"))
setExceptionFormat("full")
}
}
sourceSets {
@@ -106,7 +72,7 @@ sourceSets {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
project.projectDir.parentFile.resolve("HEADER").forEachLine {
rootProject.file("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
@@ -119,14 +85,12 @@ sourceSets {
property("license", generateLicenseHeader())
property("modid", "cobblemon")
property("version", project.version as String)
property("isSnapshot", "true")
/*
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")
*/
}
}
}
-97
View File
@@ -1,97 +0,0 @@
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.isSnapshot
import utilities.version
/*
*
* * 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/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "3.1.0"
}
architectury {
common("neoforge", "fabric")
}
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
mavenLocal()
}
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
// Showdown
modCompileOnly(libs.graal)
// Data Storage
modCompileOnly(libs.bundles.mongo)
// Unit Testing
testImplementation(libs.bundles.unitTesting)
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
setEvents(listOf("failed"))
setExceptionFormat("full")
}
}
sourceSets {
main {
blossom {
kotlinSources {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
rootProject.file("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
builder.append(" * ").append(it).append("\n")
}
}
return builder.append(" */").append("\n").toString()
}
property("license", generateLicenseHeader())
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")
}
}
}
}
-95
View File
@@ -1,95 +0,0 @@
[versions]
# Kotlin
kotlin = "2.0.0"
# Plugins
licenser = "0.6.1"
shadow = "8.1.1"
blossom = "2.1.0"
idea-ext = "1.1.7"
# Loom
loom = "1.7-SNAPSHOT"
architectury-plugin = "3.4-SNAPSHOT"
# Common
molang = "1.1.18"
graal = "22.3.0"
icu4j = "71.1"
jei-api = "19.8.2.99"
# Fabric
fabric-loader = "0.16.5"
fabric-api = "0.104.0+1.21.1"
fabric-kotlin = "1.11.0+kotlin.2.0.0"
fabric-permissions-api = "0.2-SNAPSHOT"
lambDynamicLights = "3.0.1+1.21.1"
debugutils-fabric = "1.21-1.0.6-fabric"
# NeoForge
neoforge = "21.1.66"
neo-kotlin-for-forge = "5.5.0"
debugutils-forge = "1.21-1.0.6-neoforge"
# Data Storage
mongo = "4.10.2"
# Testing
junit = "5.9.0"
mockito = "5.2.0"
mockk = "1.12.1"
classgraph = "4.8.153"
[libraries]
# General Buildscript Plugins
kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
loom = { group = "dev.architectury", name = "architectury-loom", version.ref = "loom" }
architectury = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
blossom = { group = "net.kyori", name = "blossom", version.ref = "blossom" }
ideaExt = { group = "org.jetbrains.gradle.plugin.idea-ext", name = "org.jetbrains.gradle.plugin.idea-ext.gradle.plugin", version.ref = "idea-ext"}
licenser = { group = "gradle.plugin.org.cadixdev.gradle", name = "licenser", version.ref = "licenser" }
# Kotlin
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
# Common
jei-api = { group = "mezz.jei", name = "jei-1.21-common-api", version.ref = "jei-api"}
molang = { group = "local.com.bedrockk", name = "molang", version.ref = "molang" }
graal = { group = "org.graalvm.js", name = "js", version.ref = "graal" }
icu4j = { group = "com.ibm.icu", name = "icu4j", version.ref = "icu4j" }
# Fabric
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
fabric-kotlin = { group = "net.fabricmc", name = "fabric-language-kotlin", version.ref = "fabric-kotlin" }
fabric-permissions = { group = "me.lucko", name = "fabric-permissions-api", version.ref = "fabric-permissions-api" }
adorn-fabric = { group = "maven.modrinth", name = "adorn", version = "6.0.0-neoforge"}
lambDynamicLights = { group = "maven.modrinth", name = "lambdynamiclights", version.ref = "lambDynamicLights"}
jei-api-fabric = { group = "mezz.jei", name = "jei-1.21-fabric-api", version.ref = "jei-api" }
jei-fabric = { group = "mezz.jei", name = "jei-1.21-fabric", version.ref = "jei-api"}
fabric-debugutils = { group = "maven.modrinth", name = "debugutils", version.ref = "debugutils-fabric"}
# NeoForge
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
neo-kotlin-forge = { group = "thedarkcolour", name = "kotlinforforge-neoforge", version.ref = "neo-kotlin-for-forge" }
neoforge-debugutils = { group = "maven.modrinth", name = "debugutils", version.ref = "debugutils-forge"}
# Data Storage
mongo-driver-core = { group = "org.mongodb", name = "mongodb-driver-core", version.ref = "mongo" }
mongo-driver-sync = { group = "org.mongodb", name = "mongodb-driver-sync", version.ref = "mongo" }
# Testing
junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" }
mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
mock-kotlin = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
classgraph = { group = "io.github.classgraph", name = "classgraph", version.ref = "classgraph" }
[bundles]
kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
fabric = [ "fabric-kotlin", "fabric-permissions" ]
fabric-integrations-compileOnly = [ "adorn-fabric", "lambDynamicLights", "jei-api-fabric" ]
fabric-integrations-runtimeOnly = [ "jei-fabric" ]
mongo = [ "mongo-driver-core", "mongo-driver-sync" ]
unitTesting = [ "junit", "mockito-core", "mock-kotlin", "classgraph" ]
@@ -9,20 +9,6 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState;
import com.cobblemon.mod.common.entity.PosableEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Debug;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -48,39 +34,4 @@ public class EntityRenderDispatcherMixin {
public void resourceManagerReloadHook(ResourceManager resourceManager, CallbackInfo ci) {
CobblemonClient.INSTANCE.onAddLayer(this.playerRenderers);
}
@Inject(
method = "renderHitbox(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/minecraft/world/entity/Entity;FFFF)V",
at = @At(value = "TAIL")
)
private static void renderLocators(
PoseStack poseStack,
VertexConsumer buffer,
Entity entity,
float red,
float green,
float blue,
float alpha,
CallbackInfo ci
) {
if (entity instanceof PosableEntity posableEntity) {
if (posableEntity.getDelegate() instanceof PosableState state) {
state.getLocatorStates().forEach((locator, matrix) -> {
poseStack.pushPose();
Vec3 pos = matrix.getOrigin().subtract(entity.position());
LevelRenderer.renderLineBox(
poseStack,
buffer,
AABB.ofSize(pos, 0.25, 0.25, 0.25),
0F,
1F,
0F,
1F
);
poseStack.popPose();
});
}
}
}
}
@@ -54,10 +54,7 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
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.*
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
@@ -68,8 +65,16 @@ 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.client.CobblemonPack
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.command.argument.DexArgumentType
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.FormArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.NPCClassArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
@@ -89,21 +94,17 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
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.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
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.server
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.*
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
@@ -115,7 +116,7 @@ import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import java.util.*
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
@@ -124,7 +125,6 @@ import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.resources.ResourceKey
import net.minecraft.server.packs.PackType
import net.minecraft.world.item.NameTagItem
import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.LevelResource
@@ -164,16 +164,6 @@ object Cobblemon {
var statProvider: StatProvider = CobblemonStatProvider
var seasonResolver: SeasonResolver = TagSeasonResolver
@JvmStatic
val builtinPacks = listOf<CobblemonPack>(
CobblemonPack(id = "adorncompatibility", name = "Adorn Compatibility", packType = PackType.CLIENT_RESOURCES, activationBehaviour = ResourcePackActivationBehaviour.ALWAYS_ENABLED, neededMods = setOf("adorn")),
CobblemonPack(id = "gyaradosjump", name = "Gyarados Jump Patterns", packType = PackType.CLIENT_RESOURCES, activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED),
CobblemonPack(id = "regionbiasforms", name = "Region Bias Forms", packType = PackType.CLIENT_RESOURCES, activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED),
CobblemonPack(id = "uniqueshinyforms", name = "Shinies for Magikarp Jump", packType = PackType.CLIENT_RESOURCES, activationBehaviour = ResourcePackActivationBehaviour.NORMAL),
CobblemonPack(id = "repurposedstructurescobblemon", name = "Repurposed Structures Cobblemon", packType = PackType.SERVER_DATA, activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED, neededMods = setOf("repurposed_structures")),
)
fun preInitialize(implementation: CobblemonImplementation) {
this.implementation = implementation
@@ -218,7 +208,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel).sendToPlayer(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel).sendToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -396,13 +386,8 @@ object Cobblemon {
BattleFormat.GEN_9_SINGLES,
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
false
).ifSuccessful {
it.mute = true
}.ifErrored {
val errors = it.errors.joinToString("\n") { it.javaClass.name }
LOGGER.error("Failed to start dummy Showdown battle: $errors")
}
true
).ifSuccessful { it.mute = true }
}
registerEventHandlers()
@@ -121,17 +121,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
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))
@JvmField //if you wonder why we don't copy of OAK_WALL_SIGN, then I can tell you its because it uses Properties#dropsLike to point to the SIGN loot table, but we cant use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
val APRICORN_WALL_SIGN = this.create(
"apricorn_wall_sign",
CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE)
)
@JvmField
val APRICORN_HANGING_SIGN = this.create(
"apricorn_hanging_sign",
CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE)
)
val APRICORN_WALL_SIGN = this.create("apricorn_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), APRICORN_WOOD_TYPE))
@JvmField
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), APRICORN_WOOD_TYPE))
@JvmField
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
@JvmField
@@ -12,7 +12,6 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.reactive.SimpleObservable
@@ -51,21 +50,19 @@ object CobblemonFlows : DataRegistry {
override fun reload(manager: ResourceManager) {
clientFlows.clear()
flows.clear()
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
val flowKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
unsortedFlows.putIfAbsent(flowKey, mutableListOf())
unsortedFlows[flowKey]!!.add(identifier.path to expression)
if (identifier.path.startsWith("flows/client/")) {
clientFlows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
} else {
flows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
}
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
}
@@ -73,31 +70,11 @@ object CobblemonFlows : DataRegistry {
}
}
unsortedFlows.forEach { (key, value) ->
value.sortBy { it.first }
if (key.path.startsWith("flows/client/")) {
clientFlows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
} else {
flows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
}
}
Cobblemon.LOGGER.info("Loaded ${flows.size} flows and ${clientFlows.size} client flows")
Cobblemon.LOGGER.info("Loaded ${CobblemonScripts.scripts.size} flows and ${CobblemonScripts.clientScripts.size} client flows")
observable.emit(this)
}
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>, cancelable: Cancelable? = null) {
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
runtime.environment.query.addFunction("cancel") { cancelable }
}
flows[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
}
it.resolve(runtime, context)
}
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>) {
flows[eventResourceLocation]?.forEach { it.resolve(runtime, context) }
}
}
@@ -8,22 +8,25 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.data.JsonDataRegistry
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.network.chat.Component
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.PreparableReloadListener
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.tags.TagKey
import net.minecraft.world.level.GameRules
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.levelgen.GenerationStep
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import kotlin.reflect.KClass
interface CobblemonImplementation {
val modAPI: ModAPI
@@ -175,6 +178,16 @@ interface CobblemonImplementation {
* @param chance The chance % of increasing the composter level, 0 to 1 expected.
*/
fun registerCompostable(item: ItemLike, chance: Float)
/**
* Registers a builtin resource pack.
*
* @param id The unique [ResourceLocation] of this pack.
* @param title The title displayed in the resource pack GUI, the description is still a part of the pack metadata.
* @param activationBehaviour The [ResourcePackActivationBehaviour] for this pack.
*/
fun registerBuiltinResourcePack(id: ResourceLocation, title: Component, activationBehaviour: ResourcePackActivationBehaviour)
}
enum class ResourcePackActivationBehaviour {
@@ -392,9 +392,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
@JvmField
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
// Medicine
@JvmField
val RARE_CANDY = candyItem("rare_candy") { _, pokemon -> pokemon.getExperienceToNextLevel() }
@@ -445,7 +442,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
@@ -481,7 +477,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val HEAL_POWDER = create("heal_powder", HealPowderItem())
@JvmField
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", Item(Item.Properties().food(FoodProperties.Builder().nutrition(8).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", Item(Item.Properties().food(FoodProperties.Builder().nutrition(8).saturationModifier(0.6f).build()).stacksTo(1)))
@JvmField
val REVIVE = create("revive", ReviveItem(max = false))
@JvmField
@@ -26,25 +26,76 @@ class PartyCheckCriterion(
companion object {
val CODEC: Codec<PartyCheckCriterion> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(PartyCheckCriterion::playerCtx),
ResourceLocation.CODEC.listOf().optionalFieldOf("party", listOf()).forGetter(PartyCheckCriterion::party)
ResourceLocation.CODEC.listOf().optionalFieldOf("id", listOf()).forGetter(PartyCheckCriterion::party)
).apply(it, ::PartyCheckCriterion) }
}
override fun matches(player: ServerPlayer, context: PlayerPartyStore): Boolean {
val matches = mutableListOf<ResourceLocation>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace("minecraft") || it == "any".asIdentifierDefaultingNamespace()) {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = context.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
matches.clear()
context.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == party.size
return matches.containsAll(party) && matches.size == partyCount
}
}
/*class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
val party = mutableListOf<Identifier>()
override fun toJson(json: JsonObject) {
json.add("party", JsonArray(party.size).also {
party.forEach { pokemon -> it.add(pokemon.toString()) }
})
}
override fun fromJson(json: JsonObject) {
party.clear()
json.getAsJsonArray("party").forEach { element ->
party.add(element.asString.asIdentifierDefaultingNamespace())
}
}
override fun matches(player: ServerPlayer, context: PartyCheckContext): Boolean {
val playerParty = player.party()
val matches = mutableListOf<Identifier>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = playerParty.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
playerParty.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == partyCount
}
}
open class PartyCheckContext(val party : PlayerPartyStore)*/
@@ -61,7 +61,7 @@ open class ItemDropEntry : DropEntry {
if (dropMethod == ItemDropMethod.ON_PLAYER && player != null) {
world.addFreshEntity(ItemEntity(player.level(), player.x, player.y, player.z, stack))
} else if (dropMethod == ItemDropMethod.TO_INVENTORY && player != null && !stack.isEmpty) {
} else if (dropMethod == ItemDropMethod.TO_INVENTORY && player != null) {
val name = stack.hoverName
val count = stack.count
val succeeded = player.addItem(stack)
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
import com.cobblemon.mod.common.api.events.battles.instruction.MegaEvolutionEvent
import com.cobblemon.mod.common.api.events.battles.instruction.TerastallizationEvent
import com.cobblemon.mod.common.api.events.battles.instruction.ZMoveUsedEvent
@@ -106,8 +105,6 @@ object CobblemonEvents {
val TERASTALLIZATION = EventObservable<TerastallizationEvent>()
@JvmField
val ZPOWER_USED = EventObservable<ZMoveUsedEvent>()
@JvmField
val FORME_CHANGE = EventObservable<FormeChangeEvent>()
@JvmField
val POKEMON_SENT_PRE = CancelableObservable<PokemonSentPreEvent>()
@@ -196,11 +193,7 @@ object CobblemonEvents {
@JvmField
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
@JvmField
val POKEMON_ASPECTS_CHANGED = EventObservable<PokemonAspectsChangedEvent>()
@JvmField
val POKEDEX_DATA_CHANGED_PRE = CancelableObservable<PokedexDataChangedEvent.Pre>()
@JvmField
val POKEDEX_DATA_CHANGED_POST = EventObservable<PokedexDataChangedEvent.Post>()
val POKEDEX_DATA_CHANGED = EventObservable<PokedexDataChangedEvent>()
// Fishing
@JvmField
@@ -8,16 +8,9 @@
package com.cobblemon.mod.common.api.events.battles
import com.bedrockk.molang.runtime.struct.QueryStruct
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.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
import java.util.function.Function
/**
* Event fired when a [BattlePokemon] faints. Exposes the [BattlePokemon] that fainted and the [BattleContext]
@@ -30,20 +23,4 @@ data class BattleFaintedEvent(
override val battle: PokemonBattle,
val killed: BattlePokemon,
val context: BattleContext
) : BattleEvent {
val structContext = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to killed.struct,
"context" to QueryStruct(
hashMapOf(
"type" to Function { StringValue(context.type.name) },
"origin" to Function { context.origin?.struct ?: DoubleValue(0.0) },
"id" to Function { StringValue(context.id) },
"turn_created" to Function { DoubleValue(context.turn.toDouble()) },
)
)
)
}
) : BattleEvent
@@ -8,12 +8,8 @@
package com.cobblemon.mod.common.api.events.battles
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.util.asArrayValue
/**
* Event fired when a [PokemonBattle] is fled by a [PlayerBattleActor].
@@ -24,11 +20,4 @@ import com.cobblemon.mod.common.util.asArrayValue
data class BattleFledEvent (
override val battle: PokemonBattle,
val player: PlayerBattleActor
) : BattleEvent {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
) : BattleEvent
@@ -9,9 +9,7 @@
package com.cobblemon.mod.common.api.events.battles
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.util.asArrayValue
import net.minecraft.network.chat.MutableComponent
/**
@@ -23,14 +21,7 @@ import net.minecraft.network.chat.MutableComponent
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason: MutableComponent? = null) : BattleEvent, Cancelable() {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason: MutableComponent? = null) : BattleEvent, Cancelable()
/**
* Event fired after a [PokemonBattle] starts.
@@ -38,11 +29,4 @@ data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent
@@ -1,38 +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.events.battles.instruction
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.battles.BattleEvent
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
/**
* Event that is fired when a Pokemon changes Forme in battle.
* @param battle The battle where the change occurred.
* @param pokemon The Pokemon that changed Forme.
*/
data class FormeChangeEvent(
override val battle: PokemonBattle,
val pokemon: BattlePokemon,
val formeName: String
) : BattleEvent {
val context = mutableMapOf(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to pokemon.struct,
"forme" to StringValue(formeName)
)
}
@@ -8,25 +8,13 @@
package com.cobblemon.mod.common.api.events.battles.instruction
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
/**
* Event that is fired when a Pokemon Mega Evolves.
* @param battle The Pokemon Battle.
* @param pokemon The Pokemon that Mega Evolves.
*/
data class MegaEvolutionEvent(val battle: PokemonBattle, val pokemon: BattlePokemon) {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to pokemon.struct
)
}
data class MegaEvolutionEvent(val battle: PokemonBattle, val pokemon: BattlePokemon)
@@ -8,13 +8,9 @@
package com.cobblemon.mod.common.api.events.battles.instruction
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.types.tera.TeraType
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
/**
* Event that is fired when a Pokemon Terastallizes.
@@ -26,13 +22,4 @@ data class TerastallizationEvent(
val battle: PokemonBattle,
val pokemon: BattlePokemon,
val teraType: TeraType
) {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to pokemon.struct,
"tera_type" to StringValue(teraType.id.toString())
)
}
)
@@ -8,24 +8,12 @@
package com.cobblemon.mod.common.api.events.battles.instruction
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asArrayValue
/**
* Event that is fired when a Pokemon uses a Z-Move.
* @param battle The Pokemon Battle.
* @param pokemon The Pokemon that uses the Z-Move.
*/
data class ZMoveUsedEvent(val battle: PokemonBattle, val pokemon: BattlePokemon) {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"pokemon" to pokemon.struct
)
}
data class ZMoveUsedEvent(val battle: PokemonBattle, val pokemon: BattlePokemon)
@@ -8,13 +8,8 @@
package com.cobblemon.mod.common.api.events.farming
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.util.toArrayStruct
import com.cobblemon.mod.common.util.worldRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.block.state.BlockState
@@ -31,11 +26,4 @@ class ApricornHarvestEvent(
fun getBlock(): BlockState {
return world.getBlockState(pos)
}
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"apricorn" to StringValue(apricorn.name),
"world" to world.worldRegistry.wrapAsHolder(world).asMoLangValue(Registries.DIMENSION),
"pos" to pos.toArrayStruct()
)
}
@@ -9,11 +9,10 @@
package com.cobblemon.mod.common.api.events.fishing
import com.cobblemon.mod.common.api.events.Cancelable
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
/**
* Event that is fired when a fishing rod is reeled.
* @param rod The ItemStack of the rod that is being reeled.
*/
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable()
class PokerodReelEvent(val rod: ItemStack) : Cancelable()
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -19,9 +18,4 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
class ThrownPokeballHitEvent(
val pokeBall : EmptyPokeBallEntity,
val pokemon : PokemonEntity
) : Cancelable() {
val context = mutableMapOf(
"pokeball" to pokeBall.struct,
"pokemon" to pokemon.asMoLangValue()
)
}
) : Cancelable()
@@ -8,14 +8,9 @@
package com.cobblemon.mod.common.api.events.pokedex.scanning
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
data class PokemonScannedEvent(val player: ServerPlayer, val scannedPokemonEntityData: PokedexEntityData, val scannedEntity: ScannableEntity) {
val pokedexEntityData: PokedexEntityData
@@ -23,15 +18,4 @@ data class PokemonScannedEvent(val player: ServerPlayer, val scannedPokemonEntit
val isOwned: Boolean
get() = scannedPokemonEntityData.ownerUUID == player.uuid
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to scannedPokemonEntityData.struct,
"entity" to {
when (scannedEntity) {
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
else -> DoubleValue(0.0)
}
},
)
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -17,10 +16,4 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @author Hiroku
* @since August 5th, 2022
*/
class LevelUpEvent(val pokemon: Pokemon, val oldLevel: Int, var newLevel: Int) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"old_level" to DoubleValue(oldLevel.toDouble()),
"new_level" to DoubleValue(newLevel.toDouble())
)
}
class LevelUpEvent(val pokemon: Pokemon, val oldLevel: Int, var newLevel: Int)
@@ -11,11 +11,10 @@ package com.cobblemon.mod.common.api.events.pokemon
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.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
@@ -25,36 +24,20 @@ import java.util.UUID
/**
* Event that fires when a Pokémon's information is gained or updated in the Pokédex.
*/
sealed interface PokedexDataChangedEvent {
val dataSource: Either<PokedexEntityData, Pokemon>
val knowledge: PokedexEntryProgress
val playerUUID: UUID
data class PokedexDataChangedEvent(
val dataSource: Either<PokedexEntityData, Pokemon>,
val knowledge: PokedexEntryProgress,
val playerUUID: UUID,
val record: FormDexRecord
) {
val pokedexManager: AbstractPokedexManager
get() = record.speciesDexRecord.pokedexManager
fun getContext(): MutableMap<String, MoValue> {
return mutableMapOf(
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
"knowledge" to StringValue(knowledge.name.lowercase()),
"pokedex" to pokedexManager.struct
)
}
class Pre(
override val dataSource: Either<PokedexEntityData, Pokemon>,
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
) : PokedexDataChangedEvent, Cancelable()
class Post(
override val dataSource: Either<PokedexEntityData, Pokemon>,
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
) : PokedexDataChangedEvent
}
val context = mutableMapOf<String, MoValue>(
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
"knowledge" to StringValue(knowledge.name.lowercase()),
"pokedex" to pokedexManager.struct
)
}
@@ -1,17 +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.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.*
data class PokemonAspectsChangedEvent(
val ownerId: UUID?,
val pokemon: Pokemon,
)
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -17,9 +16,4 @@ import com.cobblemon.mod.common.pokemon.Pokemon
data class PokemonFaintedEvent(
val pokemon: Pokemon,
var faintedTimer: Int
) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"fainted_timer" to DoubleValue(faintedTimer)
)
}
)
@@ -8,16 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.UUID
data class PokemonGainedEvent(
val playerId: UUID,
val pokemon: Pokemon
) {
val context = mutableMapOf(
"player_id" to StringValue(playerId.toString()),
"pokemon" to pokemon.struct
)
}
)
@@ -74,7 +74,7 @@ abstract class RequestManager<T : ServerPlayerActionRequest> {
?: this.getInboundRequest(receiver.uuid, requestID)
/** Determines whether [player] can receive a [T]. */
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading() || player.party().find { it.entity?.isBusy == true } != null
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading()
/** Determines if the [target] is a valid interaction request target. */
abstract fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean
@@ -176,7 +176,7 @@ abstract class RequestManager<T : ServerPlayerActionRequest> {
else if (!this.isValidInteraction(player, target ?: request.sender)) // with teams anyone can accept
request.notify(player, true, "ui.interact.failed")
// if sending player is occupied, can't accept response
else if (this.isBusy(request.sender) || this.isBusy(request.receiver)) // TODO enhancement: allow retries - modify client so we don't remove request when sending acceptance
else if (this.isBusy(request.sender)) // TODO enhancement: allow retries - modify client so we don't remove request when sending acceptance
request.notify(player, true, "ui.interact.unavailable")
// if no condition is blocking acceptance, accept
else if (this.canAccept(request))
@@ -38,9 +38,8 @@ import net.minecraft.world.phys.AABB
*/
interface PokemonSelectingItem {
fun use(player: ServerPlayer, stack: ItemStack): InteractionResultHolder<ItemStack> {
val range = player.entityInteractionRange()
val entity = player.level()
.getEntities(player, AABB.ofSize(player.position(), range, range, range))
.getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0))
.filter { player.isLookingAt(it, stepDistance = 0.1F) }
.minByOrNull { it.distanceTo(player) } as? PokemonEntity?
@@ -96,12 +95,11 @@ interface PokemonSelectingItem {
player.sendSystemMessage(battleLang("bagitem.invalid").red())
} else {
battlePokemon.actor.forceChoose(BagItemActionResponse(bagItem, battlePokemon))
val stackName = BuiltInRegistries.ITEM.getKey(stack.item)
if (!player.isCreative) {
stack.shrink(1)
battlePokemon.actor.itemsUsed.add(bagItem)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.effectedPokemon.species.resourceIdentifier, stackName))
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.effectedPokemon.species.resourceIdentifier, BuiltInRegistries.ITEM.getKey(stack.item)))
}
}
@@ -149,4 +147,4 @@ interface PokemonSelectingItem {
return InteractionResultHolder.success(stack)
}
}
}
@@ -47,8 +47,6 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
@@ -58,15 +56,12 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
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.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import java.util.UUID
import kotlin.math.sqrt
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.commands.arguments.EntityAnchorArgument
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
@@ -180,30 +175,6 @@ object MoLangFunctions {
val expression = params.getString(0).asExpressionLike()
runtime.resolve(expression)
},
"system_time_millis" to java.util.function.Function { _ ->
DoubleValue(System.currentTimeMillis())
},
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
"date_local_time" to java.util.function.Function { _ ->
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
StringValue(formatted)
},
"date_of" to java.util.function.Function { params ->
val time = params.getDouble(0).toLong()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
StringValue(formatted)
},
"date_is_after" to java.util.function.Function { params ->
val dateA = params.getString(0)
val dateB = params.getString(1)
val format = java.text.SimpleDateFormat("DD/MM/YYYY")
val a = format.parse(dateA)
val b = format.parse(dateB)
DoubleValue(a.after(b))
}
)
val biomeFunctions = mutableListOf<(Holder<Biome>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
val worldFunctions = mutableListOf<(Holder<Level>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
@@ -264,31 +235,6 @@ object MoLangFunctions {
world.addFreshEntity(lightning)
return@put DoubleValue.ONE
}
// q.entity.world.spawn_bedrock_particles(effect, x, y, z, [player]) - sends to everyone nearby or just to the player if they're set.
map.put("spawn_bedrock_particles") { params ->
val particle = params.getString(0).asResource()
val x = params.getDouble(1)
val y = params.getDouble(2)
val z = params.getDouble(3)
val player = params.getOrNull<MoValue>(4)?.let {
if (it is StringValue) world.getPlayerByUUID(UUID.fromString(it.value))
else if (it is ObjectValue<*>) it.obj
else null
} as? ServerPlayer
val pos = Vec3(x, y, z)
if (world !is ClientLevel) {
val packet = SpawnSnowstormParticlePacket(particle, pos)
if (player != null) {
packet.sendToPlayer(player)
} else {
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
}
} else {
val effect = BedrockParticleOptionsRepository.getEffect(particle) ?: return@put DoubleValue.ZERO
ParticleStorm.createAtPosition(world, effect, pos).spawn()
}
}
map.put("get_entities_around") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
@@ -462,11 +408,6 @@ object MoLangFunctions {
}
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
map.put("name") { _ -> StringValue(entity.effectiveName().string) }
map.put("type") { _ ->
entity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(entity.type)?.toString()?.let {
StringValue(it)
} ?: DoubleValue.ZERO
}
map.put("yaw") { _ -> DoubleValue(entity.yRot.toDouble()) }
map.put("pitch") { _ -> DoubleValue(entity.xRot.toDouble()) }
map.put("x") { _ -> DoubleValue(entity.x) }
@@ -514,57 +455,11 @@ object MoLangFunctions {
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
}
map.put("is_standing_on_blocks") { params ->
val depth = params.getDouble(0).toInt()
val blocks: MutableSet<Block> = mutableSetOf()
for (blockIndex in 1..<params.params.size) {
val blockString = params.getString(blockIndex)
val block = BuiltInRegistries.BLOCK.get(blockString.asIdentifierDefaultingNamespace("minecraft"))
blocks.add(block)
}
return@put if (entity.isStandingOn(blocks, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
val animation = params.getString(0)
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
val target = params.getStringOrNull(1)
if (target != null) {
val targetPlayer = if (target.asUUID != null) {
entity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
} else if (entity.level() is ServerLevel) {
entity.level().server!!.playerList.getPlayerByName(target)
} else {
null
}
if (targetPlayer != null) {
packet.sendToPlayer(targetPlayer)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
} else {
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
return@put DoubleValue.ONE
}
}
}
// q.entity.spawn_bedrock_particles(effect, locator, [player]) - sends to everyone nearby or just to the player if they're set. Locator is necessary even if unused on non-posables.
map.put("spawn_bedrock_particles") { params ->
val particle = params.getString(0).asResource()
val locator = params.getString(1)
val player = params.getOrNull<MoValue>(2)?.let {
if (it is StringValue) entity.level().getPlayerByUUID(UUID.fromString(it.value))
else if (it is ObjectValue<*>) it.obj
else null
} as? ServerPlayer
val packet = SpawnSnowstormEntityParticlePacket(particle, entity.id, listOf(locator))
if (player == null) {
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
} else {
packet.sendToPlayer(player)
}
}
map
@@ -614,8 +509,7 @@ object MoLangFunctions {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(NPCProvider(npc)),
runtime = runtime,
level = npc.level()
runtime = runtime
)
npc.actionEffect = context
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
@@ -718,11 +612,6 @@ object MoLangFunctions {
map.put("form") { StringValue(pokemon.form.name) }
map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) }
map.put("matches") { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
map.put("apply") { params ->
params.getString(0).toProperties().apply(pokemon)
DoubleValue.ONE
}
map.put("owner") { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
map
}
)
@@ -8,12 +8,6 @@
package com.cobblemon.mod.common.api.moves
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.types.ElementalType
@@ -31,7 +25,6 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import kotlin.math.ceil
import kotlin.properties.Delegates
import net.minecraft.network.chat.MutableComponent
import java.util.function.Function
/**
* Representing a Move based on some template and with current PP and the number of raised PP stages.
@@ -97,21 +90,6 @@ open class Move(
val maxPp: Int
get() = template.pp + raisedPpStages * template.pp / 5
val struct: QueryStruct = ObjectValue(this).also {
it.addFunctions(
hashMapOf(
"name" to Function { StringValue(name) },
"display_name" to Function { StringValue(displayName.string) },
"description" to Function { StringValue(description.string) },
"type" to Function { StringValue(type.name) },
"damage_category" to Function { StringValue(damageCategory.name) },
"power" to Function { DoubleValue(power) },
"accuracy" to Function { DoubleValue(accuracy) },
"max_pp" to Function { DoubleValue(maxPp.toDouble()) }
)
)
}
/** Raises the max PP and rescales the current PP value. Returns false if it was already maxed out. */
fun raiseMaxPP(amount: Int): Boolean {
val oldPp = maxPp
@@ -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.api.moves
enum class MoveEffectiveness(val showdownId: Int, val multiplier: Double) {
NORMAL(0, 1.0),
WEAKNESS(1, 2.0),
RESISTANCE(2, 0.5),
IMMUNITY(3, 0.0);
companion object {
fun of(showdownId: Int): MoveEffectiveness {
return entries.find { it.showdownId == showdownId }
?: throw IllegalArgumentException("Unknown MoveEffectiveness: $showdownId")
}
}
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.moves
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.DataKeys
@@ -29,22 +27,6 @@ class MoveSet : Iterable<Move> {
private val moves = arrayOfNulls<Move>(MOVE_COUNT)
fun toStruct(): QueryStruct {
val struct = QueryStruct(hashMapOf())
struct.addFunction("move") { params ->
val index = params.getInt(0) as? Int ?: return@addFunction null
return@addFunction get(index)?.struct
}
struct.addFunction("has_move") { params ->
val moveID = params.getString(0) ?: return@addFunction null
return@addFunction if(moves.any { it?.template?.name == moveID }) DoubleValue(1.0) else DoubleValue(0.0)
}
struct.addFunction("get_move") { params ->
val moveID = params.getString(0) ?: return@addFunction null
return@addFunction moves.firstOrNull { it?.template?.name == moveID }?.struct
}
return struct
}
override fun iterator() = moves.filterNotNull().iterator()
@@ -0,0 +1,48 @@
/*
* 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.moves
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.runner.ShowdownService
object TypeChart {
private val movesEffectiveness = mutableMapOf<ElementalType, MutableMap<ElementalType, MoveEffectiveness>>()
fun load() {
movesEffectiveness.clear()
val typeChartJson = ShowdownService.service.getTypeChart()
typeChartJson.keySet().forEach { type ->
val defenderType = ElementalTypes.get(type) ?: return@forEach
val moveTypes = typeChartJson.get(type).asJsonObject.get("damageTaken").asJsonObject
moveTypes.keySet().forEach innerLoop@ {
val moveType = ElementalTypes.get(it) ?: return@innerLoop
val moveEffectiveness = moveTypes.get(it).asInt
val defenderMap = movesEffectiveness.getOrPut(moveType) { mutableMapOf() }
defenderMap[defenderType] = MoveEffectiveness.of(moveEffectiveness)
}
}
}
fun getMoveEffectiveness(moveType: ElementalType, defenderTypes: List<ElementalType?>): Double {
var multiplier = 1.0
defenderTypes.filterNotNull().forEach {
multiplier *= getMoveEffectiveness(moveType, it).multiplier
}
return multiplier
}
private fun getMoveEffectiveness(moveType: ElementalType, defenderType: ElementalType): MoveEffectiveness {
return movesEffectiveness[moveType]?.get(defenderType) ?: throw IllegalArgumentException("Unknown effectiveness type: $moveType $defenderType")
}
}
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.Level
import java.util.concurrent.CompletableFuture
/**
@@ -70,8 +69,7 @@ class ActionEffectContext(
val runtime: MoLangRuntime,
var canBeInterrupted: Boolean = false,
var interrupted: Boolean = false,
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf(),
var level: Level? = null
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
) {
inline fun <reified T> findOneProvider() = providers.filterIsInstance<T>().firstOrNull()
}
@@ -36,7 +36,7 @@ class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityC
// Treat them as expressions if possible but otherwise yeah just send them as strings
val animation = animation.map {
try {
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0" } ?: it
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0.0" } ?: it
} catch (e: Exception) {
it
}
@@ -24,7 +24,6 @@ import net.minecraft.server.level.ServerLevel
/**
* Spawns particles on the entity based on the given particle effect and locator.
* Optionally, you can specify target that ends up setting the destination pos on the spawned ParticleStorm
*
* @author Hiroku
* @since January 21st, 2024
@@ -33,50 +32,25 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
override val entityCondition = "q.entity.is_user".asExpressionLike()
var effect: String? = null
var locators: List<String> = listOf("target")
var targetLocators: Set<String>? = null
val delay: ExpressionLike = "0".asExpressionLike()
val visibilityRange = 200
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val allEntities = context.providers
val entities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities }
val userEntities = context.providers
.filterIsInstance<UsersProvider>()
.flatMap { prov -> prov.entities }
val targetEntities = context.providers
.filterIsInstance<TargetsProvider>()
.flatMap { prov -> prov.entities}
val sourceEntities = allEntities.filter { test(context, it, it in userEntities) }
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val effectIdentifier = try {
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0" } ?: effect
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: effect
} catch (e: Exception) {
effect
}?.asIdentifierDefaultingNamespace() ?: return skip()
//Things could be a little weird here since TECHNICALLY it might be possible for a battle to be happening across dimensions
val calcedLevel = (context.level as? ServerLevel) ?: allEntities.first().level() as ServerLevel
val players = calcedLevel.getPlayers { player ->
allEntities.any { player.distanceTo(it) <= visibilityRange }
}
sourceEntities.filter { it is PosableEntity }.forEach { entity ->
if (targetLocators == null) {
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators)
packet.sendToPlayers(players)
}
else {
targetEntities.forEach { targetEntity ->
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators, targetEntity.id, targetLocators!!.toList())
packet.sendToPlayers(players)
}
}
entities.filter { it is PosableEntity }.forEach { entity ->
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, (locators).toList())
val players = (entity.level() as ServerLevel).getPlayers { it.distanceTo(entity) <= visibilityRange }
packet.sendToPlayers(players)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
@@ -35,7 +35,7 @@ class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), Entit
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val soundIdentifier = try {
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0" } ?: sound
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: sound
} catch (e: Exception) {
sound
}?.asIdentifierDefaultingNamespace() ?: return skip()
@@ -38,9 +38,8 @@ class NPCClass {
var resourceIdentifier: ResourceLocation = cobblemonResource("dummy")
var names: MutableList<Component> = mutableListOf()
var aspects: MutableSet<String> = mutableSetOf() // These only make sense when applied via presets
var baseScale: Float = 1F
var hitbox = EntityDimensions.scalable(0.6F, 1.8F).withEyeHeight(1.62F)
var modelScale: Float = 0.9375F
var modelScale: Float = 0.94F
var battleConfiguration = NPCBattleConfiguration()
var interaction: NPCInteractConfiguration? = null
var canDespawn = true
@@ -63,10 +62,9 @@ class NPCClass {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(resourceIdentifier.toString())
buffer.writeCollection(names) { _, v -> buffer.writeText(v) }
buffer.writeFloat(baseScale)
buffer.writeFloat(hitbox.width)
buffer.writeFloat(hitbox.height)
buffer.writeBoolean(hitbox.fixed)
buffer.writeFloat(this.hitbox.width)
buffer.writeFloat(this.hitbox.height)
buffer.writeBoolean(this.hitbox.fixed)
battleConfiguration.encode(buffer)
buffer.writeNullable(interaction) { _, value ->
buffer.writeString(value.type)
@@ -102,7 +100,6 @@ class NPCClass {
fun decode(buffer: RegistryFriendlyByteBuf) {
resourceIdentifier = ResourceLocation.parse(buffer.readString().toString())
names = buffer.readList { buffer.readText().copy() }.toMutableList()
baseScale = buffer.readFloat()
val length = buffer.readFloat()
val width = buffer.readFloat()
val fixed = buffer.readBoolean()
@@ -26,7 +26,6 @@ class NPCPreset {
var canDespawn: Boolean? = null
var interaction: NPCInteractConfiguration? = null
var names: MutableSet<Component>? = null
var baseScale: Float? = null
var hitbox: EntityDimensions? = null
var ai: List<BrainConfig>? = null
var skill: Int? = null
@@ -52,7 +51,6 @@ class NPCPreset {
// aiScripts?.let { npcClass.aiScripts.addAll(it) }
interaction?.let { npcClass.interaction = it }
names?.let { npcClass.names.addAll(it) }
baseScale?.let { npcClass.baseScale = it }
hitbox?.let { npcClass.hitbox = it }
skill?.let { npcClass.skill = it }
autoHealParty?.let { npcClass.autoHealParty = it }
@@ -32,18 +32,6 @@ abstract class AbstractPokedexManager {
markDirty()
}
fun deleteFormRecord(speciesId: ResourceLocation, formName: String) {
val speciesRecord = speciesRecords[speciesId] ?: return
speciesRecord.deleteFormRecord(formName)
if (speciesRecord.isFormRecordsEmpty) {
deleteSpeciesRecord(speciesId)
return
}
markDirty()
}
fun getSpeciesRecord(speciesId: ResourceLocation): SpeciesDexRecord? {
return speciesRecords[speciesId]
}
@@ -162,4 +150,25 @@ abstract class AbstractPokedexManager {
open fun markDirty() {
// Save stuff
}
companion object {
const val NUM_CAUGHT_KEY = "cobblemon.pokedex.entries.caught"
const val NUM_SEEN_KEY = "cobblemon.pokedex.entries.seen"
fun getKeyForSpeciesBase(speciesId: ResourceLocation): String {
return "cobblemon.pokedex.${speciesId.path}"
}
fun getKnowledgeKeyForSpecies(speciesId: ResourceLocation): String {
return "${getKeyForSpeciesBase(speciesId)}.knowledge"
}
fun getKnowledgeKeyForForm(speciesId: ResourceLocation, formName: String): String {
return "${getKnowledgeKeyForSpecies(speciesId)}.${formName.lowercase()}"
}
fun getCaptureMethodKeyForSpecies(speciesId: ResourceLocation): String {
return "${getKeyForSpeciesBase(speciesId)}.capturemethod"
}
}
}
@@ -13,7 +13,6 @@ import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokedexDataChangedEvent
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -54,11 +53,8 @@ class FormDexRecord {
/** The genders that the dex is aware of. */
private val genders: MutableSet<Gender> = mutableSetOf()
/** Shiny states could be shiny or non-shiny - on the off chance they only saw the shiny, they shouldn't know what the normal looks like. */
private val seenShinyStates =
mutableSetOf<String>() // consider: radiants in the future (radiants should just be a resource pack tbh)
private val seenShinyStates = mutableSetOf<String>() // consider: radiants in the future (radiants should just be a resource pack tbh)
/** The current awareness of the form that the dex has. */
var knowledge = PokedexEntryProgress.NONE
private set
@@ -113,11 +109,8 @@ class FormDexRecord {
//Used when granting all entries in dex, should figure out better way
fun addAllShinyStatesAndGenders() {
val form = PokemonSpecies.getByIdentifier(speciesDexRecord.id)?.getFormByName(formName)
genders.addAll(form?.possibleGenders ?: listOf(Gender.MALE, Gender.FEMALE))
genders.addAll(listOf(Gender.MALE, Gender.FEMALE))
seenShinyStates.addAll(listOf("shiny", "normal"))
speciesDexRecord.onFormRecordUpdated(this)
}
fun setKnowledgeProgress(newKnowledge: PokedexEntryProgress) {
@@ -126,71 +119,40 @@ class FormDexRecord {
}
private fun addInformation(pokemon: Pokemon, knowledge: PokedexEntryProgress) {
genders.add(pokemon.gender)
seenShinyStates.add(if (pokemon.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokemon, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.postThen(
PokedexDataChangedEvent.Pre(
Either.right(pokemon),
knowledge,
pokedexManager.uuid,
this
),
ifSucceeded = {
genders.add(pokemon.gender)
seenShinyStates.add(if (pokemon.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokemon, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
CobblemonEvents.POKEDEX_DATA_CHANGED_POST.post(
PokedexDataChangedEvent.Post(
Either.right(pokemon),
knowledge,
pokedexManager.uuid,
this
)
)
})
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.right(pokemon), knowledge, pokedexManager.uuid, this))
}
}
private fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
genders.add(pokedexEntityData.gender)
seenShinyStates.add(if (pokedexEntityData.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokedexEntityData, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.postThen(
PokedexDataChangedEvent.Pre(
Either.left(pokedexEntityData),
knowledge,
pokedexManager.uuid,
this
),
ifSucceeded = {
genders.add(pokedexEntityData.gender)
seenShinyStates.add(if (pokedexEntityData.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokedexEntityData, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
CobblemonEvents.POKEDEX_DATA_CHANGED_POST.post(
PokedexDataChangedEvent.Post(
Either.left(pokedexEntityData),
knowledge,
pokedexManager.uuid,
this
)
)
}
)
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.left(pokedexEntityData), knowledge, pokedexManager.uuid, this))
}
}
/** Returns whether the given [Pokemon] and [knowledge] would add new information to the Pokédex.*/
fun wouldBeDifferent(pokemon: Pokemon, knowledge: PokedexEntryProgress): Boolean {
return pokemon.gender !in genders
|| (pokemon.shiny && "shiny" !in seenShinyStates)
|| (!pokemon.shiny && "normal" !in seenShinyStates)
|| knowledge.ordinal > this.knowledge.ordinal
|| speciesDexRecord.wouldBeDifferent(pokemon)
|| (pokemon.shiny && "shiny" !in seenShinyStates)
|| (!pokemon.shiny && "normal" !in seenShinyStates)
|| knowledge.ordinal > this.knowledge.ordinal
|| speciesDexRecord.wouldBeDifferent(pokemon)
}
fun wouldBeDifferent(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress): Boolean {
@@ -50,8 +50,6 @@ class SpeciesDexRecord {
lateinit var id: ResourceLocation
private val aspects: MutableSet<String> = mutableSetOf()
private val formRecords: MutableMap<String, FormDexRecord> = mutableMapOf()
val isFormRecordsEmpty: Boolean
get() = formRecords.isEmpty()
fun describe(): String {
return "SpeciesDexRecord(aspects=$aspects, formRecords=$formRecords)"
@@ -32,10 +32,9 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
if (searchString == "") return true
val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
val highestKnowledgeForEntry = pokedexManager.getHighestKnowledgeFor(entry)
if (highestKnowledgeForEntry == PokedexEntryProgress.NONE) return false
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.NONE) return false
when (searchByType) {
when(searchByType) {
SearchByType.ABILITIES -> {
if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.CAUGHT) return false
val abilityList = mutableListOf<String>()
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.TypeChart
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
@@ -125,6 +126,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
TypeChart.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -130,7 +129,7 @@ interface Evolution : EvolutionLike {
owner.party().getFirstAvailablePosition() ?: return false
// Add shed Pokemon to player's party
val shedPokemon = pokemon.clone(registryAccess = owner.registryAccess())
val shedPokemon = pokemon.clone()
shedPokemon.removeHeldItem()
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
@@ -163,7 +162,7 @@ interface Evolution : EvolutionLike {
evolutionMethod(pokemon)
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("ui.evolve.into", preEvoName, pokemon.species.translatedName))
} else {
pokemonEntity.entityData.set(PokemonEntity.EVOLUTION_STARTED, true)
pokemonEntity.busyLocks.add("evolving")
pokemonEntity.navigation.stop()
pokemonEntity.after(1F) {
evolutionAnimation(pokemonEntity)
@@ -173,7 +172,7 @@ interface Evolution : EvolutionLike {
}
pokemonEntity.after( seconds = 12F ) {
cryAnimation(pokemonEntity)
pokemonEntity.entityData.set(PokemonEntity.EVOLUTION_STARTED, false)
pokemonEntity.busyLocks.remove("evolving")
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("ui.evolve.into", preEvoName, pokemon.species.translatedName))
}
}
@@ -190,23 +189,15 @@ interface Evolution : EvolutionLike {
}
fun evolutionMethod(pokemon: Pokemon) {
// This ensures the Pokémon doesn't lose moves during evolution
// (e.g., Oshawott evolving at level 17 knowing Razor Shell, while Dewott only learns it at level 18).
val previousSpeciesLearnableMoves = pokemon.relearnableMoves
this.result.apply(pokemon)
val movesToLearn = previousSpeciesLearnableMoves + this.learnableMoves
movesToLearn.forEach { move ->
this.learnableMoves.forEach { move ->
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
} else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
@@ -31,7 +31,6 @@ class BedrockParticleEmitter(
var rate: ParticleEmitterRate = InstantParticleEmitterRate(),
var shape: ParticleEmitterShape = SphereParticleEmitterShape(),
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0)),
var space: EmitterSpace = EmitterSpace(),
var eventTimeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
@@ -46,20 +45,18 @@ class BedrockParticleEmitter(
ParticleEmitterRate.codec.fieldOf("rate").forGetter { it.rate },
ParticleEmitterShape.codec.fieldOf("shape").forGetter { it.shape },
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime },
EmitterSpace.CODEC.fieldOf("space").forGetter { it.space },
EventTriggerTimeline.CODEC.fieldOf("eventTimeline").forGetter { it.eventTimeline },
SimpleEventTrigger.CODEC.listOf().fieldOf("creationEvents").forGetter { it.creationEvents },
SimpleEventTrigger.CODEC.listOf().fieldOf("expirationEvents").forGetter { it.expirationEvents },
EventTriggerTimeline.CODEC.fieldOf("travelDistanceEvents").forGetter { it.travelDistanceEvents },
LoopingTravelDistanceEventTrigger.CODEC.listOf().fieldOf("loopingTravelDistanceEvents").forGetter { it.loopingTravelDistanceEvents }
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, space, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
BedrockParticleEmitter(
startExpressions = startExpressions,
updateExpressions = updateExpressions,
shape = shape,
rate = rate,
lifetime = lifetime,
space = space,
eventTimeline = eventTimeline,
creationEvents = creationEvents,
expirationEvents = expirationEvents,
@@ -1,61 +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.snowstorm
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.cobblemon.mod.common.util.codec.optionalFieldOfWithDefault
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import org.joml.AxisAngle4f
import org.joml.Matrix4f
import org.joml.Vector3f
class EmitterSpace(
var scaling: ScalingMode = ScalingMode.WORLD
) {
companion object {
val CODEC: Codec<EmitterSpace> = RecordCodecBuilder.create { instance ->
instance.group(
Codec.STRING.optionalFieldOfWithDefault("scaling", "world").forGetter { it.scaling.toString() }
).apply(instance) { scaling ->
EmitterSpace(
ScalingMode.valueOf(scaling.uppercase())
)
}
}
}
fun initializeEmitterMatrix(rootMatrix: MatrixWrapper, locatorMatrix: MatrixWrapper): MatrixWrapper {
val rootRotation = rootMatrix.matrix.getRotation(AxisAngle4f())
val scale = Vector3f()
if (scaling == ScalingMode.ENTITY) {
locatorMatrix.matrix.getScale(scale)
}
else {
rootMatrix.matrix.getScale(scale)
//Incase they are multiplying some axis by -1 to rotate
scale.x = Math.signum(scale.x)
scale.y = Math.signum(scale.y)
scale.z = Math.signum(scale.z)
}
val particleScale = Vector3f(scale.x, scale.y, scale.z)
//Presumably we will want to make the initial rotation configurable instead of always using root
val particleRawMatrix = Matrix4f().scale(particleScale).rotate(rootRotation)
return MatrixWrapper().updateMatrix(particleRawMatrix).updatePosition(locatorMatrix.getOrigin())
}
enum class ScalingMode {
WORLD,
ENTITY
}
}
@@ -26,7 +26,6 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
/**
* An event that can be referenced from various particle event triggers. The events are not necessarily particles,
@@ -78,11 +77,9 @@ class ParticleEvent(
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
particleEffect?.let { effect ->
val bedrockParticleOptions = BedrockParticleOptionsRepository.getEffect(effect.effect) ?: return@let
val particleMatrix = when (effect.type) {
//It may seem incorrect to clone for both of these, but the locator matrix determines whether the
//emitter follows the locator. We don't want the child emitter to affect the parent emitter so we make a copy
EventParticleOptions.EventParticleType.EMITTER,
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.emitterSpaceMatrix.clone()
val rootMatrix = when (effect.type) {
EventParticleOptions.EventParticleType.EMITTER-> MatrixWrapper().updatePosition(storm.matrixWrapper.position).updateMatrix(storm.matrixWrapper.matrix)
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
EventParticleOptions.EventParticleType.PARTICLE,
EventParticleOptions.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let {
Vec3(
@@ -90,19 +87,7 @@ class ParticleEvent(
it.getY(),
it.getZ()
)
} ?: Vec3(storm.getX(), storm.getY(), storm.getZ())).let {
val matrixWrapper = MatrixWrapper()
matrixWrapper.updatePosition(it)
//We only want the orientation of the original matrix, not position/translation
matrixWrapper.updateMatrix(Matrix4f(storm.emitterSpaceMatrix.matrix).setTranslation(0F, 0F, 0F))
}
}
val locatorMatrix = when (effect.type) {
EventParticleOptions.EventParticleType.EMITTER -> particleMatrix
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.locatorSpaceMatrix
EventParticleOptions.EventParticleType.PARTICLE,
EventParticleOptions.EventParticleType.PARTICLE_WITH_VELOCITY -> particleMatrix
} ?: Vec3(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
}
val sourceVelocity = when (effect.type) {
@@ -120,24 +105,17 @@ class ParticleEvent(
val newStorm = ParticleStorm(
effect = bedrockParticleOptions,
emitterSpaceMatrix = particleMatrix,
locatorSpaceMatrix = locatorMatrix,
matrixWrapper = rootMatrix,
world = storm.world,
sourceVelocity = sourceVelocity,
sourceAlive = storm.sourceAlive,
sourceVisible = storm.sourceVisible,
onDespawn = {},
targetPos = storm.targetPos,
runtime = MoLangRuntime().setup(),
runtime = MoLangRuntime().setup().also { it.environment.query = storm.runtime.environment.query },
entity = storm.entity
)
//The reason this doesn't use the newStorms runtime is that the parent storm can add queries AFTER
//the child storm spawns in. Say a parent storm has a creation event. The child spawns before the parent
//When the parent tries to add its queries, it overwrites the child's
val tempRuntime = MoLangRuntime().setup().also {
it.environment.query = storm.runtime.environment.query
}
effect.expression?.resolve(tempRuntime)
effect.expression?.resolve(newStorm.runtime)
newStorm.spawn()
}
@@ -77,17 +77,16 @@ class ParametricParticleMotion(
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3, emitterPos: Vec3) = Vec3.ZERO
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3): Vec3 {
val particlePositionWS = Vec3(
particle.getX(),
particle.getY(),
particle.getZ()
val stormPosition = Vec3(
particle.storm.getX(),
particle.storm.getY(),
particle.storm.getZ()
)
val matrix = particle.matrixWrapper
val targetPosPS = runtime.resolveVec3d(offset)
val targetPosWS = matrix.transformPosition(targetPosPS)
return targetPosWS.subtract(particlePositionWS)
val offset = runtime.resolveVec3d(this.offset)
val particlePosition =
Vec3(particle.getX(), particle.getY(), particle.getZ())
val desiredPosition = stormPosition.add(offset)
return desiredPosition.subtract(particlePosition)
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3, minSpeed: Float) = runtime.resolveVec3d(direction).normalize()
@@ -278,7 +277,7 @@ class CustomMotionDirection(
runtime.resolveDouble(direction.second),
runtime.resolveDouble(direction.third)
)
return storm.emitterSpaceMatrix.matrix.transformDirection(v)
return storm.matrixWrapper.matrix.transformDirection(v)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item
import net.minecraft.world.item.enchantment.EnchantmentHelper
import net.minecraft.world.item.enchantment.Enchantments
@@ -28,10 +27,6 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
var minLureLevel: Int? = null
var maxLureLevel: Int? = null
var bait: ResourceLocation? = null
var rodType: ResourceLocation? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
@@ -59,6 +59,10 @@ abstract class SpawningCondition<T : SpawningContext> {
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var structures: MutableList<Either<ResourceLocation, TagKey<Structure>>>? = null
var minLureLevel: Int? = null
var maxLureLevel: Int? = null
var bait: ResourceLocation? = null
var rodType: ResourceLocation? = null
var isSlimeChunk: Boolean? = null
@Transient
@@ -33,11 +33,6 @@ open class PlayerLevelRangeInfluence(
val noPokemonRange: IntRange = 1 .. config.minimumLevelRangeMax,
val recalculationMillis: Long = 5000L
) : SpawningInfluence {
companion object {
/** The internally tuned variation for player level range influences */
val TYPICAL_VARIATION = 5
}
val uuid = player.uuid
var lastCalculatedTime: Long = 0
var previousRange: IntRange = noPokemonRange
@@ -53,7 +53,7 @@ class BestSpawnerConfig {
.create()
const val CONFIG_NAME = "best-spawner-config"
const val INTERNAL_PATH = "data/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
const val INTERNAL_PATH = "/data/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
const val EXTERNAL_PATH = "config/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
fun load(): BestSpawnerConfig {
@@ -77,7 +77,7 @@ class BestSpawnerConfig {
}
private fun loadInternal(): BestSpawnerConfig {
val reader = InputStreamReader(Cobblemon.javaClass.classLoader.getResourceAsStream(INTERNAL_PATH)!!)
val reader = InputStreamReader(Cobblemon::class.java.getResourceAsStream(INTERNAL_PATH)!!)
val config = GSON.fromJson(reader, BestSpawnerConfig::class.java)
reader.close()
return config
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence.Companion.TYPICAL_VARIATION
import com.cobblemon.mod.common.api.spawning.influence.RestrictedSpawnBlocksInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
@@ -43,7 +42,7 @@ object PlayerSpawnerFactory {
var influenceBuilders = mutableListOf<(player: ServerPlayer) -> SpawningInfluence?>({
PlayerLevelRangeInfluence(
it,
variation = TYPICAL_VARIATION
variation = 5
)
}, { RestrictedSpawnBlocksInfluence() })
@@ -30,9 +30,8 @@ abstract class TriggerSpawner<T : SpawnCause>(override val name: String, var spa
private var selector: SpawningSelector = FlatContextWeightedSelector()
override val influences = mutableListOf<SpawningInfluence>()
open fun run(cause: T, world: ServerLevel, pos: BlockPos, influences: List<SpawningInfluence> = emptyList()): CompletableFuture<*>? {
open fun run(cause: T, world: ServerLevel, pos: BlockPos): CompletableFuture<*>? {
val context = parseContext(cause, world, pos) ?: return null
context.influences.addAll(influences)
selector.select(this, listOf(context))?.let { (_, spawn) ->
val action = spawn.doSpawn(context)
action.complete()
@@ -8,14 +8,12 @@
package com.cobblemon.mod.common.api.storage
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.storage.factory.FileBackedPokemonStoreFactory
import com.cobblemon.mod.common.api.storage.factory.PokemonStoreFactory
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.google.gson.JsonObject
import java.util.UUID
import net.minecraft.core.RegistryAccess
@@ -149,14 +147,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
operator fun get(uuid: UUID) = find { it.uuid == uuid }
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {
Cobblemon.LOGGER.error("Failed to read unknown species: ${nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)}")
}
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {}
abstract fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess): CompoundTag
abstract fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess): PokemonStore<T>
open fun handleInvalidSpeciesJSON(json: JsonObject) {
Cobblemon.LOGGER.error("Failed to read unknown species: ${json.get(DataKeys.POKEMON_SPECIES_IDENTIFIER).asString}")
}
open fun handleInvalidSpeciesJSON(json: JsonObject) {}
abstract fun saveToJSON(json: JsonObject, registryAccess: RegistryAccess): JsonObject
abstract fun loadFromJSON(json: JsonObject, registryAccess: RegistryAccess): PokemonStore<T>
abstract fun savePositionToNBT(position: T, nbt: CompoundTag)
@@ -39,7 +39,7 @@ abstract class JsonBackedPlayerDataStoreBackend<T : InstancedPlayerData>(
override fun load(uuid: UUID): T {
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
return if (playerFile.exists()) {
gson.fromJson(BufferedReader(FileReader(playerFile)), classToken).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
@@ -37,11 +37,11 @@ abstract class NbtBackedPlayerData<T : InstancedPlayerData>(
override fun load(uuid: UUID): T {
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
return if (playerFile.exists()) {
val input = NbtIo.read(playerFile.toPath())
val decodeResult = NbtOps.INSTANCE.withDecoder(codec).apply(input)
decodeResult.getPartialOrThrow {
Cobblemon.LOGGER.error("Error decoding $subfolder for player uuid $uuid")
Cobblemon.LOGGER.error("Error decoding pokedex for player uuid ${uuid}")
Cobblemon.LOGGER.error(it)
throw UnsupportedOperationException()
}.first
@@ -113,13 +113,16 @@ object BattleBuilder {
player2Actor.battleTheme = player1.getBattleTheme()
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(player1Actor),
side2 = BattleSide(player2Actor)
).ifSuccessful {
it.battlePartyStores.addAll(battlePartyStores)
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
@@ -211,12 +214,13 @@ object BattleBuilder {
return if (errors.isEmpty) {
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActors[0], playerActors[1]),
side2 = BattleSide(playerActors[2], playerActors[3])
battleFormat = battleFormat,
side1 = BattleSide(playerActors[0], playerActors[1]),
side2 = BattleSide(playerActors[2], playerActors[3])
).ifSuccessful {
it.battlePartyStores.addAll(battlePartyStores)
}
errors
} else {
errors
}
@@ -283,6 +287,7 @@ object BattleBuilder {
playerActor.battleTheme = pokemonEntity.getBattleTheme()
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
@@ -291,7 +296,9 @@ object BattleBuilder {
if (!cloneParties) {
pokemonEntity.battleId = it.battleId
}
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
@@ -360,13 +367,16 @@ object BattleBuilder {
playerActor.battleTheme = npcEntity.getBattleTheme()
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
side2 = BattleSide(npcActor)
).ifSuccessful { battle ->
npcEntity.entityData.update(NPCEntity.BATTLE_IDS) { it + battle.battleId }
result = SuccessfulBattleStart(battle)
}
result
} else {
errors
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
@@ -179,35 +178,19 @@ object BattleRegistry {
ShowdownService.service.startBattle(battle, messages.toTypedArray())
}
/**
* Instantiates a new [PokemonBattle] and launches the Showdown service.
*
* @param battleFormat The format to use for the battle.
* @param side1 The first side for the battle.
* @param side2 The second side for the battle.
* @param canPreempt Whether the battle can be blocked by subscribers to [CobblemonEvents.BATTLE_STARTED_PRE].
*
* NOTE: this does NOT validate the [battleFormat] or sides before launching Showdown.
* This creates the battle and hands it off to Showdown - YOU WILL GET ISSUES IF THE BATTLE IS INVALID!
* See [BattleBuilder] functions for how the various types of battles are built and validated.
*/
fun startBattle(
battleFormat: BattleFormat,
side1: BattleSide,
side2: BattleSide,
canPreempt: Boolean = true
silent: Boolean = false
): BattleStartResult {
val battle = PokemonBattle(battleFormat, side1, side2)
val start: () -> Unit = {
battleMap[battle.battleId] = battle
startShowdown(battle)
}
if (!canPreempt) start().also { return SuccessfulBattleStart(battle) }
if (silent) return SuccessfulBattleStart(battle)
val preBattleEvent = BattleStartedPreEvent(battle)
CobblemonEvents.BATTLE_STARTED_PRE.postThen(preBattleEvent) {
start()
battleMap[battle.battleId] = battle
startShowdown(battle)
CobblemonEvents.BATTLE_STARTED_POST.post(BattleStartedPostEvent(battle))
return SuccessfulBattleStart(battle)
}
@@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3
import java.util.*
/**
* Responsible for managing [BattleChallenge]s and initiating PVP [com.cobblemon.mod.common.api.battles.model.PokemonBattle]s.
* Responsible for managing [BattleChallenge]s and initiating PVP [PokemonBattle]s.
*
* @author Segfault Guy, JazzMcNade
* @since October 28th, 2024
@@ -122,7 +122,7 @@ object ChallengeManager : RequestManager<ChallengeManager.BattleChallenge>() {
}
}
override fun isValidInteraction(player: ServerPlayer, target: ServerPlayer) = player.canInteractWith(target, Cobblemon.config.battlePvPMaxDistance)
override fun isValidInteraction(player: ServerPlayer, target: ServerPlayer) = player.canInteractWith(target, Cobblemon.config.BattlePvPMaxDistance)
override fun canAccept(request: BattleChallenge): Boolean {
if (request is MultiBattleChallenge) {
@@ -132,7 +132,7 @@ object ChallengeManager : RequestManager<ChallengeManager.BattleChallenge>() {
if (existingReceiverTeam == null || existingSenderTeam == null) {
request.notifySender(true, "error.missing_team")
request.notifyReceiver(true, "error.missing_team")
return false
return false;
}
val players = existingReceiverTeam.teamPlayers + existingSenderTeam.teamPlayers
@@ -107,7 +107,7 @@ interface Targetable {
MoveTarget.adjacentAllyOrSelf -> null
MoveTarget.adjacentFoe -> null
MoveTarget.foeSide -> getAllActivePokemon().filter { !it.isAllied(this) }
MoveTarget.scripted -> listOf(this)
MoveTarget.scripted -> null
else -> null
}
}
@@ -54,7 +54,7 @@ object ShowdownInterpreter {
}
listOf(
"player", "teamsize", "gametype", "gen", "tier", "rated", "clearpoke", "poke", "teampreview", "start", "rule", "t:", "", "capture", "-anim"
"player", "teamsize", "gametype", "gen", "tier", "rated", "clearpoke", "poke", "teampreview", "start", "rule", "t:", "", "capture"
).forEach { updateInstructionParser[it] = { _, _, _, _ -> IgnoredInstruction() } }
updateInstructionParser["-ability"] = { _, instructionSet, message, _ -> AbilityInstruction(instructionSet, message) }
@@ -69,7 +69,7 @@ object ShowdownInterpreter {
updateInstructionParser["-copyboost"] = { _, _, message, _ -> CopyBoostInstruction(message) }
updateInstructionParser["-crit"] = { _, instructionSet, message, _ -> CritInstruction(message, instructionSet) }
updateInstructionParser["-curestatus"] = { _, _, message, _ -> CureStatusInstruction(message) }
updateInstructionParser["detailschange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
updateInstructionParser["detailschange"] = { _, _, message, _ -> DetailsChangeInstruction(message) }
updateInstructionParser["-endability"] = { _, _, message, _ -> EndAbilityInstruction(message) }
updateInstructionParser["-end"] = { _, _, message, _ -> EndInstruction(message) }
updateInstructionParser["-enditem"] = { _, _, message, _ -> EndItemInstruction(message) }
@@ -78,7 +78,6 @@ object ShowdownInterpreter {
updateInstructionParser["-fieldactivate"] = { _, _, message, _ -> FieldActivateInstruction(message) }
updateInstructionParser["-fieldend"] = { _, _, message, _ -> FieldEndInstruction(message) }
updateInstructionParser["-fieldstart"] = { _, _, message, _ -> FieldStartInstruction(message) }
updateInstructionParser["-formechange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
updateInstructionParser["-hitcount"] = { _, _, message, _ -> HitCountInstruction(message) }
updateInstructionParser["-immune"] = { _, _, message, _ -> ImmuneInstruction(message) }
updateInstructionParser["-invertboost"] = { _, _, message, _ -> InvertBoostInstruction(message) }
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.battles.dispatch
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import org.objectweb.asm.tree.analysis.Interpreter
class InstructionSet {
val instructions: MutableList<InterpreterInstruction> = mutableListOf()
@@ -63,8 +63,7 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -56,8 +56,7 @@ class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val is
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -52,8 +52,7 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -114,8 +114,7 @@ class DamageInstruction(
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -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.battles.interpreter.instructions
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.util.battleLang
/**
* Format: |detailschange|POKEMON|DETAILS|HP STATUS
*
* POKEMON has changed formes permanently (i.e. Mega Evolution) to DETAILS.
* @author Segfault Guy
* @since September 10th, 2023
*/
class DetailsChangeInstruction(val message: BattleMessage): InterpreterInstruction {
override fun invoke(battle: PokemonBattle) {
val battlePokemon = message.battlePokemon(0, battle) ?: return
val formName = message.argumentAt(1)?.split(',')?.get(0)?.substringAfter('-')?.lowercase() ?: return
battle.dispatchWaiting {
val pokemonName = battlePokemon.getName()
battle.broadcastChatMessage(battleLang("detailschange.$formName", pokemonName))
battle.majorBattleActions[battlePokemon.uuid] = message
}
}
}
@@ -1,51 +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.battles.interpreter.instructions
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.util.battleLang
/**
* Format: |-formechange|POKEMON|SPECIES|HP STATUS or |detailschange|POKEMON|DETAILS|HP STATUS
*
* POKEMON has temporarily (-formechange) or permanently (detailschange) changed form to SPECIES/DETAILS.
* @author Segfault Guy
* @since December 9th, 2024
*/
class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction {
override fun invoke(battle: PokemonBattle) {
val battlePokemon = message.battlePokemon(0, battle) ?: return
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
val speciesName = details.substringBefore('-')
val formName = details.substringAfterLast('-').ifBlank { speciesName }
val typeKey = if (message.id == "detailschange") "permanent" else "temporary"
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), battlePokemon)
battle.dispatchWaiting {
battle.minorBattleActions[battlePokemon.uuid] = message
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
val pokemonName = battlePokemon.getName()
val lang = when(formName) {
"busted", "hero", "complete" -> return@dispatchWaiting
"school", "wishiwashi", "meteor", "minior" -> battleLang("formechange.$formName", pokemonName)
speciesName -> battleLang("formechange.default.temporary.end", pokemonName)
else -> battleLang("formechange.default.$typeKey", pokemonName, formName)
}
battle.broadcastChatMessage(lang)
}
}
}
@@ -102,8 +102,7 @@ class MoveInstruction(
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
providers = providers
)
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
@@ -13,8 +13,6 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
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.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokemonSeenEvent
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
import com.cobblemon.mod.common.battles.ShowdownInterpreter
@@ -215,12 +213,7 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
battleLang("switch.other.nickname", actor.getName(), nickname, publicPokemon.species.translatedName)
} ?: battleLang("switch.other", actor.getName(), publicPokemon.getDisplayName())
actor.sendMessage(battleLang("switch.self", publicPokemon.getDisplayName()))
battle.actors.filter { it != actor }.forEach {
it.sendMessage(publicLang)
}
battle.playerUUIDs.forEach { uuid ->
CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, publicPokemon))
}
battle.actors.filter { it != actor }.forEach { it.sendMessage(publicLang) }
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.pokemon.PokemonSeenEvent
import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.plus
import com.cobblemon.mod.common.api.text.red
@@ -69,6 +70,11 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
}
battle.dispatchGo {
battle.end()
battle.actors.flatMap { it.pokemonList }.map { it.originalPokemon }.forEach { pokemon ->
battle.playerUUIDs.forEach { playerUuid ->
CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(playerUuid, pokemon))
}
}
CobblemonEvents.BATTLE_VICTORY.post(BattleVictoryEvent(battle, winners, losers, wasCaught))
ShowdownInterpreter.lastCauser.remove(battle.battleId)
}
@@ -8,11 +8,7 @@
package com.cobblemon.mod.common.battles.pokemon
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asStruct
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
@@ -28,10 +24,8 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.server
import java.util.UUID
import net.minecraft.network.chat.MutableComponent
import java.util.function.Function
open class BattlePokemon(
val originalPokemon: Pokemon,
@@ -39,11 +33,9 @@ open class BattlePokemon(
val postBattleEntityOperation: (PokemonEntity) -> Unit = {}
) {
lateinit var actor: BattleActor
companion object {
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
//TOOD figure out a closer registry access (might have to break some method signatures for this (1.7?)
val effectedPokemon = pokemon.clone(registryAccess = server()?.registryAccess() ?: throw IllegalStateException("No registry access available"))
val effectedPokemon = pokemon.clone()
BattleCloneProperty.isBattleClone().apply(effectedPokemon)
UncatchableProperty.uncatchable().apply(effectedPokemon)
return BattlePokemon(
@@ -62,22 +54,6 @@ open class BattlePokemon(
)
}
val struct = QueryStruct(
hashMapOf(
"pokemon" to Function { effectedPokemon.asStruct() },
"original_pokemon" to Function {
originalPokemon.asStruct()
},
"actor" to Function { actor.struct },
"battle" to Function { actor.battle.struct },
"uuid" to Function { StringValue(uuid.toString()) },
"health" to Function { DoubleValue(health.toDouble()) },
"max_health" to Function { DoubleValue(maxHealth.toDouble()) },
"ivs" to Function { effectedPokemon.ivs.struct },
"nature" to Function { StringValue(effectedPokemon.nature.name.toString()) },
"moveset" to Function { effectedPokemon.moveSet.toStruct() }
))
val uuid: UUID
get() = effectedPokemon.uuid
val health: Int
@@ -106,7 +82,7 @@ open class BattlePokemon(
* The [HeldItemManager] backing this [BattlePokemon].
*/
val heldItemManager: HeldItemManager by lazy { HeldItemProvider.provide(this) }
val contextManager = ContextManager()
open fun getName(): MutableComponent {
@@ -124,11 +100,11 @@ open class BattlePokemon(
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
fun canBeSentOut() =
if (actor.request?.side?.pokemon?.any { it.reviving } == true) {
!isSentOut() && !willBeSwitchedIn && health <= 0
} else {
!isSentOut() && !willBeSwitchedIn && health > 0
}
if (actor.request?.side?.pokemon?.any{ it.reviving } == true) {
!isSentOut() && !willBeSwitchedIn && health <= 0
} else {
!isSentOut() && !willBeSwitchedIn && health > 0
}
fun getIllusion(): BattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.runner
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.util.UUID
/**
@@ -30,6 +31,7 @@ interface ShowdownService {
fun getAbilityIds(): JsonArray
fun getMoves(): JsonArray
fun getItemIds(): JsonArray
fun getTypeChart(): JsonObject
fun registerSpecies()
fun registerBagItems()
fun indicateSpeciesInitialized() {}
@@ -38,4 +40,4 @@ interface ShowdownService {
companion object {
val service: ShowdownService by lazy { GraalShowdownService() }
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.io.File
import java.io.IOException
import java.net.URI
@@ -152,6 +153,12 @@ class GraalShowdownService : ShowdownService {
return gson.fromJson(arrayResult, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
val getCobbledItemIdsFn = context.getBindings("js").getMember("getTypeChart")
val arrayResult = getCobbledItemIdsFn.execute().asString()
return gson.fromJson(arrayResult, JsonObject::class.java)
}
override fun registerSpecies() {
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
val jsArray = this.context.eval("js", "new Array();")
@@ -65,8 +65,8 @@ class GraalShowdownUnbundler {
}
if (extract) {
FileUtils.copyInternalToExternal("data/${Cobblemon.MODID}/showdown.zip", showdownZip.toPath())
FileUtils.copyInternalToExternal("data/${Cobblemon.MODID}/showdown.json", showdownMetadataFile.toPath())
FileUtils.copyInternalToExternal("/data/${Cobblemon.MODID}/showdown.zip", showdownZip.toPath())
FileUtils.copyInternalToExternal("/data/${Cobblemon.MODID}/showdown.json", showdownMetadataFile.toPath())
FileUtils.unzipFile(showdownZip.toPath(), showdownDir.toPath())
showdownZip.delete()
}
@@ -75,7 +75,7 @@ class GraalShowdownUnbundler {
private fun loadShowdownMetadata() : ShowdownMetadata? {
try {
val inputStream = javaClass.classLoader.getResourceAsStream("data/${Cobblemon.MODID}/showdown.json")!!
val inputStream = javaClass.getResourceAsStream("/data/${Cobblemon.MODID}/showdown.json")!!
return gson.fromJson<ShowdownMetadata>(InputStreamReader(inputStream))
} catch (exception: Exception) {
exception.printStackTrace()
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStreamWriter
@@ -123,6 +124,13 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
return gson.fromJson(response, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
writer.write(">getTypeChart")
writer.flush()
val response = readMessage()
return gson.fromJson(response, JsonObject::class.java)
}
private fun sendSpeciesData(species: Species, form: FormData?) {
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
acknowledge()
@@ -26,7 +26,6 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.world.InteractionHand
@@ -55,7 +54,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Properties) : BaseEntityBlock(settings), BonemealableBlock, Mulchable, ShearableBlock {
class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Properties) : BaseEntityBlock(settings), BonemealableBlock, Mulchable {
private val lookupDirections = setOf(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)
@@ -187,11 +186,12 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
return InteractionResult.PASS
} else if (this.isMaxAge(state)) {
return if (harvestBerry(world, state, pos, player)) {
InteractionResult.sidedSuccess(world.isClientSide)
} else {
InteractionResult.PASS
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return InteractionResult.PASS
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.popResource(world, pos, drop)
}
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
return InteractionResult.sidedSuccess(world.isClientSide)
}
return super.useWithoutItem(state, world, pos, player, blockHitResult)
}
@@ -334,24 +334,4 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
}
}
}
override fun attemptShear(world: Level, state: BlockState, pos: BlockPos, successCallback: () -> Unit): Boolean {
return if (this.isMaxAge(state)) {
harvestBerry(world, state, pos)
} else {
false
}
}
private fun harvestBerry(world: Level, state: BlockState, pos: BlockPos, player: Player? = null): Boolean {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.popResource(world, pos, drop)
}
val sound = if (player != null) CobblemonSounds.BERRY_HARVEST else SoundEvents.SHEEP_SHEAR
world.playSound(null, pos, sound, SoundSource.BLOCKS, 1F, 1F)
return true
}
}
@@ -94,18 +94,12 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
return result
}
override fun onRemove(
state: BlockState,
world: Level,
pos: BlockPos,
newState: BlockState,
moved: Boolean
) {
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
if (!entity.getStack().isEmpty) {
Containers.dropContentsOnDestroy(state, newState, world, pos)
if (!entity.getStack().isEmpty && !player.isCreative) {
Containers.dropContents(world, pos, entity.inv)
}
super.onRemove(state, world, pos, newState, moved)
return super.playerWillDestroy(world, pos, state, player)
}
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
@@ -55,6 +55,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
val CODEC = simpleCodec(::PCBlock)
val PART = EnumProperty.create("part", PCPart::class.java)
val ON = BooleanProperty.create("on")
val WATERLOGGED = BooleanProperty.create("waterlogged")
val NATURAL = BooleanProperty.create("natural")
private val NORTH_AABB_TOP = Shapes.or(
@@ -234,7 +235,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
@Deprecated("Deprecated in Java")
override fun isPathfindable(
blockState: BlockState,
blockState: BlockState?,
pathComputationType: PathComputationType
): Boolean = false
@@ -225,7 +225,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
* @param player The [Player] harvesting the tree.
* @return The resulting [ItemStack]s to be dropped.
*/
fun harvest(world: Level, state: BlockState, pos: BlockPos, player: Player?): Collection<ItemStack> {
fun harvest(world: Level, state: BlockState, pos: BlockPos, player: Player): Collection<ItemStack> {
val drops = arrayListOf<ItemStack>()
val unique = this.growthPoints.groupingBy { it }.eachCount()
unique.forEach { (identifier, amount) ->
@@ -248,10 +248,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
return drops
}
fun harvest(world: Level, state: BlockState, pos: BlockPos): Collection<ItemStack>{
return harvest(world, state, pos, null);
}
override fun loadAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider) {
this.berryIdentifier = ResourceLocation.parse(nbt.getString(BERRY).takeIf { it.isNotBlank() } ?: "cobblemon:pecha")
this.wasLoading = true
@@ -334,7 +330,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
this.setChanged()
}
private fun refresh(world: Level, pos: BlockPos, state: BlockState, player: Player?) {
private fun refresh(world: Level, pos: BlockPos, state: BlockState, player: Player) {
val newState = state.setValue(BerryBlock.AGE, 3)
world.setBlock(pos, newState, UPDATE_CLIENTS)
world.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
@@ -118,7 +118,7 @@ class RestorationTankBlockEntity(
}
override fun getSlotsForFace(side: Direction): IntArray {
return if (side == Direction.DOWN) intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) else intArrayOf()
return if (side == Direction.DOWN) intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) else intArrayOf(-1)
}
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, direction: Direction?): Boolean {
@@ -155,7 +155,7 @@ class FossilMultiblockStructure (
// Reclaim the last fossil from the machine if their hand is empty
if (player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty) {
if(!this.isRunning() && !this.hasCreatedPokemon) {
if(!this.isRunning() && this.hasCreatedPokemon) {
if (fossilInventory.isEmpty()) {
return InteractionResult.CONSUME
}
@@ -17,9 +17,9 @@ import net.minecraft.world.item.alchemy.Potions
object BrewingRecipes {
val recipes: List<Triple<CobblemonIngredient, Item, Item>> by lazy {
val recipes: List<Triple<CobblemonIngredient, CobblemonIngredient, Item>> by lazy {
listOf(
Triple(CobblemonPotionIngredient(Potions.WATER), CobblemonItems.MEDICINAL_LEEK, CobblemonItems.MEDICINAL_BREW),
Triple(CobblemonPotionIngredient(Potions.WATER), CobblemonItemIngredient(CobblemonItems.MEDICINAL_LEEK), CobblemonItems.MEDICINAL_BREW),
convert(CobblemonItems.MEDICINAL_BREW, CobblemonItems.LEPPA_BERRY, CobblemonItems.ETHER),
convert(CobblemonItems.MEDICINAL_BREW, CobblemonItems.HOPO_BERRY, CobblemonItems.ELIXIR),
convert(CobblemonItems.MEDICINAL_BREW, CobblemonItems.ORAN_BERRY, CobblemonItems.POTION),
@@ -50,8 +50,8 @@ object BrewingRecipes {
)
}
private fun convert(input: Item, ingredient: Item, output: Item): Triple<CobblemonIngredient, Item, Item> {
return Triple(CobblemonItemIngredient(input), ingredient, output)
private fun convert(input: Item, ingredient: Item, output: Item): Triple<CobblemonIngredient, CobblemonIngredient, Item> {
return Triple(CobblemonItemIngredient(input), CobblemonItemIngredient(ingredient), output)
}
}
@@ -8,16 +8,12 @@
package com.cobblemon.mod.common.client
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonClientImplementation
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
@@ -25,13 +21,7 @@ import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
@@ -39,14 +29,7 @@ import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererR
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.*
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
@@ -103,6 +86,7 @@ object CobblemonClient {
GenericBedrockEntityModelRepository
)
val overlay: PartyOverlay by lazy { PartyOverlay() }
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
@@ -1,23 +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.client
import com.cobblemon.mod.common.ResourcePackActivationBehaviour
import net.minecraft.network.chat.Component
import net.minecraft.server.packs.PackType
class CobblemonPack(
val id: String,
val name: String,
val packType: PackType,
val activationBehaviour: ResourcePackActivationBehaviour,
val neededMods: Set<String> = setOf()
) {
val displayName = Component.literal(name)
}
@@ -14,7 +14,6 @@ import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addEntityFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
@@ -44,6 +43,8 @@ import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
companion object {
@@ -192,18 +193,15 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
val ballType =
currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_", "")
val mode = if (currentEntity.isBattling) "battle" else "casual"
//TODO: A lot of this is probably able to be simplified by just using a single particle with events
//Do it in the particle file, not code.
val sendflash =
BedrockParticleOptionsRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = PoseStack()
//This could potentially cause strange issues if the matrix has a translation component here.
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.last().pose())
wrapper.updatePosition(Vec3(newPos.x, newPos.y, newPos.z))
val world = Minecraft.getInstance().level ?: return@let
ParticleStorm(effect, wrapper, wrapper, world).spawn()
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks =
BedrockParticleOptionsRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle =
@@ -214,7 +212,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}
@@ -222,7 +219,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}
@@ -233,7 +229,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}

Some files were not shown because too many files have changed in this diff Show More