Compare commits

...
113 Commits
Author SHA1 Message Date
apion 263bccca4b idk whats up here but I wanna swap off this branch so 2025-02-07 17:49:10 -05:00
apion 1d94adface Fabric now boots 2025-01-26 03:02:57 -05:00
apion c741ddbc2e Comments 2025-01-25 04:23:40 -05:00
apion 8cad44ffb1 NEOFORGE IS BOOTING INTO A WORLD!!! 2025-01-25 04:05:34 -05:00
apion dec0376d11 More MDG testing 2025-01-18 15:46:50 -05:00
apion 007b2a49f4 More ModDevGradle porting 2025-01-18 01:19:26 -05:00
apion 082683723d Initial moddevgradle port 2025-01-17 21:31:25 -05:00
Paul bbd2b3aeed Updated sprites for galarica nuts, cuff, wreath 2025-01-13 08:37:39 +10:00
Hiroku 33951c868b Merge branch 'main' into 'main'
Fix Potions from applying more health than intended

See merge request cable-mc/cobblemon!1461
2025-01-12 00:23:35 +00:00
Chase 02f0e62bf8 Edit CHANGELOG.md 2025-01-12 00:01:08 +00:00
Hiroku 977b39e65c Merge branch '1.6.1-molang-expansion' into 'main'
[1.6.1] Add flows and expand some structs, also add FormeChangeEvent

See merge request cable-mc/cobblemon!1444
2025-01-11 22:55:33 +00:00
Chase 25a02e0b27 Small tweak to logic 2025-01-11 15:55:12 +00:00
Chase 4950297daa Remove unused variable 2025-01-11 15:52:26 +00:00
Chase 1c09cf6f30 Fix potion items from calling heal function twice 2025-01-11 15:50:56 +00:00
Paul 00d72902af Added recipes for galarica cuff and wreath 2025-01-11 17:41:29 +10:00
Paul eb986087cf Pipeline fixo (as if by magic!!!) 2025-01-11 16:31:39 +10:00
Paul 294e439f5e Moved the repurposed structures stuff to an inbuilt pack 2025-01-11 15:56:55 +10:00
Paul 6e7e870a1f Added galarica nut item 2025-01-11 12:09:29 +10:00
Hiroku ab78185689 Merge branch 'main' into 'main'
Add Built-in 1.21.1+ Compat With Repurposed Structures

See merge request cable-mc/cobblemon!1457
2025-01-10 20:40:00 +00:00
amo 7403556fa2 fixes for threads 2025-01-10 13:22:50 +00:00
Paul 7f451f1989 Cleaned up duplicates in changelog 2025-01-10 20:36:26 +10:00
Hiroku 3b2bfd5548 Merge branch 'display-cases' into 'main'
Fixed display cases not dropping items if destroyed via explosions

See merge request cable-mc/cobblemon!1460
2025-01-10 09:27:10 +00:00
Hiroku 3d1ee5ee0f Merge branch 'lion-eats-bugs' into 'main'
Small Fixes

Closes #1329, #1324, #1318, and #1325

See merge request cable-mc/cobblemon!1455
2025-01-10 09:13:36 +00:00
Guilherme Pereira 0584276189 emit pokemon aspects changed event only when pokemon is owned 2025-01-10 06:01:59 -03:00
Hiroku a16b17582d Merge branch 'feat/dynamic-pokemon-name-render' into 'main'
Better Pokédex discovery system

See merge request cable-mc/cobblemon!1442
2025-01-10 04:20:53 +00:00
Hiroku 71c1ae5540 Merge branch 'palafin-hero' into 'main'
Add features to several mons

See merge request cable-mc/cobblemon!1439
2025-01-10 04:10:42 +00:00
Hiroku 0233d2b0be Merge branch 'feature/migration-post-mortem' into 'main'
fix moves having raised stages that are beyond what new codecs expect when migrating from 1.5.2

See merge request cable-mc/cobblemon!1453
2025-01-10 03:55:28 +00:00
PolymetaandHiroku b67d474386 fix moves having raised stages that are beyond what new codecs expect when migrating from 1.5.2 2025-01-10 03:55:28 +00:00
Test 3239362e1a fix incorrect texture size for interact interface 2025-01-08 23:41:52 -05:00
Conan 0d240f01b6 Fixed display cases not dropping items if destroyed via explosions. 2025-01-09 02:55:27 +01:00
Test 99a71dc2e1 restore pokedex close transition 2025-01-06 20:20:52 -05:00
Paul fd99573a46 Fixed NPC editing GUI not immediately applying aspect changes on save 2025-01-05 18:16:52 +10:00
Guilherme Pereira fbcae404c0 update CHANGELOG.md 2025-01-05 03:11:27 -03:00
Guilherme Pereira 68cfd16fed emit pokemon aspects changed event only when aspects really changed 2025-01-05 03:10:13 -03:00
Guilherme Pereira 818038c715 fix hay bales not going into restoration machines 2025-01-05 03:09:47 -03:00
TelepathicGrunt 7c4b31db25 Add Built-in Compat With Repurposed Structures 2025-01-04 19:22:10 -05:00
Guilherme Pereira da6a88292e fix berry shear sound 2025-01-04 16:54:17 -03:00
Guilherme Pereira dee9a268d7 update CHANGELOG.md 2025-01-04 14:38:32 -03:00
Guilherme Pereira 5fd37438fc add player to pokerod reel event 2025-01-04 14:35:00 -03:00
Guilherme Pereira 51b30ab468 close pokedex and dialogues on inventory keybind press 2025-01-04 14:27:08 -03:00
Guilherme Pereira 36a3549b3e fix petilil typo 2025-01-04 13:03:31 -03:00
Guilherme Pereira 9e0ea63db6 fix vivichoke dip and leek and potato stew not returning bowls 2025-01-04 12:56:18 -03:00
Guilherme Pereira 739459c061 fix wooloo dex registration 2025-01-04 12:51:13 -03:00
Polymeta a6e06c80fe flipping more signs for particles
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-01-03 13:18:45 +00:00
Polymeta fc1cc54107 Merge branch 'BanInBattleRocketry' into 'main'
Ban in battle rocketry

See merge request cable-mc/cobblemon!1451
2025-01-03 12:07:49 +00:00
JazzMcNade 210bf9f5fb Updated Changelog 2025-01-02 21:13:01 -06:00
JazzMcNade 9454f2046a Addition comments 2025-01-02 20:58:24 -06:00
JazzMcNade 5621e69d58 Merge branch 'main' into BanInBattleRocketry 2025-01-02 20:51:11 -06:00
Polymeta 0c0f022e34 Merge branch 'fix-pokedex-command' into 'main'
Pokédex command and GUI fixes

Closes #1320 and #1321

See merge request cable-mc/cobblemon!1450
2025-01-02 16:58:35 +00:00
lionandPolymeta 220b363864 Pokédex command and GUI fixes 2025-01-02 16:58:35 +00:00
Polymeta 26d97f2043 Merge branch 'add-base-scale-npc-property' into 'main'
Add 'baseScale' NPC property

Closes #1268

See merge request cable-mc/cobblemon!1448
2024-12-31 14:54:38 +00:00
lionandPolymeta c4f5416bb3 Add 'baseScale' NPC property 2024-12-31 14:54:38 +00:00
Polymeta 8066a92ab5 Merge branch 'lion-eats-bugs' into 'main'
Small Fixes

Closes #1311, #889, and #1197

See merge request cable-mc/cobblemon!1449
2024-12-31 14:42:00 +00:00
lionandPolymeta f149439da8 Small Fixes 2024-12-31 14:41:59 +00:00
JazzMcNade 978064d2d0 Update PokemonMoveControl.kt 2024-12-31 02:54:49 -06:00
Polymeta 95dd6cbfcb Merge branch 'test/adorn_compat' into 'main'
Fixing up Adorn compatibility

See merge request cable-mc/cobblemon!1441
2024-12-30 16:17:17 +00:00
aeroeng15andPolymeta 551b3ebe87 Fixing up Adorn compatibility 2024-12-30 16:17:17 +00:00
Polymeta 89914813a1 Merge branch 'fix-pokemon-forgetting-moves-when-evolving' into 'main'
Fix Pokémon forgetting moves when evolving on specific cases

Closes #1252

See merge request cable-mc/cobblemon!1437
2024-12-30 15:54:09 +00:00
lionandPolymeta 742ec131d5 Fix Pokémon forgetting moves when evolving on specific cases 2024-12-30 15:54:09 +00:00
Polymeta 4175c36388 Merge branch 'fix/no-next-level-at-cap' into 'main'
Align the "To next level" display with official games logic, it will now show 0 when at cap.

See merge request cable-mc/cobblemon!1445
2024-12-30 15:32:33 +00:00
Hiroku f6ed1b38f8 Merge branch 'make-berries-shearable' into 'main'
Made berry trees be shearable by dispenser blocks

Closes #1071

See merge request cable-mc/cobblemon!1419
2024-12-30 11:39:21 +00:00
Faintful c299967cce Updated changelog 2024-12-30 12:35:44 +01:00
Polymeta b79f569def Merge branch 'fix-pokemon-riding-two-boats' into 'main'
Fix Pokémon riding two boats

Closes #1281

See merge request cable-mc/cobblemon!1447
2024-12-30 10:54:31 +00:00
lionandPolymeta 944144a671 Fix Pokémon riding two boats 2024-12-30 10:54:31 +00:00
Polymeta 586b5a860f Merge branch 'fix-npc-player-lose-command' into 'main'
Fix npc player lose command

Closes #1306 and #1282

See merge request cable-mc/cobblemon!1443
2024-12-30 01:33:47 +00:00
lionandPolymeta 29d91fb191 Fix npc player lose command 2024-12-30 01:33:46 +00:00
Polymeta 183d85a3b6 Merge branch 'overlay-invisible-man-collab' into 'main'
fix: Party overlay not respecting hidden UI (F1)

See merge request cable-mc/cobblemon!1440
2024-12-30 00:48:56 +00:00
Polymeta 0795b06ca0 use 1.4.1 instead 1.4.0 for tags history [skip-ci]
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-12-30 00:15:21 +00:00
Polymeta 6fdd87f5cf update tags directory to keep history [skip-ci]
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-12-29 23:59:02 +00:00
Ina 3b364a4590 fix: Align the "To next level" display with official games logic, it will now show 0 when at cap. 2024-12-29 17:20:01 +00:00
Ina baca93c9b2 feat: Support for Illusion in the dex reveal process. 2024-12-29 16:54:17 +00:00
amo b6d13b8861 Merge branch 'refs/heads/main' into 1.6.1-molang-expansion
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/pokemon/BattlePokemon.kt
2024-12-29 16:42:55 +00:00
amo b271223f6a lots o' stuff 2024-12-29 16:31:25 +00:00
Ina 004f2de623 chore: Changelog 2024-12-29 15:11:45 +00:00
Ina 570629bff2 feat: Improve Pokemon reveal logic 2024-12-29 15:08:16 +00:00
Ina 7e5b8260de refactor: Simplify base label resolving logic. 2024-12-29 13:45:23 +00:00
Ina 3e5e851dba fix: Don't instance new texts for static elements every tick. 2024-12-29 11:25:50 +00:00
Ina 9f866aa292 chore: Hiro bullies the work-challenged. 2024-12-29 11:18:54 +00:00
Ina 4e4417d4df Merge branch 'main' into overlay-invisible-man-collab 2024-12-29 11:17:39 +00:00
Hiroku 900ca0cd8d Merge branch 'feature/small-fixes' into 'main'
fixes and minor cleanup

Closes #1277, #1302, and #1310

See merge request cable-mc/cobblemon!1432
2024-12-29 01:04:53 +00:00
PolymetaandHiroku 5448031cae fixes and minor cleanup 2024-12-29 01:04:53 +00:00
Ina abd92c2325 fix: Party overlay not respecting hidden UI (F1) 2024-12-28 22:07:37 +00:00
Vaniron c4948a6611 Add three_segment feature to Dudunsparce 2024-12-28 18:24:52 +00:00
Vaniron cc459c99aa Add hero features to Palafin 2024-12-28 18:15:28 +00:00
Polymeta a3ac3c36e6 Merge branch 'fix-bait-consumption' into 'main'
Fixed bait being consumed when not reeling in any Pokémon.

Closes #1308 and #1307

See merge request cable-mc/cobblemon!1438
2024-12-28 12:22:19 +00:00
lionandPolymeta 36ce262755 Fixed bait being consumed when not reeling in any Pokémon. 2024-12-28 12:22:19 +00:00
Apion 9246523853 Merge branch 'particle-stuff' into 'main'
A bunch of particle changes

See merge request cable-mc/cobblemon!1295
2024-12-28 03:03:56 +00:00
Apion db2c2f0089 A bunch of particle changes 2024-12-28 03:03:56 +00:00
Polymeta 3a090ba01c Merge branch 'RestorePokemonSpawnYaw' into 'main'
Restore pokemon spawn yaw

See merge request cable-mc/cobblemon!1436
2024-12-28 02:11:17 +00:00
JazzMcNadeandPolymeta 3c4481c9cc Restore pokemon spawn yaw 2024-12-28 02:11:17 +00:00
Polymeta 4e73a19ade Merge branch 'configurable-dex-scanning-range' into 'main'
Add config to max Pokédex scanning range

Closes #1247

See merge request cable-mc/cobblemon!1387
2024-12-28 00:33:24 +00:00
lionandPolymeta c58ba08a65 Add config to max Pokédex scanning range 2024-12-28 00:33:24 +00:00
Polymeta 18940956a8 Merge branch 'fix-pasture-pokemon-list-padding' into 'main'
Fix pasture Pokémon list padding

Closes #1273

See merge request cable-mc/cobblemon!1421
2024-12-27 23:52:04 +00:00
lionandPolymeta 68520307b4 Fix pasture Pokémon list padding 2024-12-27 23:52:04 +00:00
Hiroku 84825dbd69 Add vietnamese 2024-12-27 22:45:16 +00:00
Polymeta b231a80b68 Merge branch 'moe-eats-bugs' into 'main'
Advancement fixes

Closes #1262

See merge request cable-mc/cobblemon!1417
2024-12-27 13:04:27 +00:00
MoeBoy76andPolymeta d2796b8755 Advancement fixes 2024-12-27 13:04:27 +00:00
Polymeta 1ab3ca8628 Merge branch 'fix-collidable-invisible-pokemon' into 'main'
Fix Pokémon being collidable when trying to capture them

Closes #401

See merge request cable-mc/cobblemon!1382
2024-12-27 12:55:22 +00:00
lionandPolymeta f4da1436a5 Fix Pokémon being collidable when trying to capture them 2024-12-27 12:55:22 +00:00
Faintful 9a0b779ca3 Merge remote-tracking branch 'upstream/main' into make-berries-shearable 2024-12-27 13:40:11 +01:00
Polymeta 2bcb42270a Merge branch 'hide-unimplemented-pokemon-from-the-dex' into 'main'
Add config to hide unimplemented Pokémon from the Pokédex

Closes #1274

See merge request cable-mc/cobblemon!1422
2024-12-27 12:37:32 +00:00
lionandPolymeta 51472d2369 Add config to hide unimplemented Pokémon from the Pokédex 2024-12-27 12:37:32 +00:00
Polymeta 2cb430d927 Merge branch 'is-standing-on-blocks-molang-function' into 'main'
Add 'is_standing_on_blocks' MoLang function

See merge request cable-mc/cobblemon!1393
2024-12-27 12:36:04 +00:00
lionandPolymeta 7bbfbf5852 Add 'is_standing_on_blocks' MoLang function 2024-12-27 12:36:04 +00:00
Polymeta 93a368a844 Merge branch 'fix_fishing_condition' into 'main'
Internal change, move condition variables related to fishing into...

See merge request cable-mc/cobblemon!1430
2024-12-27 01:46:23 +00:00
Polymeta 5c934a0b66 Merge branch 'FossilMachineReturnFossilFix' into 'main'
FossilMachineReturnFossilFix

See merge request cable-mc/cobblemon!1426
2024-12-27 01:36:24 +00:00
JazzMcNadeandPolymeta 1a4034ded4 FossilMachineReturnFossilFix 2024-12-27 01:36:23 +00:00
JPAK dbd73c53ea Internal change, move condition variables related to fishing into FishingSpawningCondition.kt instead of SpawningCondition.kt 2024-12-26 11:28:11 -06:00
Polymeta 25d17a271b Merge branch 'toms-fixed' into 'main'
Fix crash when placing a restoration tank next to Tom's Storage block.

See merge request cable-mc/cobblemon!1427
2024-12-26 11:39:22 +00:00
JordanandPolymeta e9b3940eb6 Fix crash when placing a restoration tank next to Tom's Storage block. 2024-12-26 11:39:22 +00:00
Hiroku e442ca2732 Merge branch 'yamask-patch' into 'main'
Add galarian yamask as feature

See merge request cable-mc/cobblemon!1428
2024-12-26 07:40:17 +00:00
Vaniron b34e1f30d1 Add galarian yamask as feature 2024-12-26 07:33:40 +00:00
Faintful ba7a3ab3c2 Made berry trees be shearable by dispenser blocks 2024-12-24 17:27:33 +01:00
990 changed files with 57393 additions and 32164 deletions
+81 -1
View File
@@ -1,8 +1,88 @@
# 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
- Fix Pokédex sometimes crashing when switching forms
- 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)
#### "Now that there's a fishing mechanic, the mod is actually good!"
+27
View File
@@ -0,0 +1,27 @@
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
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -0,0 +1,85 @@
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)
}
}
}
@@ -0,0 +1,12 @@
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"
}
}
@@ -0,0 +1,82 @@
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"
}
}
@@ -0,0 +1,33 @@
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)
}
}
}
+3 -1
View File
@@ -1,12 +1,14 @@
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}"
}
*/
+90 -54
View File
@@ -1,68 +1,102 @@
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"
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")
}
architectury {
common("neoforge", "fabric")
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/")
}
}
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
mavenLocal()
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://maven.blamejared.com/")
maven("https://api.modrinth.com/maven")
maven(url = "../deps")
}
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
compileOnly(libs.bundles.mongo)
compileOnly(libs.bundles.kotlin)
compileOnly(libs.molang)
compileOnly(libs.graal)
compileOnly(libs.jei.api)
compileOnly(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
compileOnly("net.fabricmc:sponge-mixin:0.15.5+mixin.0.8.7")
compileOnly("io.github.llamalad7:mixinextras-fabric:0.4.1")
testImplementation(libs.bundles.unitTesting)
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
setEvents(listOf("failed"))
setExceptionFormat("full")
}
neoForge {
neoFormVersion = "1.21.1-20240808.144430"
interfaceInjectionData.from("src/main/resources/interfaces.json")
validateAccessTransformers = true
}
sourceSets {
@@ -72,7 +106,7 @@ sourceSets {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
rootProject.file("HEADER").forEachLine {
project.projectDir.parentFile.resolve("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
@@ -85,12 +119,14 @@ sourceSets {
property("license", generateLicenseHeader())
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
property("version", project.version as String)
property("isSnapshot", "true")
/*
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
@@ -0,0 +1,97 @@
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
@@ -0,0 +1,95 @@
[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,6 +9,20 @@
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;
@@ -34,4 +48,39 @@ 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();
});
}
}
}
}
@@ -68,6 +68,7 @@ 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.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
@@ -123,6 +124,7 @@ 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
@@ -162,6 +164,16 @@ 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
@@ -206,7 +218,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel).sendToPlayer(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel).sendToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -392,6 +392,9 @@ 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() }
@@ -442,6 +445,7 @@ 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()
@@ -477,7 +481,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).build()).stacksTo(1)))
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)))
@JvmField
val REVIVE = create("revive", ReviveItem(max = false))
@JvmField
@@ -33,12 +33,13 @@ class PartyCheckCriterion(
override fun matches(player: ServerPlayer, context: PlayerPartyStore): Boolean {
val matches = mutableListOf<ResourceLocation>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
if (it == "any".asIdentifierDefaultingNamespace("minecraft") || 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)
@@ -47,55 +48,3 @@ class PartyCheckCriterion(
return matches.containsAll(party) && matches.size == party.size
}
}
/*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)*/
@@ -13,6 +13,7 @@ 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
@@ -105,6 +106,8 @@ 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>()
@@ -193,6 +196,8 @@ 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>()
@@ -8,9 +8,16 @@
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]
@@ -23,4 +30,20 @@ data class BattleFaintedEvent(
override val battle: PokemonBattle,
val killed: BattlePokemon,
val context: BattleContext
) : BattleEvent
) : 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()) },
)
)
)
}
@@ -8,8 +8,12 @@
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].
@@ -20,4 +24,11 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
data class BattleFledEvent (
override val battle: PokemonBattle,
val player: PlayerBattleActor
) : BattleEvent
) : 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 }
)
}
@@ -9,7 +9,9 @@
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
/**
@@ -21,7 +23,14 @@ 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()
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 }
)
}
/**
* Event fired after a [PokemonBattle] starts.
@@ -29,4 +38,11 @@ data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent
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 }
)
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.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,13 +8,25 @@
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)
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
)
}
@@ -8,9 +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.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.
@@ -22,4 +26,13 @@ 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,12 +8,24 @@
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)
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
)
}
@@ -8,8 +8,13 @@
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
@@ -26,4 +31,11 @@ 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,10 +9,11 @@
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 rod: ItemStack) : Cancelable()
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable()
@@ -9,6 +9,7 @@
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
@@ -18,4 +19,9 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
class ThrownPokeballHitEvent(
val pokeBall : EmptyPokeBallEntity,
val pokemon : PokemonEntity
) : Cancelable()
) : Cancelable() {
val context = mutableMapOf(
"pokeball" to pokeBall.struct,
"pokemon" to pokemon.asMoLangValue()
)
}
@@ -8,9 +8,14 @@
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
@@ -18,4 +23,15 @@ 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,6 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -16,4 +17,10 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @author Hiroku
* @since August 5th, 2022
*/
class LevelUpEvent(val pokemon: Pokemon, val oldLevel: Int, var newLevel: Int)
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())
)
}
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.*
data class PokemonAspectsChangedEvent(
val ownerId: UUID?,
val pokemon: Pokemon,
)
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -16,4 +17,9 @@ 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,10 +8,16 @@
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
)
}
@@ -96,11 +96,12 @@ 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, BuiltInRegistries.ITEM.getKey(stack.item)))
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.effectedPokemon.species.resourceIdentifier, stackName))
}
}
@@ -148,4 +149,4 @@ interface PokemonSelectingItem {
return InteractionResultHolder.success(stack)
}
}
}
@@ -514,6 +514,17 @@ 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)
@@ -603,7 +614,8 @@ object MoLangFunctions {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(NPCProvider(npc)),
runtime = runtime
runtime = runtime,
level = npc.level()
)
npc.actionEffect = context
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
@@ -706,6 +718,11 @@ 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,6 +8,12 @@
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
@@ -25,6 +31,7 @@ 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.
@@ -90,6 +97,21 @@ 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
@@ -8,6 +8,8 @@
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
@@ -27,6 +29,22 @@ 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()
@@ -15,6 +15,7 @@ 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
/**
@@ -69,7 +70,8 @@ class ActionEffectContext(
val runtime: MoLangRuntime,
var canBeInterrupted: Boolean = false,
var interrupted: Boolean = false,
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf(),
var level: Level? = null
) {
inline fun <reified T> findOneProvider() = providers.filterIsInstance<T>().firstOrNull()
}
@@ -24,6 +24,7 @@ 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
@@ -32,14 +33,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 entities = context.providers
val allEntities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
.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) }
val effectIdentifier = try {
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0" } ?: effect
@@ -47,10 +59,24 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
effect
}?.asIdentifierDefaultingNamespace() ?: return skip()
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)
//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)
}
}
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
@@ -38,8 +38,9 @@ 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.94F
var modelScale: Float = 0.9375F
var battleConfiguration = NPCBattleConfiguration()
var interaction: NPCInteractConfiguration? = null
var canDespawn = true
@@ -62,6 +63,7 @@ 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)
@@ -100,6 +102,7 @@ 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,6 +26,7 @@ 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
@@ -51,6 +52,7 @@ 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,6 +32,18 @@ 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]
}
@@ -13,6 +13,7 @@ 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
@@ -112,8 +113,11 @@ class FormDexRecord {
//Used when granting all entries in dex, should figure out better way
fun addAllShinyStatesAndGenders() {
genders.addAll(listOf(Gender.MALE, Gender.FEMALE))
val form = PokemonSpecies.getByIdentifier(speciesDexRecord.id)?.getFormByName(formName)
genders.addAll(form?.possibleGenders ?: listOf(Gender.MALE, Gender.FEMALE))
seenShinyStates.addAll(listOf("shiny", "normal"))
speciesDexRecord.onFormRecordUpdated(this)
}
fun setKnowledgeProgress(newKnowledge: PokedexEntryProgress) {
@@ -50,6 +50,8 @@ 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,9 +32,10 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
if (searchString == "") return true
val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.NONE) return false
val highestKnowledgeForEntry = pokedexManager.getHighestKnowledgeFor(entry)
if (highestKnowledgeForEntry == PokedexEntryProgress.NONE) return false
when(searchByType) {
when (searchByType) {
SearchByType.ABILITIES -> {
if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.CAUGHT) return false
val abilityList = mutableListOf<String>()
@@ -130,7 +130,7 @@ interface Evolution : EvolutionLike {
owner.party().getFirstAvailablePosition() ?: return false
// Add shed Pokemon to player's party
val shedPokemon = pokemon.clone()
val shedPokemon = pokemon.clone(registryAccess = owner.registryAccess())
shedPokemon.removeHeldItem()
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
@@ -190,15 +190,23 @@ 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)
this.learnableMoves.forEach { move ->
val movesToLearn = previousSpeciesLearnableMoves + this.learnableMoves
movesToLearn.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,6 +31,7 @@ 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(),
@@ -45,18 +46,20 @@ 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, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, space, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
BedrockParticleEmitter(
startExpressions = startExpressions,
updateExpressions = updateExpressions,
shape = shape,
rate = rate,
lifetime = lifetime,
space = space,
eventTimeline = eventTimeline,
creationEvents = creationEvents,
expirationEvents = expirationEvents,
@@ -0,0 +1,61 @@
/*
* 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,6 +26,7 @@ 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,
@@ -77,9 +78,11 @@ class ParticleEvent(
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
particleEffect?.let { effect ->
val bedrockParticleOptions = BedrockParticleOptionsRepository.getEffect(effect.effect) ?: return@let
val rootMatrix = when (effect.type) {
EventParticleOptions.EventParticleType.EMITTER-> MatrixWrapper().updatePosition(storm.matrixWrapper.position).updateMatrix(storm.matrixWrapper.matrix)
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
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()
EventParticleOptions.EventParticleType.PARTICLE,
EventParticleOptions.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let {
Vec3(
@@ -87,7 +90,19 @@ class ParticleEvent(
it.getY(),
it.getZ()
)
} ?: Vec3(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
} ?: 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
}
val sourceVelocity = when (effect.type) {
@@ -105,17 +120,24 @@ class ParticleEvent(
val newStorm = ParticleStorm(
effect = bedrockParticleOptions,
matrixWrapper = rootMatrix,
emitterSpaceMatrix = particleMatrix,
locatorSpaceMatrix = locatorMatrix,
world = storm.world,
sourceVelocity = sourceVelocity,
sourceAlive = storm.sourceAlive,
sourceVisible = storm.sourceVisible,
onDespawn = {},
runtime = MoLangRuntime().setup().also { it.environment.query = storm.runtime.environment.query },
targetPos = storm.targetPos,
runtime = MoLangRuntime().setup(),
entity = storm.entity
)
effect.expression?.resolve(newStorm.runtime)
//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)
newStorm.spawn()
}
@@ -77,16 +77,17 @@ 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 stormPosition = Vec3(
particle.storm.getX(),
particle.storm.getY(),
particle.storm.getZ()
val particlePositionWS = Vec3(
particle.getX(),
particle.getY(),
particle.getZ()
)
val offset = runtime.resolveVec3d(this.offset)
val particlePosition =
Vec3(particle.getX(), particle.getY(), particle.getZ())
val desiredPosition = stormPosition.add(offset)
return desiredPosition.subtract(particlePosition)
val matrix = particle.matrixWrapper
val targetPosPS = runtime.resolveVec3d(offset)
val targetPosWS = matrix.transformPosition(targetPosPS)
return targetPosWS.subtract(particlePositionWS)
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3, minSpeed: Float) = runtime.resolveVec3d(direction).normalize()
@@ -277,7 +278,7 @@ class CustomMotionDirection(
runtime.resolveDouble(direction.second),
runtime.resolveDouble(direction.third)
)
return storm.matrixWrapper.matrix.transformDirection(v)
return storm.emitterSpaceMatrix.matrix.transformDirection(v)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -11,6 +11,7 @@ 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
@@ -27,6 +28,10 @@ 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,10 +59,6 @@ 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
@@ -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::class.java.getResourceAsStream(INTERNAL_PATH)!!)
val reader = InputStreamReader(Cobblemon.javaClass.classLoader.getResourceAsStream(INTERNAL_PATH)!!)
val config = GSON.fromJson(reader, BestSpawnerConfig::class.java)
reader.close()
return config
@@ -8,12 +8,14 @@
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
@@ -147,10 +149,14 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
operator fun get(uuid: UUID) = find { it.uuid == uuid }
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {}
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {
Cobblemon.LOGGER.error("Failed to read unknown species: ${nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)}")
}
abstract fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess): CompoundTag
abstract fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess): PokemonStore<T>
open fun handleInvalidSpeciesJSON(json: JsonObject) {}
open fun handleInvalidSpeciesJSON(json: JsonObject) {
Cobblemon.LOGGER.error("Failed to read unknown species: ${json.get(DataKeys.POKEMON_SPECIES_IDENTIFIER).asString}")
}
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()) {
return if (playerFile.exists() && playerFile.length() > 0L) {
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()) {
return if (playerFile.exists() && playerFile.length() > 0L) {
val input = NbtIo.read(playerFile.toPath())
val decodeResult = NbtOps.INSTANCE.withDecoder(codec).apply(input)
decodeResult.getPartialOrThrow {
Cobblemon.LOGGER.error("Error decoding pokedex for player uuid ${uuid}")
Cobblemon.LOGGER.error("Error decoding $subfolder for player uuid $uuid")
Cobblemon.LOGGER.error(it)
throw UnsupportedOperationException()
}.first
@@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3
import java.util.*
/**
* Responsible for managing [BattleChallenge]s and initiating PVP [PokemonBattle]s.
* Responsible for managing [BattleChallenge]s and initiating PVP [com.cobblemon.mod.common.api.battles.model.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
@@ -54,7 +54,7 @@ object ShowdownInterpreter {
}
listOf(
"player", "teamsize", "gametype", "gen", "tier", "rated", "clearpoke", "poke", "teampreview", "start", "rule", "t:", "", "capture"
"player", "teamsize", "gametype", "gen", "tier", "rated", "clearpoke", "poke", "teampreview", "start", "rule", "t:", "", "capture", "-anim"
).forEach { updateInstructionParser[it] = { _, _, _, _ -> IgnoredInstruction() } }
updateInstructionParser["-ability"] = { _, instructionSet, message, _ -> AbilityInstruction(instructionSet, message) }
@@ -9,7 +9,6 @@
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,7 +63,8 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
providers = providers,
level = battle.players.firstOrNull()?.level()
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -56,7 +56,8 @@ class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val is
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
providers = providers,
level = battle.players.firstOrNull()?.level()
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -52,7 +52,8 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
providers = providers,
level = battle.players.firstOrNull()?.level()
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -114,7 +114,8 @@ class DamageInstruction(
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
providers = providers,
level = battle.players.firstOrNull()?.level()
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -10,6 +10,8 @@ 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
@@ -35,6 +37,7 @@ class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction
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
@@ -102,7 +102,8 @@ class MoveInstruction(
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
providers = providers,
level = battle.players.firstOrNull()?.level()
)
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
@@ -13,6 +13,8 @@ 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
@@ -213,7 +215,12 @@ 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.actors.filter { it != actor }.forEach {
it.sendMessage(publicLang)
}
battle.playerUUIDs.forEach { uuid ->
CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, publicPokemon))
}
}
}
@@ -13,7 +13,6 @@ 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
@@ -70,11 +69,6 @@ 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,7 +8,11 @@
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
@@ -24,8 +28,10 @@ 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,
@@ -33,9 +39,11 @@ open class BattlePokemon(
val postBattleEntityOperation: (PokemonEntity) -> Unit = {}
) {
lateinit var actor: BattleActor
companion object {
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
val effectedPokemon = pokemon.clone()
//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"))
BattleCloneProperty.isBattleClone().apply(effectedPokemon)
UncatchableProperty.uncatchable().apply(effectedPokemon)
return BattlePokemon(
@@ -54,6 +62,22 @@ 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
@@ -82,7 +106,7 @@ open class BattlePokemon(
* The [HeldItemManager] backing this [BattlePokemon].
*/
val heldItemManager: HeldItemManager by lazy { HeldItemProvider.provide(this) }
val contextManager = ContextManager()
open fun getName(): MutableComponent {
@@ -100,11 +124,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
}
@@ -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.getResourceAsStream("/data/${Cobblemon.MODID}/showdown.json")!!
val inputStream = javaClass.classLoader.getResourceAsStream("data/${Cobblemon.MODID}/showdown.json")!!
return gson.fromJson<ShowdownMetadata>(InputStreamReader(inputStream))
} catch (exception: Exception) {
exception.printStackTrace()
@@ -26,6 +26,7 @@ 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
@@ -54,7 +55,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 {
class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Properties) : BaseEntityBlock(settings), BonemealableBlock, Mulchable, ShearableBlock {
private val lookupDirections = setOf(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)
@@ -186,12 +187,11 @@ 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)) {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return InteractionResult.PASS
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.popResource(world, pos, drop)
return if (harvestBerry(world, state, pos, player)) {
InteractionResult.sidedSuccess(world.isClientSide)
} else {
InteractionResult.PASS
}
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,4 +334,24 @@ 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,12 +94,18 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
return result
}
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
override fun onRemove(
state: BlockState,
world: Level,
pos: BlockPos,
newState: BlockState,
moved: Boolean
) {
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
if (!entity.getStack().isEmpty && !player.isCreative) {
Containers.dropContents(world, pos, entity.inv)
if (!entity.getStack().isEmpty) {
Containers.dropContentsOnDestroy(state, newState, world, pos)
}
return super.playerWillDestroy(world, pos, state, player)
super.onRemove(state, world, pos, newState, moved)
}
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
@@ -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,6 +248,10 @@ 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
@@ -330,7 +334,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(-1)
return if (side == Direction.DOWN) intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) else intArrayOf()
}
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
}
@@ -14,7 +14,6 @@ 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.ResourcePackActivationBehaviour
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
@@ -104,14 +103,6 @@ object CobblemonClient {
GenericBedrockEntityModelRepository
)
@JvmStatic
val builtinResourcePacks = listOf<CobblemonResourcePack>(
CobblemonResourcePack(id = "adorncompatibility", name = "Adorn Compatibility", activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED, neededMods = setOf("adorn")),
CobblemonResourcePack(id = "gyaradosjump", name = "Gyarados Jump Patterns", activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED),
CobblemonResourcePack(id = "regionbiasforms", name = "Region Bias Forms", activationBehaviour = ResourcePackActivationBehaviour.DEFAULT_ENABLED),
CobblemonResourcePack(id = "uniqueshinyforms", name = "Shinies for Magikarp Jump", activationBehaviour = ResourcePackActivationBehaviour.NORMAL),
)
val overlay: PartyOverlay by lazy { PartyOverlay() }
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
@@ -10,10 +10,12 @@ 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 CobblemonResourcePack(
class CobblemonPack(
val id: String,
val name: String,
val packType: PackType,
val activationBehaviour: ResourcePackActivationBehaviour,
val neededMods: Set<String> = setOf()
) {
@@ -14,6 +14,7 @@ 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
@@ -43,8 +44,6 @@ 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 {
@@ -193,15 +192,18 @@ 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()
matrix.translate(newPos.x, newPos.y, newPos.z)
//This could potentially cause strange issues if the matrix has a translation component here.
wrapper.updateMatrix(matrix.last().pose())
wrapper.updatePosition(Vec3(newPos.x, newPos.y, newPos.z))
val world = Minecraft.getInstance().level ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
ParticleStorm(effect, wrapper, wrapper, world).spawn()
val ballsparks =
BedrockParticleOptionsRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle =
@@ -212,6 +214,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}
@@ -219,6 +222,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}
@@ -229,6 +233,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ParticleStorm(
effect,
wrapper,
wrapper,
world
).spawn()
}
@@ -92,7 +92,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
if (!screenExemptions.contains(minecraft.screen?.javaClass as Class<out Screen>))
return
}
if (minecraft.debugOverlay.showDebugScreen()) {
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) {
return
}
// Hiding if toggled via Keybind
@@ -28,13 +28,10 @@ import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueDTO
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.screens.Screen
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable {
@@ -230,4 +227,13 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
override fun onClose() {
EscapeDialoguePacket().sendToServer()
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (isInventoryKeyPressed(minecraft, keyCode, scanCode) && focused !is EditBox) {
onClose()
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
}
@@ -113,13 +113,17 @@ class InteractWheelButton(
override fun playDownSound(soundManager: SoundManager) {}
private fun isHovered(mouseX: Float, mouseY: Float): Boolean {
val xMin = x.toFloat()
val xMax = xMin + BUTTON_SIZE
val yMin = y.toFloat()
val yMax = yMin + BUTTON_SIZE
val xMin = x.toFloat() + 1
val xMax = xMin + BUTTON_SIZE - 2
val yMin = y.toFloat() + 1
val yMax = yMin + BUTTON_SIZE - 2
return canHover(mouseX.toDouble(), mouseY.toDouble()) && mouseX in xMin..xMax && mouseY in yMin..yMax
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
return if (isHovered(mouseX.toFloat(), mouseY.toFloat())) super.mouseClicked(mouseX, mouseY, button) else false
}
override fun getTooltip(): Tooltip? {
tooltipText?.let { return Tooltip.create(Component.translatable(it)) } ?: return super.getTooltip()
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.interact.wheel
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.interact.wheel.InteractWheelButton.Companion.BUTTON_SIZE
import com.cobblemon.mod.common.client.gui.startselection.widgets.preview.ArrowButton
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.Multimap
@@ -23,8 +24,7 @@ import kotlin.math.max
class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title), CobblemonRenderable {
companion object {
const val SIZE = 140
const val OPTION_SIZE = 71
const val SIZE = 138
private val backgroundResource = cobblemonResource("textures/gui/interact/interact_base.png")
private val buttonResources = mutableMapOf(
Orientation.TOP_LEFT to cobblemonResource("textures/gui/interact/button_left_top.png"),
@@ -142,9 +142,9 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
val (x, y) = getDimensions()
return when (orientation) {
Orientation.TOP_LEFT -> Pair(x, y)
Orientation.TOP_RIGHT -> Pair(x + OPTION_SIZE, y)
Orientation.BOTTOM_LEFT -> Pair(x, y + OPTION_SIZE)
Orientation.BOTTOM_RIGHT -> Pair(x + OPTION_SIZE, y + OPTION_SIZE)
Orientation.TOP_RIGHT -> Pair(x + BUTTON_SIZE, y)
Orientation.BOTTOM_LEFT -> Pair(x, y + BUTTON_SIZE)
Orientation.BOTTOM_RIGHT -> Pair(x + BUTTON_SIZE, y + BUTTON_SIZE)
}
}
@@ -23,6 +23,7 @@ import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ContainerObjectSelectionList
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.client.gui.components.WidgetTooltipHolder
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarratableEntry
@@ -124,6 +125,9 @@ class ConfigVariableList(
var textValue = value
var booleanValue = value.let { it.toDoubleOrNull() == 1.0 || it.toBooleanStrictOrNull() == true }
val tooltip = Tooltip.create(variable.description)
val tooltipHolder = WidgetTooltipHolder().also { it.set(tooltip) }
val editBox = EditBox(
client.font,
parent.listX,
@@ -133,7 +137,6 @@ class ConfigVariableList(
variable.displayName,
).also {
it.isBordered = false
it.tooltip = Tooltip.create(variable.description)
it.height = SLOT_HEIGHT - 16
it.setMaxLength(250)
it.value = value
@@ -145,6 +148,7 @@ class ConfigVariableList(
it.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() || value == "." || value == "-" }
}
}
val toggleButton = NPCEditorButton(
parent.listX.toFloat(),
parent.listY + 6F,
@@ -156,7 +160,7 @@ class ConfigVariableList(
parent.parent.dto.variables[variable.variableName] = if (booleanValue) "1" else "0"
(it as NPCEditorButton).cycleButtonState = booleanValue
}.also {
it.tooltip = Tooltip.create(variable.description)
it.tooltip = tooltip
}
init {
@@ -211,6 +215,10 @@ class ConfigVariableList(
editBox.x = x + 4
editBox.y = y + 18
editBox.render(context, mouseX, mouseY, partialTicks)
// Manually renders the tooltip for the edit box because disabling its border messes everything up
val isEditBoxHovered = mouseX >= x && mouseX <= x + SLOT_WIDTH - 1 && mouseY >= y + 15 && mouseY <= y + 15 + 14
tooltipHolder.refreshTooltipForNextRenderPass(isEditBoxHovered, editBox.isFocused, editBox.rectangle)
}
}
}
@@ -192,7 +192,8 @@ class PasturePokemonScrollList(
partialTicks: Float
) {
val x = rowLeft - 4
val y = rowTop
val y = rowTop + 2
val matrixStack = context.pose()
blitk(
matrixStack = matrixStack,
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.pokedex
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
@@ -40,22 +41,17 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_DROPS
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ICON_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
import com.cobblemon.mod.common.client.gui.pokedex.widgets.AbilitiesWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DescriptionWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DropsScrollingWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.EntriesScrollingWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.PokemonInfoWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.SearchWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.SizeWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.StatsWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.Renderable
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarratableEntry
@@ -411,6 +407,13 @@ class PokedexGUI private constructor(
var entries = filteredPokedex
.flatMap { it.getEntries() }
if (Cobblemon.config.hideUnimplementedPokemonInThePokedex) {
entries = entries.filter {
val species = PokemonSpecies.getByIdentifier(it.speciesId) ?: return@filter false
return@filter species.implemented
}
}
for (filter in getFilters()) {
entries = entries.filter { filter.test(it) }
}
@@ -580,6 +583,15 @@ class PokedexGUI private constructor(
override fun isPauseScreen(): Boolean = false
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (isInventoryKeyPressed(minecraft, keyCode, scanCode) && focused !is EditBox) {
onClose()
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
fun playSound(soundEvent: SoundEvent) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
}
@@ -113,6 +113,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
var selectedFormIndex: Int = 0
var type: Array<ElementalType?> = arrayOf(null, null)
var seenShinyStates: Set<String> = setOf()
var shiny = false
var maleRatio = -1F
var gender: Gender = Gender.GENDERLESS
@@ -207,9 +208,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
20,
buttonNone,
clickAction = {
val currentEntry = currentEntry ?: return@ScaledButton
val currentForm = currentEntry.forms[selectedFormIndex]
val seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(currentEntry, currentForm)
if (seenShinyStates.size > 1) {
shiny = !shiny
updateAspects()
@@ -536,11 +534,11 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
}
selectedFormIndex = 0
setupButtons(pokedexEntry, forms[selectedFormIndex])
if (visibleForms.isNotEmpty()) {
setupButtons(pokedexEntry, visibleForms[selectedFormIndex])
updateAspects()
} else {
setupButtons(pokedexEntry, forms[selectedFormIndex])
type = arrayOf(null, null)
}
}
@@ -551,7 +549,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
variationButtons.forEach { removeWidget(it.getWidget()) }
variationButtons.clear()
val seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
shiny = seenShinyStates.count() == 1 && seenShinyStates.first() == "shiny"
shinyButton.resource = if (shiny) buttonShiny else buttonNone
shinyButton.visible = shiny || seenShinyStates.size > 1
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNatureTextIfMinted
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
@@ -89,7 +90,12 @@ class InfoWidget(
typeWidget.render(context, pMouseX, pMouseY, pPartialTicks)
// Original Trainer
val otName: MutableComponent = Component.literal(pokemon.originalTrainerName ?: "")
val otName: MutableComponent = pokemon.originalTrainerName?.let { name ->
Component.literal(name)
} ?: pokemon.originalTrainer?.let { trainer ->
Component.literal("${trainer.take(15)}...")
} ?: Component.literal("")
val otWidget = InfoOneLineWidget(
pX = x,
pY = y + 3 * ROW_HEIGHT,
@@ -164,8 +170,9 @@ class InfoWidget(
val experience = pokemon.experience.toString().text()
val experienceForThisLevel =
pokemon.experience - if (pokemon.level == 1) 0 else pokemon.experienceGroup.getExperience(pokemon.level)
val nextLevel = (pokemon.level + 1).coerceAtMost(ServerSettings.maxPokemonLevel)
val experienceToNext =
pokemon.experienceGroup.getExperience(pokemon.level + 1) - pokemon.experienceGroup.getExperience(pokemon.level)
pokemon.experienceGroup.getExperience(nextLevel) - pokemon.experienceGroup.getExperience(pokemon.level)
drawScaledText(
context = context,
@@ -176,10 +183,11 @@ class InfoWidget(
shadow = true
)
val experienceToNextText = Component.literal(experienceToNext.toString())
drawScaledText(
context = context,
text = experienceToNext.toString().text(),
x = (x + 127) - (mcFont.width(experienceToNext.toString().text()) * smallTextScale),
text = experienceToNextText,
x = (x + 127) - (mcFont.width(experienceToNextText) * smallTextScale),
y = y + 137,
scale = smallTextScale,
shadow = true
@@ -9,8 +9,6 @@
package com.cobblemon.mod.common.client.gui.toast
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.components.toasts.ToastComponent
@@ -21,7 +19,6 @@ import net.minecraft.util.Mth
import java.util.*
import kotlin.math.min
@Environment(EnvType.CLIENT)
class CobblemonToast(
val id: UUID,
var icon: ItemStack,
@@ -8,18 +8,52 @@
package com.cobblemon.mod.common.client.net.effect
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
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.PosableState
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import net.minecraft.client.Minecraft
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Entity
object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormEntityParticlePacket> {
override fun handle(packet: SpawnSnowstormEntityParticlePacket, client: Minecraft) {
val world = Minecraft.getInstance().level ?: return
val effect = BedrockParticleOptionsRepository.getEffect(packet.effectId) ?: return
val entity = world.getEntity(packet.entityId) as? LivingEntity ?: return
ParticleStorm.createAtEntity(world, effect, entity, packet.locator).forEach(ParticleStorm::spawn)
val sourceEntity = world.getEntity(packet.sourceEntityId) as? PosableEntity ?: return
val targetedEntity = packet.targetedEntityId?.let { world.getEntity(it) }
sourceEntity as Entity
val sourceState = sourceEntity.delegate as PosableState
val targetState = (targetedEntity as? PosableEntity)?.delegate as? PosableState
val sourceLocators = packet.sourceLocators.firstNotNullOfOrNull { sourceState.getMatchingLocators(it).takeIf { it.isNotEmpty() } } ?: return
val targetedLocator = packet.targetLocators?.firstOrNull { targetState?.getMatchingLocators(it)?.isNotEmpty() == true }
val rootMatrix = sourceState.locatorStates["root"]!!
sourceLocators.forEach { locator ->
val locatorMatrix = sourceState.locatorStates[locator]!!
val particleMatrix = effect.emitter.space.initializeEmitterMatrix(rootMatrix, locatorMatrix)
val particleRuntime = MoLangRuntime().setup().setupClient()
particleRuntime.environment.query.addFunction("entity") { sourceState.runtime.environment.query }
val storm = ParticleStorm(
effect = effect,
emitterSpaceMatrix = particleMatrix,
locatorSpaceMatrix = locatorMatrix,
world = world,
runtime = particleRuntime,
sourceVelocity = { sourceEntity.deltaMovement },
sourceAlive = { !sourceEntity.isRemoved },
sourceVisible = { !sourceEntity.isInvisible },
targetPos = if (targetedEntity != null) { { targetedLocator?.let { targetState?.locatorStates?.get(it) }?.getOrigin() ?: targetedEntity.position() } } else null,
entity = sourceEntity
)
storm.spawn()
}
}
}
@@ -11,13 +11,19 @@ package com.cobblemon.mod.common.client.net.effect
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import net.minecraft.client.Minecraft
import com.mojang.blaze3d.vertex.PoseStack
object SpawnSnowstormParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormParticlePacket> {
override fun handle(packet: SpawnSnowstormParticlePacket, client: Minecraft) {
val wrapper = MatrixWrapper()
val matrix = PoseStack()
wrapper.updateMatrix(matrix.last().pose())
wrapper.updatePosition(packet.position)
val world = Minecraft.getInstance().level ?: return
val effect = BedrockParticleOptionsRepository.getEffect(packet.effectId) ?: return
ParticleStorm.createAtPosition(world, effect, packet.position).spawn()
ParticleStorm(effect, wrapper, wrapper, world).spawn()
}
}
@@ -18,6 +18,7 @@ object ServerSettingsPacketHandler : ClientNetworkPacketHandler<ServerSettingsPa
ServerSettings.preventCompletePartyDeposit = packet.preventCompletePartyDeposit
ServerSettings.displayEntityLevelLabel = packet.displayEntityLevelLabel
ServerSettings.displayEntityNameLabel = packet.displayEntityNameLabel
ServerSettings.maxPokemonLevel = packet.maxPokemonLevel
}
}
@@ -31,12 +31,16 @@ object BedrockParticleOptionsRepository {
fun loadEffects(resourceManager: ResourceManager) {
LOGGER.info("Loading particle effects...")
effects.clear()
resourceManager.listResources("bedrock/particles") { path -> path.endsWith(".particle.json") }.forEach { identifier, resource ->
resource.open().use { stream ->
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
val effect = SnowstormParticleReader.loadEffect(GSON.fromJson(json, JsonObject::class.java))
effects[effect.id] = effect
try {
resource.open().use { stream ->
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
val effect = SnowstormParticleReader.loadEffect(GSON.fromJson(json, JsonObject::class.java))
effects[effect.id] = effect
}
}
catch (e: Exception) {
LOGGER.error(e)
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.particle
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
@@ -22,8 +23,10 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.particle.SnowstormParticleOptions
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import com.mojang.blaze3d.vertex.PoseStack
import com.cobblemon.mod.common.util.resolve
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.particle.NoRenderParticle
@@ -42,15 +45,21 @@ import net.minecraft.world.level.Level
*/
class ParticleStorm(
val effect: BedrockParticleOptions,
val matrixWrapper: MatrixWrapper,
val emitterSpaceMatrix: MatrixWrapper,
//This is a mildly inaccurate name. This matrix is basically the matrix we want to peg our movement to.
//Often times, this is the locator matrix, but sometimes, when we want the emitter to NOT track the locator, this value is the same obj as emitterSpaceMatrix.
//Currently only position is tracked. Ideally we would come up with a way to configure whether the emitter tracks any combination of position/rotation (or neither)
//on this matrix.
val locatorSpaceMatrix: MatrixWrapper,
val world: ClientLevel,
val sourceVelocity: () -> Vec3 = { Vec3.ZERO },
val sourceAlive: () -> Boolean = { true },
val sourceVisible: () -> Boolean = { true },
val targetPos: (() -> Vec3)? = null,
val onDespawn: () -> Unit = {},
val runtime: MoLangRuntime = MoLangRuntime(),
val entity: Entity? = null
): NoRenderParticle(world, matrixWrapper.getOrigin().x, matrixWrapper.getOrigin().y, matrixWrapper.getOrigin().z) {
val entity: Entity? = null,
): NoRenderParticle(world, emitterSpaceMatrix.getOrigin().x, emitterSpaceMatrix.getOrigin().y, emitterSpaceMatrix.getOrigin().z) {
fun spawn() {
if (entity != null) {
runtime.environment.query
@@ -69,6 +78,9 @@ class ParticleStorm(
}
if (entity is PosableEntity) {
runtime.environment.query.addFunction("entity") { entity.struct }
if (targetPos != null) {
addAttackingFunctions(targetPos)
}
} else if (entity is Player) {
runtime.environment.query.addFunction("entity") { entity.asMoLangValue() }
}
@@ -83,6 +95,43 @@ class ParticleStorm(
Minecraft.getInstance().particleEngine.add(this)
}
fun addAttackingFunctions(destinationPosSupplier: (() -> Vec3)) {
runtime.environment.query.addFunction("target_deltax") { params ->
DoubleValue(distanceTo(destinationPosSupplier.invoke()).x)
}
runtime.environment.query.addFunction("target_deltay") { params ->
DoubleValue(distanceTo(destinationPosSupplier.invoke()).y * -1)
}
runtime.environment.query.addFunction("target_deltaz") { params ->
DoubleValue(distanceTo(destinationPosSupplier.invoke()).z)
}
runtime.environment.query.addFunction("target_distance") { params ->
DoubleValue(distanceTo(destinationPosSupplier.invoke()).length())
}
runtime.environment.setSimpleVariable("target_deltax", DoubleValue(distanceTo(destinationPosSupplier.invoke()).x))
runtime.environment.setSimpleVariable("target_deltay", DoubleValue(distanceTo(destinationPosSupplier.invoke()).y * -1))
runtime.environment.setSimpleVariable("target_deltaz", DoubleValue(distanceTo(destinationPosSupplier.invoke()).z))
runtime.environment.setSimpleVariable("target_distance", DoubleValue(distanceTo(destinationPosSupplier.invoke()).length()))
}
//These are variables that need to be different per particle instance
fun lockParticleVars(struct: VariableStruct) {
val destinationPos = targetPos?.invoke() ?: return
struct.setDirectly(
"target_deltax", DoubleValue(distanceTo(destinationPos).x)
)
struct.setDirectly(
"target_deltay", DoubleValue(distanceTo(destinationPos).y * -1)
)
struct.setDirectly(
"target_deltaz", DoubleValue(distanceTo(destinationPos).z),
)
struct.setDirectly(
"target_distance", DoubleValue(distanceTo(destinationPos).length()),
)
}
fun getX() = x
fun getY() = y
fun getZ() = z
@@ -108,7 +157,7 @@ class ParticleStorm(
val matrix = PoseStack()
matrix.translate(position.x, position.y, position.z)
wrapper.updateMatrix(matrix.last().pose())
return ParticleStorm(effect, wrapper, world)
return ParticleStorm(effect, wrapper, wrapper, world)
}
/**
@@ -124,7 +173,8 @@ class ParticleStorm(
particleRuntime.environment.query.addFunction("entity") { state.runtime.environment.query }
ParticleStorm(
effect = effect,
matrixWrapper = matrixWrapper,
emitterSpaceMatrix = matrixWrapper,
locatorSpaceMatrix = matrixWrapper,
world = world,
runtime = particleRuntime,
sourceVelocity = { entity.deltaMovement },
@@ -141,7 +191,8 @@ class ParticleStorm(
return listOf(
ParticleStorm(
effect = effect,
matrixWrapper = matrixWrapper,
emitterSpaceMatrix = matrixWrapper,
locatorSpaceMatrix = matrixWrapper,
world = world,
runtime = particleRuntime,
sourceVelocity = { entity.deltaMovement },
@@ -192,7 +243,7 @@ class ParticleStorm(
return
}
val pos = matrixWrapper.getOrigin()
val pos = locatorSpaceMatrix.getOrigin()
xo = x
yo = y
zo = z
@@ -201,6 +252,9 @@ class ParticleStorm(
y = pos.y
z = pos.z
//Keeps emitter attached to locator
emitterSpaceMatrix.updatePosition(locatorSpaceMatrix.getOrigin())
val oldDistanceTravelled = distanceTravelled
distanceTravelled += Vec3(x - xo, y - yo, z - zo).length().toFloat()
@@ -255,7 +309,12 @@ class ParticleStorm(
contextStorm = null
}
fun transformPosition(position: Vec3): Vec3 = matrixWrapper.transformPosition(position)
fun transformPosition(position: Vec3): Vec3 = emitterSpaceMatrix.transformPosition(position)
fun transformDirection(direction: Vec3): Vec3 = matrixWrapper.matrix.transformDirection(direction)
fun transformDirection(direction: Vec3): Vec3 = emitterSpaceMatrix.matrix.transformDirection(direction)
//Gets distance between emitter pos and destination pos in emitter space
fun distanceTo(destinationPos: Vec3): Vec3 {
return emitterSpaceMatrix.transformWorldToParticle(Vec3(x, y, z)).subtract(emitterSpaceMatrix.transformWorldToParticle(destinationPos))
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.render
import com.cobblemon.mod.common.util.math.geometry.getOrigin
import com.cobblemon.mod.common.util.math.geometry.transformPosition
import net.fabricmc.loader.impl.lib.sat4j.core.Vec
import org.joml.Matrix4f
import net.minecraft.world.phys.Vec3
@@ -38,4 +37,6 @@ class MatrixWrapper {
return position.add(matrix.getOrigin())
}
fun transformPosition(position: Vec3) = this.position.add(matrix.transformPosition(position))
fun transformWorldToParticle(position: Vec3) = Matrix4f(matrix).invertAffine().transformPosition(position.subtract(this.position))
fun clone() = MatrixWrapper().updateMatrix(matrix).updatePosition(position)
}
@@ -8,12 +8,16 @@
package com.cobblemon.mod.common.client.render
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.ModAPI
import com.cobblemon.mod.common.api.snowstorm.ParticleMaterial
import com.cobblemon.mod.common.api.snowstorm.UVDetails
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.mojang.blaze3d.platform.GlStateManager
@@ -37,6 +41,7 @@ import org.joml.AxisAngle4d
import org.joml.Quaterniond
import org.joml.Vector3d
import org.joml.Vector3f
import kotlin.random.Random
class SnowstormParticle(
val storm: ParticleStorm,
@@ -45,7 +50,10 @@ class SnowstormParticle(
y: Double,
z: Double,
initialVelocity: Vec3,
var invisible: Boolean = false
//Usually, this will be the storms runtime. But sometimes particles need to have variables different from their emitter, so the VariableStruct will differ
val runtime: MoLangRuntime,
val matrixWrapper: MatrixWrapper,
var invisible: Boolean = false,
) : Particle(world, x, y, z) {
companion object {
const val MAXIMUM_DISTANCE_CHANGE_PER_TICK_FOR_FRICTION = 0.005
@@ -59,11 +67,6 @@ class SnowstormParticle(
var texture = storm.effect.particle.texture
val random1 = storm.runtime.environment.variable.map["particle_random_1"]
val random2 = storm.runtime.environment.variable.map["particle_random_2"]
val random3 = storm.runtime.environment.variable.map["particle_random_3"]
val random4 = storm.runtime.environment.variable.map["particle_random_4"]
var localX = x - storm.getX()
var localY = y - storm.getY()
var localZ = z - storm.getZ()
@@ -95,30 +98,25 @@ class SnowstormParticle(
fun getSpriteFromAtlas(): TextureAtlasSprite {
val atlas = Minecraft.getInstance().particleEngine.textureAtlas
// val field = atlas::class.java.getDeclaredField("sprites")
// field.isAccessible = true
// val map = field.get(atlas) as Map<Identifier, Sprite>
// println(map.keys.joinToString { it.toString() })
val sprite = atlas.getSprite(storm.effect.particle.texture)
// println(storm.effect.particle.texture)
return sprite
}
private fun applyRandoms() {
storm.runtime.environment.variable.setDirectly("particle_random_1", random1)
storm.runtime.environment.variable.setDirectly("particle_random_2", random2)
storm.runtime.environment.variable.setDirectly("particle_random_3", random3)
storm.runtime.environment.variable.setDirectly("particle_random_4", random4)
//Will having these emitter randoms not equal the real emitter randoms be a problem?
runtime.environment.setSimpleVariable("emitter_random_1", DoubleValue(Random.Default.nextDouble()))
runtime.environment.setSimpleVariable("emitter_random_2", DoubleValue(Random.Default.nextDouble()))
runtime.environment.setSimpleVariable("emitter_random_3", DoubleValue(Random.Default.nextDouble()))
runtime.environment.setSimpleVariable("emitter_random_4", DoubleValue(Random.Default.nextDouble()))
}
init {
setParticleSpeed(initialVelocity.x, initialVelocity.y, initialVelocity.z)
roll = -storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
roll = -storm.effect.particle.rotation.getInitialRotation(runtime).toFloat()
oRoll = roll
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(storm.runtime)
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(runtime)
friction = 1F
lifetime = (storm.runtime.resolveDouble(storm.effect.particle.maxAge) * 20).toInt()
lifetime = (runtime.resolveDouble(storm.effect.particle.maxAge) * 20).toInt()
storm.particles.add(this)
gravity = 0F
particleTextureSheet = if (invisible) NO_RENDER else PARTICLE_SHEET_TRANSLUCENT
@@ -140,8 +138,8 @@ class SnowstormParticle(
applyRandoms()
setParticleAgeInRuntime()
storm.effect.curves.forEach { it.apply(storm.runtime) }
storm.runtime.execute(storm.effect.particle.renderExpressions)
storm.effect.curves.forEach { it.apply(runtime) }
runtime.execute(storm.effect.particle.renderExpressions)
// // TODO need to implement the other materials but not sure exactly what they are GL wise
when (storm.effect.particle.material) {
@@ -173,7 +171,7 @@ class SnowstormParticle(
val g = (pos.y + originPos.y - vec3d.y()).toFloat()
val h = (pos.z + originPos.z - vec3d.z()).toFloat()
val quaternion = storm.effect.particle.cameraMode.getRotation(
matrixWrapper = storm.matrixWrapper,
matrixWrapper = matrixWrapper,
prevAngle = oRoll,
angle = roll,
deltaTicks = tickDelta,
@@ -184,8 +182,8 @@ class SnowstormParticle(
cameraPitch = camera.xRot,
viewDirection = viewDirection
)
val xSize = storm.runtime.resolveDouble(storm.effect.particle.sizeX).toFloat() / 1.5.toFloat()
val ySize = storm.runtime.resolveDouble(storm.effect.particle.sizeY).toFloat() / 1.5.toFloat()
val xSize = runtime.resolveDouble(storm.effect.particle.sizeX).toFloat() / 1.5.toFloat()
val ySize = runtime.resolveDouble(storm.effect.particle.sizeY).toFloat() / 1.5.toFloat()
val particleVertices = arrayOf(
Vector3f(xSize, -ySize, 0.0f),
@@ -200,8 +198,8 @@ class SnowstormParticle(
vertex.add(f, g, h)
}
val uvs = storm.effect.particle.uvMode.get(storm.runtime, age / 20.0, lifetime / 20.0, uvDetails)
val colour = storm.effect.particle.tinting.getTint(storm.runtime)
val uvs = storm.effect.particle.uvMode.get(runtime, age / 20.0, lifetime / 20.0, uvDetails)
val colour = storm.effect.particle.tinting.getTint(runtime)
val spriteURange = sprite.u1 - sprite.u0
val spriteVRange = sprite.v1 - sprite.v0
@@ -240,21 +238,21 @@ class SnowstormParticle(
override fun tick() {
if (storm.effect.space.localPosition) {
originPos = storm.matrixWrapper.getOrigin()
originPos = matrixWrapper.getOrigin()
}
applyRandoms()
setParticleAgeInRuntime()
storm.effect.curves.forEach { it.apply(storm.runtime) }
storm.runtime.execute(storm.effect.particle.updateExpressions)
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(storm.runtime, -roll.toDouble(), angularVelocity) / 20
storm.effect.curves.forEach { it.apply(runtime) }
runtime.execute(storm.effect.particle.updateExpressions)
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(runtime, -roll.toDouble(), angularVelocity) / 20
if (age >= lifetime || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
if (age >= lifetime || runtime.resolveBoolean(storm.effect.particle.killExpression)) {
runExpirationEvents()
remove()
return
} else {
val velocity = storm.effect.particle.motion.getVelocity(storm.runtime, this,
val velocity = storm.effect.particle.motion.getVelocity(runtime, this,
Vec3(xd, yd, zd)
)
xd = velocity.x
@@ -266,7 +264,7 @@ class SnowstormParticle(
}
viewDirection = storm.effect.particle.viewDirection.getDirection(
runtime = storm.runtime,
runtime = runtime,
lastDirection = viewDirection,
currentVelocity = Vec3(xd, yd, zd)
).normalize()
@@ -282,7 +280,17 @@ class SnowstormParticle(
oldAxisRotation = axisRotation
prevRotatedLocal = oldAxisRotation.transform(Vector3d(prevLocalX, prevLocalY, prevLocalZ))
storm.matrixWrapper.matrix.getRotation(axisRotation)
//This is a bit of a hack. Technically local rotation is supposed to make it so the particle uses
//the emitter's rotation - but we've made it so the emitter doesn't actually follow the locators rotation, only position.
//So instead of being bound to the emitter, the particle is actually bound to the locator for local rotation.
//If you're messing with this - make sure evo particles still rotate with the pokemon
if (storm.effect.space.localRotation) {
storm.locatorSpaceMatrix.matrix.getRotation(axisRotation)
}
else {
matrixWrapper.matrix.getRotation(axisRotation)
}
rotatedLocal = axisRotation.transform(Vector3d(prevLocalX, prevLocalY, prevLocalZ))
Quaterniond().rotateTo(prevRotatedLocal, rotatedLocal).get(currentRotation)
@@ -295,7 +303,7 @@ class SnowstormParticle(
override fun move(dx: Double, dy: Double, dz: Double) {
val collision = storm.effect.particle.collision
val radius = storm.runtime.resolveDouble(collision.radius)
val radius = runtime.resolveDouble(collision.radius)
boundingBox = AABB.ofSize(Vec3(x, y, z), radius, radius, radius)
if (dx == 0.0 && dy == 0.0 && dz == 0.0) {
updatePosition()
@@ -306,7 +314,7 @@ class SnowstormParticle(
var dy = dy
var dz = dz
if (storm.runtime.resolveBoolean(collision.enabled) && radius > 0.0 && !storm.effect.space.isLocalSpace) {
if (runtime.resolveBoolean(collision.enabled) && radius > 0.0 && !storm.effect.space.isLocalSpace) {
hasPhysics = true
val newMovement = checkCollision(Vec3(dx, dy, dz))
@@ -359,7 +367,7 @@ class SnowstormParticle(
}
fun updatePosition() {
val localVector = if (storm.effect.space.localRotation) storm.transformDirection(
val localVector = if (storm.effect.space.localRotation) storm.locatorSpaceMatrix.matrix.transformDirection(
Vec3(
localX,
localY,
@@ -374,8 +382,8 @@ class SnowstormParticle(
private fun checkCollision(movement: Vec3): Vec3 {
val collision = storm.effect.particle.collision
var box = boundingBox
val bounciness = storm.runtime.resolveDouble(collision.bounciness)
val friction = storm.runtime.resolveDouble(collision.friction)
val bounciness = runtime.resolveDouble(collision.bounciness)
val friction = runtime.resolveDouble(collision.friction)
val expiresOnContact = collision.expiresOnContact
val collisions = level.getBlockCollisions(null, box.expandTowards(movement))
@@ -513,8 +521,8 @@ class SnowstormParticle(
private fun setParticleAgeInRuntime() {
storm.runtime.environment.variable.setDirectly("particle_age", DoubleValue(age / 20.0))
storm.runtime.environment.variable.setDirectly("particle_lifetime", DoubleValue(lifetime / 20.0))
runtime.environment.variable.setDirectly("particle_age", DoubleValue(age / 20.0))
runtime.environment.variable.setDirectly("particle_lifetime", DoubleValue(lifetime / 20.0))
}
override fun getRenderType() = particleTextureSheet
@@ -14,8 +14,6 @@ import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.util.cobblemonResource
import java.awt.Color
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.RenderType
@@ -33,7 +31,6 @@ import net.minecraft.world.entity.HumanoidArm
import net.minecraft.world.item.ItemDisplayContext
import org.joml.Quaternionf
@Environment(value = EnvType.CLIENT)
class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : EntityRenderer<PokeRodFishingBobberEntity>(context) {
override fun render(fishingBobberEntity: PokeRodFishingBobberEntity, elapsedPartialTicks: Float, tickDelta: Float, matrixStack: PoseStack, vertexConsumerProvider: MultiBufferSource, light: Int) {
@@ -56,7 +56,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
matrices.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrices.translate(model.profileTranslation.x, model.profileTranslation.y, -4.0)
matrices.scale(model.profileScale, model.profileScale, 0.15F)
val rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(transformations.rotation.x, transformations.rotation.y, transformations.rotation.z))
@@ -35,7 +35,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
@@ -44,8 +43,8 @@ import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.text.get
/**
* Represents some kind of animation state for an entity or GUI element or other renderable component in the game.
@@ -185,15 +184,17 @@ abstract class PosableState : Schedulable {
val entity = getEntity() ?: return@addFunction Unit
val world = entity.level() as ClientLevel
val matrixWrapper = locatorStates[locator] ?: locatorStates["root"]!!
val rootMatrix = locatorStates["root"]!!
val locatorMatrix = locatorStates[locator] ?: locatorStates["root"]!!
val particleMatrix = effect.emitter.space.initializeEmitterMatrix(rootMatrix, locatorMatrix)
val particleRuntime = MoLangRuntime().setup().setupClient()
particleRuntime.environment.query.addFunction("entity") { runtime.environment.query }
val storm = ParticleStorm(
effect = effect,
entity = entity,
matrixWrapper = matrixWrapper,
emitterSpaceMatrix = particleMatrix,
locatorSpaceMatrix = locatorMatrix,
world = world,
runtime = particleRuntime,
sourceVelocity = { entity.deltaMovement },
@@ -54,9 +54,12 @@ class PoseAdapter(
addCondition("isTouchingWater") { entity, expectedValue -> entity.isInWater == expectedValue }
addCondition("isInWaterOrRain") { entity, expectedValue -> entity.isInWaterOrRain == expectedValue }
addCondition("isUnderWater") { entity, expectedValue -> entity.isUnderWater == expectedValue }
// Kept for compatibility
addCondition("isStandingOnRedSand") { entity, expectedValue -> entity.isStandingOnRedSand() == expectedValue }
addCondition("isStandingOnSand") { entity, expectedValue -> entity.isStandingOnSand() == expectedValue }
addCondition("isStandingOnSandOrRedSand") { entity, expectedValue -> entity.isStandingOnSandOrRedSand() == expectedValue }
addCondition("isDusk") { entity, expectedValue -> entity.isDusk() == expectedValue }
conditionsList.addAll(poseConditionReader(json))
@@ -93,4 +96,4 @@ class PoseAdapter(
)
}
}
}
}
@@ -14,6 +14,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleOptions
import com.cobblemon.mod.common.client.particle.ParticleStorm
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.client.render.models.blockbench.repository.RenderContext
@@ -32,6 +33,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.*
data class BedrockAnimationGroup(
@@ -66,7 +68,10 @@ class BedrockParticleKeyframe(
override fun run(entity: Entity?, state: PosableState) {
entity ?: return
val world = entity.level() as? ClientLevel ?: return
val matrixWrapper = state.locatorStates[locator] ?: state.locatorStates["root"]!!
val rootMatrix = state.locatorStates["root"]!!
val locatorMatrix = state.locatorStates[locator] ?: state.locatorStates["root"]!!
val particleMatrix = effect.emitter.space.initializeEmitterMatrix(rootMatrix, locatorMatrix)
if (this in state.poseParticles) {
return
@@ -79,7 +84,8 @@ class BedrockParticleKeyframe(
val storm = ParticleStorm(
effect = effect,
matrixWrapper = matrixWrapper,
emitterSpaceMatrix = particleMatrix,
locatorSpaceMatrix = locatorMatrix,
world = world,
runtime = particleRuntime,
sourceVelocity = { entity.deltaMovement },
@@ -51,6 +51,7 @@ import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.entity.MobRenderer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Style
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
@@ -75,6 +76,11 @@ class PokemonRenderer(
.withUnderlined(false)
.withStrikethrough(false)
.withObfuscated(false)
private const val HIDDEN_NAME = "???"
private const val SPACE = " "
}
val ballContext = RenderContext().also {
@@ -355,23 +361,15 @@ class PokemonRenderer(
matrices.scale((0.025 * sizeScale).toFloat(), (-0.025 * sizeScale).toFloat(), (1 * sizeScale).toFloat())
val matrix4f = matrices.last().pose()
val opacity = (Minecraft.getInstance().options.getBackgroundOpacity(0.25F) * 255.0F).toInt() shl 24
var label = if (ServerSettings.displayEntityNameLabel &&
!Cobblemon.config.displayNameForUnknownPokemon &&
CobblemonClient.clientPokedexData.getKnowledgeForSpecies(entity.pokemon.species.resourceIdentifier) == PokedexEntryProgress.NONE) {
Component.literal("???")
} else if (ServerSettings.displayEntityNameLabel) {
entity.name.copy()
} else {
Component.empty()
}
if(ServerSettings.displayEntityNameLabel && ServerSettings.displayEntityLevelLabel && entity.labelLevel() > 0) {
label.append(Component.literal(" "))
}
val label = this.resolveBaseLabel(entity)
if (ServerSettings.displayEntityLevelLabel && entity.labelLevel() > 0) {
if (ServerSettings.displayEntityNameLabel) {
label.append(SPACE)
}
// This a Style.EMPTY with a lot of effects set to false and color set to white, renderer inherits these from nick otherwise
val levelLabel = lang("label.lv", entity.labelLevel())
.setStyle(LEVEL_LABEL_STYLE)
label = label.append(levelLabel)
label.append(levelLabel)
}
var h = (-this.font.width(label) / 2).toFloat()
val y = 0F
@@ -390,6 +388,14 @@ class PokemonRenderer(
}
}
private fun resolveBaseLabel(entity: PokemonEntity): MutableComponent {
return when {
!ServerSettings.displayEntityNameLabel -> Component.empty()
Cobblemon.config.displayNameForUnknownPokemon || CobblemonClient.clientPokedexData.getKnowledgeForSpecies(entity.pokemon.species.resourceIdentifier) != PokedexEntryProgress.NONE -> entity.name.copy()
else -> Component.literal(HIDDEN_NAME)
}
}
private fun drawPokeBall(
state: ClientBallDisplay,
matrixStack: PoseStack,
@@ -24,4 +24,6 @@ object ServerSettings {
var preventCompletePartyDeposit = Cobblemon.config.preventCompletePartyDeposit
var displayEntityLevelLabel = Cobblemon.config.displayEntityLevelLabel
var displayEntityNameLabel = Cobblemon.config.displayEntityNameLabel
var maxPokemonLevel = Cobblemon.config.maxPokemonLevel
}
@@ -27,7 +27,7 @@ object AbandonMultiTeam {
val command = dispatcher.register(
Commands.literal("abandonmultiteam")
.permission(CobblemonPermissions.ABANDON_MULTITEAM)
.executes { execute(it,) }
.executes { execute(it) }
)
dispatcher.register(command.alias(ALIAS))
}

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