Compare commits

...
403 Commits
Author SHA1 Message Date
Test c346f84844 speed 2026-02-21 21:17:26 -05:00
Paul d1b8094539 Validate party positions of traded Pokémon upon trading 2026-02-16 11:23:24 +10:00
Paul b8d79af911 Migrate to a more unique tag name for data and config on scriptable entities, closes #1869 2026-02-16 10:57:11 +10:00
Hiroku a7198fd324 Merge branch 'fossil_crash' into 'main'
Adjust fossil creation

Closes #1801

See merge request cable-mc/cobblemon!2150
2026-02-08 01:16:45 +00:00
aeroeng15 bc69ce91f3 Changelog 2026-02-07 16:00:00 -07:00
aeroeng15 1de1d8cdc3 Adjust ordering of party modification 2026-02-07 15:26:36 -07:00
Hiroku 087bf75500 Merge branch 'party-selection-toast' into 'main'
Party Selection Tutorial Toasts

See merge request cable-mc/cobblemon!2148
2026-02-02 05:58:58 +00:00
CeejayandHiroku bac435ba7b Party Selection Tutorial Toasts 2026-02-02 05:58:58 +00:00
Hiroku 74a6106eb6 Merge branch 'snack-stack-fix' into 'main'
Fixes odd stacking behavior with the poke_bait item.

See merge request cable-mc/cobblemon!2143
2026-02-01 03:29:20 +00:00
Neovitalism 5ec993ad3c Update CHANGELOG.md 2026-01-31 21:13:10 -06:00
Paul 73a03d90da Resnapshot 2026-01-31 13:22:11 +10:00
Paul e6fda6137e Teeny tiny changelog [skip-ci] 2026-01-31 10:47:13 +10:00
Paul afae39acc8 Missed a bit of changelog [skip-ci] 2026-01-31 09:21:40 +10:00
Paul 37264aff0f Changelog stuff and no snapshot 2026-01-31 09:17:43 +10:00
Paul 2b60e6de77 Merge remote-tracking branch 'origin/lang-updates' 2026-01-31 09:12:27 +10:00
Hiroku 4f29e7457c Merge branch 'feature/riding-scrape' into 'main'
scrape riding data and fix script

See merge request cable-mc/cobblemon!2141
2026-01-30 09:43:44 +00:00
Polymeta 04a6ab31f4 rescrape data
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-01-30 09:26:22 +00:00
Hiroku a2cdc847dd Merge branch 'packet-sync-error-fix' into 'main'
Fixed packet sync issue with pokemon interactions, added "chance" requirement type

Closes #1933 and #1938

See merge request cable-mc/cobblemon!2147
2026-01-30 03:04:14 +00:00
阿鱼andWeblate 0bca678f56 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-01-30 02:59:44 +00:00
Paul 067c3607aa Tweak /pctake to check the PC's box count and use that for constraints 2026-01-30 12:20:17 +10:00
Hiroku 823c1f6444 Merge branch 'main' into 'main'
Fixes PcTake not respecting defaultBoxCount

See merge request cable-mc/cobblemon!2146
2026-01-30 02:18:38 +00:00
Paul bfc00635cd Fixed packet sync issue with pokemon interactions, added "chance" requirement type 2026-01-30 12:16:01 +10:00
Hiroku b2e5b85ac5 Merge branch 'Custom-Ball-Resources-updated' into 'main'
updated ball texture locations missing from last update

See merge request cable-mc/cobblemon!2145
2026-01-30 00:24:49 +00:00
阿鱼andWeblate 2c27476954 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.7% (9034 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-01-29 13:40:10 +00:00
Auristite aab78d84fc Fixes PcTake not respecting defaultBoxCount
Command is also missing lang option for "cobblemon.command.pctake.removed" (Removing Pokémon from self)
2026-01-29 04:46:25 +00:00
Zack the GhastandWeblate cb632c2a65 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.9% (6154 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-29 01:28:38 +00:00
Dragonwhisper92 8d219f1338 updated ball texture locations 2026-01-28 23:45:08 +00:00
Zack the GhastandWeblate 1e6a4ed984 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.7% (6143 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-28 21:10:25 +00:00
geumuandWeblate 64240c31fc Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-01-28 17:35:35 +00:00
Zack the GhastandWeblate d2222c3858 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.6% (6129 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-28 14:44:02 +00:00
geumuandWeblate c7f31c3e14 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-01-28 14:44:01 +00:00
Zack the GhastandWeblate 740e846b2d Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.5% (6125 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-28 08:26:35 +00:00
Neovitalism 246d00aa3f Fixes odd stacking behavior with the poke_bait item. 2026-01-27 21:45:05 -06:00
Zack the GhastandWeblate 06f9d60595 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.5% (6123 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-27 04:12:54 +00:00
Zack the GhastandWeblate d54439ce14 Translated using Weblate (Portuguese (Portugal))
Currently translated at 67.3% (6103 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-26 02:31:37 +00:00
TeggyandWeblate 32ee4bb72a Translated using Weblate (Spanish)
Currently translated at 84.3% (7644 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-01-25 13:02:37 +00:00
Polymeta e7d2a46cc0 Bumping snapshot version 2026-01-25 06:57:31 +00:00
YasitoandWeblate 3ee94ec076 Translated using Weblate (French)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-25 03:06:53 +00:00
YasitoandWeblate 589293abc4 Translated using Weblate (French (Canada))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-25 03:06:49 +00:00
Paul 279865cc7c Resnaps your shot 2026-01-25 11:53:29 +10:00
Paul b597b149c3 Snapnot 2026-01-25 10:18:32 +10:00
Paul b7f592f2a8 Relocate mongodb packages 2026-01-25 10:18:02 +10:00
Paul 70e17c22e7 Merge remote-tracking branch 'origin/lang-updates' 2026-01-25 10:17:49 +10:00
YasitoandWeblate 51b18ca0f6 Translated using Weblate (French)
Currently translated at 99.6% (9027 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-25 00:16:58 +00:00
YasitoandWeblate a83f243a3b Translated using Weblate (French (Canada))
Currently translated at 99.9% (9059 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-25 00:16:55 +00:00
Weblate f5b088c708 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/
2026-01-25 00:14:04 +00:00
YasitoandWeblate dc454cbb68 Translated using Weblate (French)
Currently translated at 99.5% (9022 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-25 00:13:56 +00:00
YasitoandWeblate 3b99e38da2 Translated using Weblate (French (Canada))
Currently translated at 99.9% (9057 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-25 00:13:54 +00:00
Paul 483af57964 Merge remote-tracking branch 'origin/lang-updates' 2026-01-25 10:12:48 +10:00
Dmytro BunnuaandWeblate a941bdddf3 Translated using Weblate (Ukrainian)
Currently translated at 76.9% (6971 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-01-24 18:24:12 +00:00
TeggyandWeblate 246ba4a93b Translated using Weblate (Spanish)
Currently translated at 83.9% (7611 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-01-24 18:24:09 +00:00
Paul 3347c084ed More changelog [skip-ci] 2026-01-24 22:42:58 +10:00
Paul 8175cee454 More translations 2026-01-24 22:11:15 +10:00
Paul 37d54053a5 Merge remote-tracking branch 'origin/lang-updates' 2026-01-24 22:10:10 +10:00
Dmytro BunnuaandWeblate b8846bd74c Translated using Weblate (Ukrainian)
Currently translated at 76.8% (6966 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-01-24 11:47:21 +00:00
Paul 1716e135c4 Changelog [skip-ci] 2026-01-24 21:45:14 +10:00
Paul 0ac804cfcb Cleanup 2026-01-24 21:20:50 +10:00
Hiroku aecc132814 Merge branch 'pokeballUIResourcePathChange' into 'main'
ball_icon resource location changes

See merge request cable-mc/cobblemon!2113
2026-01-24 11:13:26 +00:00
Hiroku 8b47876dff Merge branch 'Add-Plaques-to-Village-Pokecenters' into 'main'
Adding Plaques to Village Pokecenters

See merge request cable-mc/cobblemon!2139
2026-01-24 09:27:56 +00:00
Slippy ffc108953a Adding Plaques to Village Pokecenters 2026-01-24 01:51:33 -06:00
Paul 0a106174fe Changelog cleanup [skip-ci] 2026-01-24 15:59:27 +10:00
Paul 90210bee3c Fixed cottonee cries being inconsistent due to not having appropriate pose type coverage 2026-01-24 15:13:08 +10:00
Paul 8603cd4ad0 Merge remote-tracking branch 'origin/main' into Expression-stuffs 2026-01-24 13:56:14 +10:00
Hiroku 7b2c6827c2 Merge branch 'Expression-stuffs' into 'main'
Expression stuffs

See merge request cable-mc/cobblemon!2138
2026-01-24 03:55:02 +00:00
Paul 902b4a6d21 Fixed croagunk and toxicroak playing cry animations without the sound 2026-01-24 13:54:41 +10:00
Hiroku a847cb5926 Merge branch 'fix-plain-aprijuice-prefix' into 'main'
Fix Aprijuice 'Plain' prefix

See merge request cable-mc/cobblemon!2137
2026-01-24 03:46:05 +00:00
Paul 050c6f5adc Merge remote-tracking branch 'origin/Expression-stuffs' into Expression-stuffs 2026-01-24 13:41:23 +10:00
Paul fb95fb3cf7 Various cry corrections from implementation 2026-01-24 13:41:11 +10:00
Carmendar d507367411 Bird stamina changes. 2026-01-23 21:30:43 -06:00
Paul faa19cebcb Fixed perma zero stamina on horses when landing from bird 2026-01-24 13:16:38 +10:00
Paul aced374367 Fixed birds falling out of the sky and not gliding all chill like 2026-01-24 12:40:04 +10:00
Paul d424a6f1d3 Fixed stamina stat flowing to crazy negative from bird sometimes 2026-01-24 11:58:25 +10:00
Carmendar 71bb1c32de stamina expression changes for Bird and Hover. Changes to Mantine riding. 2026-01-23 18:56:17 -06:00
Carmendar f726fcedb9 Merge branch 'main' into implement_pokemon 2026-01-23 18:09:48 -06:00
Weblate 77344ccc2f Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/
2026-01-23 17:29:44 +00:00
Dmytro BunnuaandWeblate 90bc16be90 Translated using Weblate (Ukrainian)
Currently translated at 76.8% (6960 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-01-23 17:29:37 +00:00
Gustavo PuerariandWeblate d1b743a01f Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.3% (8908 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-01-23 17:29:33 +00:00
TeggyandWeblate aed651c0a0 Translated using Weblate (Spanish)
Currently translated at 83.9% (7608 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-01-23 17:29:28 +00:00
Faintful 4c477e1675 Add changelog 2026-01-23 17:36:05 +01:00
Faintful d1cdc6789c Handle case for LOW quality Aprijuice in getName() for proper 'Plain' prefix 2026-01-23 17:33:04 +01:00
geumuandWeblate cd3127b983 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-01-23 13:22:11 +00:00
Watuneni iteiteandWeblate 68f5eff048 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-01-23 13:22:06 +00:00
Paul c3d2918a2b Fixed a crash when joining a server and a player is riding nearby 2026-01-23 17:49:57 +10:00
Paul 0432525c06 Throh resize 2026-01-23 17:04:45 +10:00
Paul a60f27e282 Fixed implemented flag missing from the new 1.7.2 pokemon, subtle size changes 2026-01-23 17:04:14 +10:00
Test 5f0182cb7e fix type asset 2026-01-23 01:48:49 -05:00
Paul 4a3ae9407b Translation changelog [skip-ci] 2026-01-23 16:44:32 +10:00
Paul 766c2838d2 Merge remote-tracking branch 'origin/lang-updates' 2026-01-23 14:34:19 +10:00
Hiroku 4cb95589b4 Merge branch 'more-fixes' into 'main'
More fixes

See merge request cable-mc/cobblemon!2134
2026-01-23 04:30:49 +00:00
Hiroku a8d3c2dd52 Merge branch '1.7.2-spawns' into 'main'
Added spawns. Fixed spawns. Updated tags.

Closes #1896

See merge request cable-mc/cobblemon!2131
2026-01-23 04:13:07 +00:00
Test cb5d7b6ad7 rm unused 2026-01-22 23:05:38 -05:00
Test c7dcc64595 sync repo assets 2026-01-22 23:00:10 -05:00
Paul 6573e08da7 Fix for packet handling to ensure all packets are handled on main and without extra delays on Fabric, closes #1903 2026-01-23 13:37:36 +10:00
Paul cd695ade4a Apparently I put more changelog in than was meant to be there 2026-01-23 13:22:37 +10:00
Paul 55cb413cfc Merge remote-tracking branch 'origin/main' into 1.7.2-spawns 2026-01-23 13:11:50 +10:00
Paul 4f1bf7fcbb Changelog for spawns [skip-ci] 2026-01-23 13:11:42 +10:00
Test 154ee80ead fix some block issues 2026-01-22 21:32:51 -05:00
Test 1c9f9bb198 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2026-01-22 20:38:08 -05:00
Test 6836343f11 ride summary 2026-01-22 20:37:46 -05:00
RedGenesectNinja aa0d1cd943 Updated changelog 2026-01-22 14:44:46 -06:00
Hiroku 798f1da8b3 Merge branch 'main' into 'main'
Issue 1926: Make SpawnBaitInfluence apply all effects correctly

See merge request cable-mc/cobblemon!2130
2026-01-22 13:44:44 +00:00
Andy d98efb52b6 Add changelog line and extend author tag 2026-01-22 08:38:13 -05:00
Kenji_64 2f27a03f08 Alolan Raichu can look around in the menu now (yay) 2026-01-22 13:36:19 +01:00
Kenji_64 e33025b1fa Fixed the weird eye clipping on Dragapult (fr this time) 2026-01-22 13:36:00 +01:00
Kenji_64 ade2481f42 Treecko line has their cries back 2026-01-22 13:35:39 +01:00
Hiroku 576e2301ce Merge branch 'open-actor-classes' into 'main'
Mark the actor classes as open so they can be extended.

See merge request cable-mc/cobblemon!2129
2026-01-22 12:19:06 +00:00
Polymeta a568d45d75 [skip-ci] tidy up changelog take two 2026-01-22 09:58:38 +00:00
Hiroku bc19a7e4c1 Merge branch 'decor-split' into 'main'
Decor split

See merge request cable-mc/cobblemon!2127
2026-01-22 09:42:35 +00:00
Polymeta 616e438f17 [skip-ci] tidy up changelog 2026-01-22 09:40:47 +00:00
Polymeta 9bb11c46d5 Merge branch 'implement_pokemon' into 'main'
Implement pokemon

Closes #1921 and #1800

See merge request cable-mc/cobblemon!2063
2026-01-22 09:28:25 +00:00
Carmendar 5f470bb703 Honchkrow and Mantine camera fixes. 2026-01-21 23:25:53 -06:00
Andy 276f13cea5 1926: Re-add affectWeight comment 2026-01-22 00:08:34 -05:00
Andy ac069687f1 1926: Changed SpawnBaitInfluence to apply all bait effects properly in affectWeight 2026-01-22 00:07:14 -05:00
Deltric f248c22eed Fix initialPos being able to change and changelog 2026-01-21 22:54:53 -06:00
RedGenesectNinja 65a5687f92 Fixed Dusknoir's stomach mouth lips glowing while it sleeps 2026-01-21 22:27:11 -06:00
Deltric ea50fa534b Mark the actor classes as open so they can be extended. 2026-01-21 21:35:03 -06:00
RedGenesectNinja 4bd3c4af58 Updated changelog 2026-01-21 21:20:16 -06:00
RedGenesectNinja 283d80f7ed Implemented Croagunk and Toxicroak's animations 2026-01-21 21:19:22 -06:00
Veraxiel 0238ca80f0 Added spawns. Fixed spawns. Updated tags. 2026-01-21 18:53:40 -05:00
RedGenesectNinja d24c5fe874 Fixed a mistake I made with file placement 2026-01-21 15:32:14 -06:00
RedGenesectNinja e65e21f2ff Fixed Chimecho's cry not playing a sound 2026-01-21 15:21:02 -06:00
Carmendar e1d417718f Added Flygon jump and Mantine ride anims with temp ride stats. 2026-01-21 02:25:05 -06:00
Polymeta d4308b0a4f Merge branch 'fix-battle-memory-leak' into 'main'
Fixed memory leak from battles not being cleaned up after ending.

See merge request cable-mc/cobblemon!2075
2026-01-21 08:09:44 +00:00
Test bc0d530484 loisence 2026-01-20 19:19:53 -05:00
Jerzykv 56650418d2 Implemented Throh and Sawk 2026-01-20 20:32:21 +01:00
Jerzykv 0543b84d34 The battle idle should probably play the battle idle animation 2026-01-20 19:57:45 +01:00
Jerzykv 1f50687a59 Re-added Honchkrow's driving license 2026-01-20 19:53:25 +01:00
Polymeta ed04faf0d8 Merge branch 'dispenser-fix' into 'main'
Resolved Dispenser Behaviors not working

See merge request cable-mc/cobblemon!2118
2026-01-20 14:46:55 +00:00
AramandPolymeta 1ed020f3e0 Resolved Dispenser Behaviors not working 2026-01-20 14:46:54 +00:00
Polymeta 7e385d8c94 Merge branch 'default-offest' into 'main'
[FEAT] Add `default_seat` option to seat offset posers

See merge request cable-mc/cobblemon!1999
2026-01-20 14:46:44 +00:00
Polymeta ee509b5438 Merge branch 'molang-inventory-and-move-utility' into 'main'
molang additions: Move struct accessor and player inventory utils

See merge request cable-mc/cobblemon!2103
2026-01-20 14:46:34 +00:00
Jerzykv ac9394131c Implemented Litleo and Pyroar - Changelog 2026-01-20 12:01:09 +01:00
Jerzykv d058a306a7 Implemented Litleo and Pyroar 2026-01-20 11:58:48 +01:00
Jerzykv b9be0c3fcb Swirlix and Slurpuff cry fixes 2026-01-20 10:14:16 +01:00
Jerzykv b59b46568a Swirlix and Slurpuff poser changes 2026-01-20 10:14:11 +01:00
Jerzykv 408cbc8c49 Slurpuff and Aromatisse scale changes 2026-01-20 10:13:55 +01:00
Test 1537816132 log of change 2026-01-20 00:56:36 -05:00
Test 1705639950 loot 2026-01-20 00:50:04 -05:00
Test ce32541dd6 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into decor-split 2026-01-19 23:45:28 -05:00
Test 3ffea4f4dd split items 2026-01-19 23:44:20 -05:00
Hiroku f942d7c34a Merge branch 'misc-additions-fixes' into 'main'
Couple of misc API additions and bug fixes.

See merge request cable-mc/cobblemon!2125
2026-01-19 02:16:09 +00:00
Deltric 0f1ada4624 Pass in pnx to the event 2026-01-18 18:52:27 -06:00
Deltric b4ee4629bf Couple of misc API additions and bug fixes. 2026-01-18 17:57:28 -06:00
Paul d0b64e2d82 Respect the BYPASSES_INVULNERABILITY damage tag 2026-01-18 22:12:25 +10:00
Jerzykv a8ce3c2175 Fixed Wishiwashi's School Form, Fixes #1921 2026-01-18 12:39:16 +01:00
Jerzykv 80ae127379 Implemented Spritzee and Aromatisse. Smells good. 2026-01-18 12:36:48 +01:00
Jerzykv bd3a9fb3af Implemented Swirlix and Slurpuff. How sweet! 2026-01-18 11:28:15 +01:00
Instrafe d0eaccb81b Fixed Ferroseed and Ferrothorn's posers not identifying a root bone (woops) 2026-01-17 12:12:08 -05:00
Paul 1ed2042283 Fixed /calculateseatpositions expecting no underscore when actually we always use an underscore 2026-01-17 22:32:57 +10:00
Deltric 0b531d967e Merge remote-tracking branch 'origin/fix-battle-memory-leak' into fix-battle-memory-leak
# Conflicts:
#	common/src/main/resources/data/cobblemon/showdown.zip
2026-01-17 03:42:38 -06:00
Deltric b0d1aec9ab Update showdown 2026-01-17 03:42:21 -06:00
Deltric f4d47098b5 Fixed memory leak from battles not being cleaned up after ending. 2026-01-17 03:42:08 -06:00
Instrafe 1989b8aa92 Implemented remodels for Ferroseed and Ferrothorn 2026-01-16 17:06:45 -05:00
Hiroku 948ca61ef3 Merge branch 'fix-super-potion-ingredients-tag' into 'main'
Fix super potion ingredients tag

See merge request cable-mc/cobblemon!2121
2026-01-16 08:27:19 +00:00
Hiroku e60aca30ba Merge branch 'patch-2' into 'main'
Fix Syrupy Apple missing from evolution items tag

See merge request cable-mc/cobblemon!2120
2026-01-16 08:26:22 +00:00
Hiroku fb5055c39e Merge branch 'form_crash' into 'main'
Initialize FormData when updating

Closes #1916

See merge request cable-mc/cobblemon!2122
2026-01-16 08:20:23 +00:00
Paul dfb5650121 Merge remote-tracking branch 'origin/main' 2026-01-16 18:17:01 +10:00
Paul 8a0cbf28fe Fixed battles not ending fully when a switch is in progress, closes #1917 2026-01-16 18:16:11 +10:00
aeroeng15 a6b9060d57 Changelog 2026-01-15 20:54:54 -07:00
aeroeng15 8e57995e44 Make It Proper 2026-01-15 20:51:59 -07:00
aeroeng15 1d8257808c Initialize rather than set; seems to fix the issue 2026-01-15 20:42:14 -07:00
Carmendar e290fe9c75 Modified Hover expressions and buffs/nerfs for hover mounts. 2026-01-15 21:23:34 -06:00
Jerzykv 06a50cfeeb Changelog for the cottons 2026-01-15 11:12:17 +01:00
Jerzykv 00888c8ad5 Implemented Cottonee and Whimsicott remodels 2026-01-15 11:11:44 +01:00
Faintful e3582ccfb4 Add changelog 2026-01-14 19:49:11 +01:00
Faintful 4c6822b1c6 Merge remote-tracking branch 'upstream/main' into fix-super-potion-ingredients-tag 2026-01-14 19:46:54 +01:00
Faintful 2e6a4c7341 Change Hondew for Aguav 2026-01-14 19:28:49 +01:00
Micky 8edb5e1013 Fix Syrupy Apple missing from evolution items tag 2026-01-14 18:25:06 +00:00
Polymeta 6fb139cbb9 Merge branch 'pokemon-interactions-fixes' into 'main'
Pokémon Interaction fixes

See merge request cable-mc/cobblemon!2117
2026-01-14 00:03:33 +00:00
lionandPolymeta c81c95142b Pokémon Interaction fixes 2026-01-14 00:03:32 +00:00
Hiroku afde822860 Edit CHANGELOG.md [skip-ci] 2026-01-12 10:50:47 +00:00
Hiroku 474c386535 Merge branch 'main' into 'main'
Fix Fresh Start Mochi EV Reset

See merge request cable-mc/cobblemon!2115
2026-01-12 10:48:40 +00:00
RedGenesectNinja 3963e83717 Implemented Ducklett and Swanna's cries with placeholder animations 2026-01-12 01:04:46 -06:00
Konoha 7893e365da yolo: drop item on give_item if inventory is full & sneak in a fix for active_dialogue 2026-01-11 18:50:52 -05:00
ToothCollector 71b9ae8dee add fix to CHANGELOG.md 2026-01-11 21:31:22 +02:00
ToothCollector b87b47d62a Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2026-01-11 21:28:04 +02:00
Hiroku 2bb85718d7 Merge branch 'main' into 'main'
Fix Adorn compatibility

See merge request cable-mc/cobblemon!2112
2026-01-11 12:21:25 +00:00
Tate Frost ee187d7b89 Delete file + update Adorn resource pack metadata
- Delete config file that was committed in error
- Update Adorn resource pack metadata to reflect new wood type
2026-01-10 22:30:27 -06:00
Hiroku b6a7051b2c Fixed galarian weezing blink crash, closes #1800 2026-01-11 04:28:37 +00:00
Tate Frost 3532cac278 Update CHANGELOG.md 2026-01-10 22:18:01 -06:00
Hiroku 1447ab25b4 Merge branch 'CombeeWorldGenFix' into 'main'
CombeeWorldGenFix

See merge request cable-mc/cobblemon!2071
2026-01-11 02:55:56 +00:00
Hiroku 9a06ca78c6 Merge branch 'fix-mongo-driver' into 'main'
Fix MongoDB ClassNotFoundException by including drivers at runtime

See merge request cable-mc/cobblemon!2098
2026-01-11 02:47:10 +00:00
Hiroku f6ba70634b Merge branch 'double-battle-gimmicks' into 'main'
Fix double battle gimmicks not being processed

See merge request cable-mc/cobblemon!2094
2026-01-11 02:46:36 +00:00
Hiroku 148fd04a93 Merge branch 'molang-evaluate_expression' into 'main'
molang addition: exception handling for run_molang

See merge request cable-mc/cobblemon!2102
2026-01-11 02:12:07 +00:00
Hiroku 17f60623ee Merge branch 'effect-id-regex-update' into 'main'
Custom messages for datapack moves and abilities

See merge request cable-mc/cobblemon!2106
2026-01-11 02:08:42 +00:00
Hiroku c5ece27c11 Merge branch '1861-galarien_mrmime_mount' into 'main'
Fix Galarian Mr. Mime incorrectly being rideable.

Closes #1861

See merge request cable-mc/cobblemon!2107
2026-01-11 02:02:14 +00:00
Hiroku 296ccd1a76 Merge branch 'main' into 'main'
Fix pasture block incorrect hit-box when facing north and south.

See merge request cable-mc/cobblemon!2109
2026-01-11 02:01:44 +00:00
ToothCollector 82a4f71f24 fix(FreshStartMochiItem.kt): Set all Pokemon's EVs to zero with the appropriate method. 2026-01-10 17:10:15 +02:00
Jerzykv 2cb1cf2f83 tiny fix on Togekiss 2026-01-10 13:36:40 +01:00
阿鱼andWeblate f13a4e0e24 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-01-08 18:45:37 +00:00
Dragonwhisper92 17e1bbd6a1 oops changelog extra '-' removed 2026-01-07 20:00:26 +00:00
Dragonwhisper92 c36ff0a0fe ball_icon resource location changes
Changed the resource location of pokeball icons in the gui to use the balls namespace instead of cobblemon, allowing mods to properly use their own name space for these textures
2026-01-07 19:57:36 +00:00
Tate Frost 08b58e2f78 Fix Adorn compatibility
- Fixed Apricorn block model rotation for counters/sinks/cupboards/drawers to match that of most recent version of Adorn
- Added Saccharine wood + Adorn compatibility
2026-01-07 02:04:52 -06:00
Merv 62d2b8db2d Edit CHANGELOG.md 2026-01-06 18:47:45 +00:00
Merv 3869f7ed93 Merge branch cobblemon:main into main 2026-01-05 23:09:08 +00:00
Jerzykv 8dbb350230 minor fix to apricorn textures 2026-01-05 21:56:02 +01:00
Dragonwhisper92 c9c44872d6 Updated Changelog. 2026-01-05 19:35:05 +00:00
Dragonwhisper92 cae53efedc Updated ActivateInstruction.kt to have an optional message parameter. 2026-01-05 19:14:40 +00:00
Jerzykv 52f9ffb1f6 fixed Politoed's drop again. thanks scraper 2026-01-05 12:23:07 +01:00
Polymeta 5fac24cee8 Merge branch 'Molang-SimpleDateFormat-Fix' into 'main'
Fixed Molang functions:

See merge request cable-mc/cobblemon!2105
2026-01-05 11:22:45 +00:00
Polymeta 1954dfec80 Merge branch '1784-riding_stats_order' into 'main'
Fix riding stat boosts to always render in the same order

Closes #1784

See merge request cable-mc/cobblemon!2108
2026-01-05 11:20:29 +00:00
Landon 2eea2b64c2 Merge branch 'bag-clause' into 'main'
Bag Clause (Preventing Item use in Battles)

See merge request cable-mc/cobblemon!1849
2026-01-05 08:12:36 +00:00
GDWR a127a93be8 Sort boosts when rendering to remove the jumping of stats between items
The sort uses the key. As `RidingStat` it sorts them by the enum value.
2026-01-04 20:27:42 +00:00
GDWR 9b32f85817 Set an empty riding definition for Galarian Mr. Mime to avoid inheriting the default definition 2026-01-04 17:28:02 +00:00
Dragonwhisper92 ec139d2677 Updated Effect.kt to use a custom REGEX entry 2026-01-04 16:46:44 +00:00
Landon Wills 4327be0cf9 Omit bag clause from getting sent to showdown 2026-01-04 02:22:32 -05:00
Landon Wills c25c832b31 Remove import 2026-01-04 01:21:20 -05:00
Landon Wills eabaabc535 Move bag clause check to bag items instead of pokemon entity 2026-01-04 01:19:26 -05:00
Dragonwhisper92 9acf97f5a0 Updated CHANGELOG.md 2026-01-04 00:18:24 +00:00
Dragonwhisper92 24fc212c76 Updated CHANGELOG.md 2026-01-04 00:15:32 +00:00
Hiroku 04f88bdd63 Merge branch 'form-change-move-sanitization-fix' into 'main'
Fix the improper sanitization of moves on mons with compounding properties

Closes #1853 and #1846

See merge request cable-mc/cobblemon!2085
2026-01-04 00:05:26 +00:00
Dragonwhisper92 a4eb58aa57 Fixed molang functions:
- date_local_time
- date_of
- date_is_after
too use the correct SimpleDateFormat of "dd/MM/yyyy"
2026-01-03 23:40:12 +00:00
LucasandWeblate 030195e3ff Translated using Weblate (French)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-03 08:52:44 +00:00
YasitoandWeblate d21a52ae68 Translated using Weblate (French)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-03 08:52:43 +00:00
YasitoandWeblate 3c1f1dbacb Translated using Weblate (French (Canada))
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-03 08:52:42 +00:00
Merv 6704d4e5ee Maybe fix pasture block incorrect hitbox 2026-01-03 02:05:03 +00:00
LucasandWeblate 787c4851ff Translated using Weblate (French)
Currently translated at 99.9% (9021 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:02:21 +00:00
YasitoandWeblate bf6e7fafd3 Translated using Weblate (French)
Currently translated at 99.9% (9021 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:02:20 +00:00
LucasandWeblate ea6df764c0 Translated using Weblate (French)
Currently translated at 99.9% (9020 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:01:41 +00:00
YasitoandWeblate f242de272d Translated using Weblate (French)
Currently translated at 99.9% (9020 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:01:41 +00:00
LucasandWeblate 5232e2e9bc Translated using Weblate (French)
Currently translated at 99.9% (9019 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:01:09 +00:00
YasitoandWeblate ca35898243 Translated using Weblate (French)
Currently translated at 99.9% (9019 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:01:08 +00:00
LucasandWeblate 869457d235 Translated using Weblate (French)
Currently translated at 99.9% (9018 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:00:52 +00:00
YasitoandWeblate 6736c8160b Translated using Weblate (French)
Currently translated at 99.9% (9018 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:00:51 +00:00
LucasandWeblate 243c50550d Translated using Weblate (French)
Currently translated at 99.9% (9017 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:00:37 +00:00
YasitoandWeblate fe7d3ef23a Translated using Weblate (French)
Currently translated at 99.9% (9017 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:00:36 +00:00
LucasandWeblate 57ad77fc39 Translated using Weblate (French)
Currently translated at 99.9% (9016 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 21:00:10 +00:00
LucasandWeblate d70e7bd1b3 Translated using Weblate (French)
Currently translated at 99.9% (9015 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:59:34 +00:00
YasitoandWeblate 254cc931ea Translated using Weblate (French)
Currently translated at 99.9% (9015 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:59:34 +00:00
LucasandWeblate 01fe5cffe8 Translated using Weblate (French)
Currently translated at 99.9% (9014 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:59:19 +00:00
YasitoandWeblate 3e18995a37 Translated using Weblate (French)
Currently translated at 99.9% (9014 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:59:18 +00:00
YasitoandWeblate ff161c9c9b Translated using Weblate (French)
Currently translated at 99.9% (9013 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:58:36 +00:00
LucasandWeblate 6b7b4f326d Translated using Weblate (French)
Currently translated at 99.9% (9013 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:58:35 +00:00
LucasandWeblate f50f7dec29 Translated using Weblate (French)
Currently translated at 99.8% (9012 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:58:17 +00:00
YasitoandWeblate a5def20c58 Translated using Weblate (French)
Currently translated at 99.8% (9012 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:58:16 +00:00
YasitoandWeblate b23204f9c9 Translated using Weblate (French)
Currently translated at 99.8% (9011 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:57:51 +00:00
LucasandWeblate 4972de0aaa Translated using Weblate (French)
Currently translated at 99.8% (9011 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:57:50 +00:00
LucasandWeblate 58a1f3d7b4 Translated using Weblate (French)
Currently translated at 99.8% (9010 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:57:33 +00:00
YasitoandWeblate f516edb858 Translated using Weblate (French)
Currently translated at 99.8% (9010 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:57:32 +00:00
LucasandWeblate 161aa952fa Translated using Weblate (French)
Currently translated at 99.8% (9008 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:43 +00:00
YasitoandWeblate 3b26a13ad6 Translated using Weblate (French)
Currently translated at 99.8% (9008 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:43 +00:00
LucasandWeblate 014189217a Translated using Weblate (French)
Currently translated at 99.8% (9007 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:29 +00:00
YasitoandWeblate 978debb40c Translated using Weblate (French)
Currently translated at 99.8% (9007 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:28 +00:00
LucasandWeblate 5be707811e Translated using Weblate (French)
Currently translated at 99.8% (9006 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:13 +00:00
YasitoandWeblate ac44a98f8a Translated using Weblate (French)
Currently translated at 99.8% (9006 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:55:12 +00:00
LucasandWeblate 03ecc2ae8d Translated using Weblate (French)
Currently translated at 99.8% (9005 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:54:13 +00:00
YasitoandWeblate 1518379107 Translated using Weblate (French)
Currently translated at 99.8% (9005 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:54:12 +00:00
YasitoandWeblate a58c8c59e1 Translated using Weblate (French)
Currently translated at 99.8% (9004 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:53:58 +00:00
LucasandWeblate 37959b5048 Translated using Weblate (French)
Currently translated at 99.8% (9004 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:53:58 +00:00
YasitoandWeblate cbcc9fc6f1 Translated using Weblate (French (Canada))
Currently translated at 99.7% (9002 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-02 20:53:57 +00:00
YasitoandWeblate d236aaa7da Translated using Weblate (French)
Currently translated at 99.7% (9000 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:50:35 +00:00
LucasandWeblate a2cd5a671c Translated using Weblate (French)
Currently translated at 99.7% (9000 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:50:35 +00:00
LucasandWeblate 1d6a45bf4d Translated using Weblate (French)
Currently translated at 99.7% (8998 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:49:59 +00:00
YasitoandWeblate 5bfe703ed7 Translated using Weblate (French)
Currently translated at 99.7% (8998 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:49:58 +00:00
YasitoandWeblate fc8485388e Translated using Weblate (French)
Currently translated at 99.7% (8997 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:45:38 +00:00
LucasandWeblate a5593b3131 Translated using Weblate (French)
Currently translated at 99.7% (8997 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:45:38 +00:00
YasitoandWeblate 9541bf3267 Translated using Weblate (French (Canada))
Currently translated at 99.6% (8991 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-02 20:45:37 +00:00
LucasandWeblate 1037fe0a0e Translated using Weblate (French)
Currently translated at 99.6% (8989 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:38:19 +00:00
YasitoandWeblate a6eea3198a Translated using Weblate (French)
Currently translated at 99.6% (8989 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:38:19 +00:00
YasitoandWeblate b28e2313a8 Translated using Weblate (French (Canada))
Currently translated at 99.6% (8988 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-02 20:38:19 +00:00
LucasandWeblate 0f0b4e6c86 Translated using Weblate (French)
Currently translated at 99.6% (8988 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:37:45 +00:00
YasitoandWeblate e033467054 Translated using Weblate (French)
Currently translated at 99.6% (8988 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:37:45 +00:00
YasitoandWeblate 0f092bf6bf Translated using Weblate (French (Canada))
Currently translated at 99.6% (8987 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-02 20:37:44 +00:00
LucasandWeblate 66ba762360 Translated using Weblate (French)
Currently translated at 99.6% (8987 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:37:01 +00:00
YasitoandWeblate c7c11838a0 Translated using Weblate (French)
Currently translated at 99.6% (8987 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2026-01-02 20:37:01 +00:00
YasitoandWeblate 301d80fabe Translated using Weblate (French (Canada))
Currently translated at 99.6% (8986 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-01-02 20:37:00 +00:00
Hiroku c88087de29 Merge branch 'main' into 'main'
Small improvements to the Battle AI

See merge request cable-mc/cobblemon!2101
2026-01-02 00:38:26 +00:00
Konoha fade5502d9 molang additions: Move struct accessor and player inventory utils 2026-01-01 15:31:40 -05:00
Konoha 86bb802140 adjustment: exception handling to run_molang 2026-01-01 11:44:29 -05:00
GGavino1 6b644b384d Updated CHANGELOG.md to include AI changes 2026-01-01 15:00:15 +00:00
Konoha c370af8914 feature: molang function for safe evaluation of booleans 2025-12-31 22:23:50 -05:00
GGavino1 1cd6d2bfc7 Edit StrongBattleAI.kt 2025-12-31 23:15:52 +00:00
Test fc720916fb wall block properties 2025-12-31 00:24:39 -05:00
Jerzykv ce55035408 changelog 2025-12-30 14:46:38 +01:00
Jerzykv d4665ddaeb implemented Falinks remodel 2025-12-30 14:44:46 +01:00
Jerzykv 4375c6f667 fixed Revavroom's hitbox. Who implemented this guy (it was me) 2025-12-30 14:43:24 +01:00
Konoha 1edeae2a60 fix: let's actually fix 2025-12-27 09:48:49 -05:00
mkh 409f1056b9 fix: Add MongoDB driver dependency to Fabric/NeoForge build configuration and shade it into the mod jar to resolve ClassNotFoundException. 2025-12-26 17:31:46 +08:00
Landon Wills d8c470db3d Oops, forgot a file 2025-12-23 01:28:50 -05:00
Landon Wills c642c4b84b Update changelog 2025-12-23 01:22:01 -05:00
Landon Wills 0b80f7e547 Make the gimmick buttons work with the back button 2025-12-23 01:18:35 -05:00
Landon Wills d543f45607 Fix double battle gimmicks not being processed, prevent people from trying to use the same gimmick multiple times on the same turn 2025-12-23 00:43:31 -05:00
Test e5699c1cdc plaques, policies 2025-12-22 01:34:10 -05:00
RedGenesectNinja f4a7c6e873 Implemented the remodels for Chingling and Chimecho 2025-12-21 22:40:17 -06:00
Konoha 2b4fdfd219 changelog: sanitization fix 2025-12-21 15:40:17 -05:00
geumuandWeblate 3bef1dad4a Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-20 19:34:20 +00:00
geumuandWeblate da50ee59cf Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-20 14:40:56 +00:00
Hiroku 1a3b655d3f Merge branch 'the-tiniest-of-tiny-changes' into 'main'
Edit pokemon_model.json

See merge request cable-mc/cobblemon!2091
2025-12-20 12:41:36 +00:00
dhi_holo 1f5f278970 Edit pokemon_model.json 2025-12-20 21:25:12 +10:00
Hiroku 0b4eb5239a Merge branch 'in-flight-dismounting-config' into 'main'
Add enable in-flight dismounting config

See merge request cable-mc/cobblemon!2079
2025-12-20 06:01:12 +00:00
lionandHiroku 118e8f6205 Add enable in-flight dismounting config 2025-12-20 06:01:12 +00:00
Hiroku cf126c5906 Merge branch 'molang-is-love-molang-is-life' into 'main'
Fixed the `run_script` Molang function to not fail if the environment's context is null.

See merge request cable-mc/cobblemon!2090
2025-12-20 01:59:22 +00:00
Spicy 645cf61911 Fixed the run_script Molang function to not fail if the environment's context is null. 2025-12-19 19:48:27 -06:00
arvid6andWeblate 6e8985d97e Translated using Weblate (Spanish)
Currently translated at 84.2% (7599 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-12-19 08:15:16 +00:00
geumuandWeblate 19c4c0d50d Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-19 08:15:15 +00:00
阿鱼andWeblate b23110950e Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-12-19 08:15:14 +00:00
Watuneni iteiteandWeblate 60c213c44b Translated using Weblate (Japanese)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-12-19 08:15:13 +00:00
Hiroku 87d5ede3dd Merge branch 'RestoreCombeeLeavesPathing' into 'main'
RestoreCombeeLeavesPathing

See merge request cable-mc/cobblemon!2046
2025-12-19 07:36:50 +00:00
Hiroku 203e5d89cd Merge branch 'label-consistency' into 'main'
Who are we if not consistent?

See merge request cable-mc/cobblemon!1985
2025-12-19 07:36:21 +00:00
Konoha ce611cc61c cleanup: fix redundancies in form change sanitization 2025-12-18 21:17:48 -05:00
geumuandWeblate 3df8dea71b Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-18 17:09:19 +00:00
geumuandWeblate 3cca50b7cc Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-18 07:49:53 +00:00
geumuandWeblate 1ff5622694 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-18 06:01:40 +00:00
JazzMcNade f6ad7ff865 Fix changelog 2025-12-17 21:08:46 -06:00
JazzMcNade d69627a9c8 Merge branch 'main' into CombeeWorldGenFix 2025-12-17 20:05:09 -06:00
Alexis SalgueiroandWeblate 411f388bd3 Translated using Weblate (French)
Currently translated at 99.5% (8984 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-12-18 00:21:32 +00:00
geumuandWeblate 5b8d35604a Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-18 00:21:30 +00:00
geumuandWeblate 619d002bc4 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-17 07:19:25 +00:00
geumuandWeblate 7049656f64 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-16 06:33:05 +00:00
geumuandWeblate e6e7c2982d Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-15 12:47:01 +00:00
JazzMcNade 54e8763470 Fixed a bug in which Combees could become stuck inside of leaves 2025-12-13 21:17:59 -06:00
JazzMcNade db34687df0 Merge branch 'main' into RestoreCombeeLeavesPathing 2025-12-13 21:11:57 -06:00
Konoha 7f008d85ff fix: form_change move updates no longer sanitize the sets of compounding property mons 2025-12-13 18:37:37 -05:00
Polymeta 06ebf51078 Merge branch 'Wii-Break-It-Wii-Fix-It' into 'main'
Boat Tags and Mint Leaves

Closes #1855

See merge request cable-mc/cobblemon!2082
2025-12-13 22:14:32 +00:00
Polymeta 39c5bcd73c Merge branch 'even-more-seasonings' into 'main'
Adding Missing Effects for Seasonings

See merge request cable-mc/cobblemon!1991
2025-12-13 22:13:24 +00:00
CeejayandPolymeta 8f5f158b60 Adding Missing Effects for Seasonings 2025-12-13 22:13:24 +00:00
dhi_holo 3bc1e35125 Update CHANGELOG.md 2025-12-13 18:42:50 +10:00
dhi_holo 460bedddae Boat Tags
Resolves #1855
2025-12-13 18:12:24 +10:00
dhi_holo 8b64c315f3 Merge branch 'main' into Wii-Break-It-Wii-Fix-It 2025-12-13 17:51:15 +10:00
dhi_holo 7c824a2b72 OVERSIGHTS 2025-12-13 17:50:57 +10:00
Paul f197ac5077 Removed a bunch of duplicates from is_held_item because it looks whack. 2025-12-12 16:24:30 +10:00
Hiroku e0be669482 Merge branch 'patch-1' into 'main'
Fix Soothe Bell not tagged as `cobblemon:held/is_held_item`

See merge request cable-mc/cobblemon!2081
2025-12-12 06:17:17 +00:00
Watuneni iteiteandWeblate 82686b0511 Translated using Weblate (Japanese)
Currently translated at 99.4% (8976 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-12-12 03:20:38 +00:00
JPAKx4 f4eca30163 Fix merge issues 2025-12-11 10:40:53 -06:00
arvid6andWeblate 2814183012 Translated using Weblate (Spanish)
Currently translated at 84.2% (7597 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-12-11 16:38:11 +00:00
JPAKx4 bbc3c2a8d6 Merge branch 'main' into default-offest
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/camera/MountedCameraRenderer.kt
2025-12-11 10:27:50 -06:00
geumuandWeblate 7eeb7feeae Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-11 06:27:41 +00:00
geumuandWeblate b2f850c1c8 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-10 15:57:39 +00:00
Micky 08ea21fff4 Fix Soothe Bell not tagged as cobblemon:held/is_held_item 2025-12-10 13:27:23 +00:00
JazzMcNade 9dac604a93 Merge branch 'main' into RestoreCombeeLeavesPathing 2025-12-10 00:20:49 -06:00
Nico MerenqandWeblate 23db346070 Translated using Weblate (Russian)
Currently translated at 77.7% (7012 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-12-09 19:49:13 +00:00
geumuandWeblate d3b7d739bb Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-09 19:49:11 +00:00
Polymeta 28c0f8ab7d Merge branch 'item-interactions-sync' into 'main'
Item interactions sync

See merge request cable-mc/cobblemon!2068
2025-12-08 16:08:48 +00:00
lionandPolymeta fcbe0db568 Item interactions sync 2025-12-08 16:08:48 +00:00
Polymeta 83cefe476e Merge branch 'partner-mark' into 'main'
Add Partner Mark

See merge request cable-mc/cobblemon!2034
2025-12-08 15:57:21 +00:00
Guilherme Pereira 690ccbd260 refactor check riding statistics method for server player mixin 2025-12-08 12:06:20 -03:00
Polymeta 63846d53fb Merge branch 'main' into 'main'
Fixed shiny Pokémon not respecting the silent flag on spawn.

See merge request cable-mc/cobblemon!2064
2025-12-08 13:07:25 +00:00
Polymeta 86cb865742 fixed mochi items being consumed twice
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-12-08 13:01:14 +00:00
Polymeta 1a0c9b6a82 Merge branch 'malformed-jsons-no-more' into 'main'
Fixes a crash caused by malformed riding prespectives json file when trying to ride a pokemon

See merge request cable-mc/cobblemon!2077
2025-12-08 12:59:22 +00:00
Polymeta 985eb103fe Merge branch 'sterr/ride-sound-changes' into 'main'
New ride sound stuff

See merge request cable-mc/cobblemon!2065
2025-12-08 11:44:22 +00:00
Polymeta aec76a78da Merge branch 'restore_adorn_texturing' into 'main'
Restore adorn texturing

Closes #1833

See merge request cable-mc/cobblemon!2078
2025-12-08 11:36:41 +00:00
geumuandWeblate eb6c134f4b Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-08 10:48:16 +00:00
Jacob Hartmann 17f3edf2be Changelog 2025-12-07 20:35:11 -07:00
Jacob Hartmann 9c5bfda20f Updating texture paths 2025-12-07 20:34:09 -07:00
geumuandWeblate aa0b8d88e8 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-07 12:09:09 +00:00
Gertimoshka 6d545fc1ca Fixes a crash caused by malformed riding prespectives json file when trying to ride a pokemon 2025-12-07 11:47:04 +02:00
Deltric 3cf9425247 Fixed memory leak from battles not being cleaned up after ending. 2025-12-06 01:35:53 -06:00
Carmendar b5578e72bd Merge branch 'main' into implement_pokemon 2025-12-05 01:30:51 -06:00
Carmendar 3aea0909a4 Merge branch 'main' into implement_pokemon 2025-12-05 01:27:25 -06:00
TeggyandWeblate 5adef3336e Translated using Weblate (Spanish)
Currently translated at 84.2% (7597 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-12-05 06:23:53 +00:00
geumuandWeblate 1a96b418bc Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-05 06:23:51 +00:00
geumuandWeblate e373e86b60 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-04 15:33:38 +00:00
geumuandWeblate 6f3035184c Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-04 09:30:13 +00:00
Jerzykv 7ae8b1957c fixed Arcanine's texture having a line 2025-12-04 10:27:42 +01:00
geumuandWeblate 824b9718ee Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-04 08:07:32 +00:00
JazzMcNade 8540a73885 Fix for error occurring while populating bee nests with pokemon on world gen 2025-12-04 01:38:56 -06:00
Khvrram 7e6ca7be5e Merge branch cobblemon:main into main 2025-12-04 01:59:17 +00:00
geumuandWeblate ef43f6c087 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-03 21:23:55 +00:00
geumuandWeblate c8a70caf79 Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-03 16:30:22 +00:00
Neovitalism 25ce784a60 Hopefully fixes changelog
Idk why gitlab didn't detect that when merging main
2025-12-03 10:17:48 -06:00
Neovitalism 5358cccb62 Merge branch 'main' into label-consistency 2025-12-03 10:09:41 -06:00
geumuandWeblate ba11758e1f Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-03 12:53:16 +00:00
geumuandWeblate f0412d6b1d Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-03 10:05:08 +00:00
Jerzykv f5f87ce1a3 fixed Dewgong swimming in battle 2025-12-03 06:57:40 +01:00
Jerzykv f28b269734 fixed Camerupt jumping in battle 2025-12-03 06:57:31 +01:00
Jerzykv d545efd843 adjusted Tinkaton & Tinkatuff profile scaling 2025-12-03 06:57:07 +01:00
Zack the GhastandWeblate f8682c2123 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.2% (8866 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-12-02 05:47:12 +00:00
geumuandWeblate f62e31a1eb Translated using Weblate (Korean)
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-02 05:46:50 +00:00
Watuneni iteiteandWeblate ea623b08a6 Translated using Weblate (Japanese)
Currently translated at 99.4% (8971 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-12-02 05:46:48 +00:00
Sterrezo a575c6bc3d Update CHANGELOG.md 2025-12-01 15:47:34 +01:00
Sterrezo 9c102a7850 Merge branch 'main' into sterr/ride-sound-changes 2025-12-01 15:45:40 +01:00
Sterrezo 22a1eaab19 Volume balancing 2025-12-01 15:35:22 +01:00
Sterrezo 0118ace5fc Implemented stereo flight loops 2025-12-01 15:09:37 +01:00
Sterrezo d4a93abbf7 Added support for submerged ride sounds 2025-12-01 14:27:55 +01:00
geumuandWeblate ce3d5a413c Translated using Weblate (Korean)
Currently translated at 95.8% (8650 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-01 13:03:30 +00:00
Sterrezo d0ee3f3e9d Added an option for ride sounds to not play for the passengers 2025-12-01 13:56:56 +01:00
JazzMcNade 698fa9dff9 Merge branch 'main' into RestoreCombeeLeavesPathing 2025-12-01 00:46:50 -06:00
geumuandWeblate d23fd6e23c Translated using Weblate (Korean)
Currently translated at 84.1% (7595 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-12-01 05:33:07 +00:00
Khvrram 46ae53f565 Merge branch cobblemon:main into main 2025-12-01 04:25:52 +00:00
geumuandWeblate 59395a254d Translated using Weblate (Korean)
Currently translated at 81.2% (7328 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-11-30 20:59:27 +00:00
geumuandWeblate 5d7c6c8d6f Translated using Weblate (Korean)
Currently translated at 80.6% (7276 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-11-30 08:10:30 +00:00
Khurram Valiyev 4d123b6e4b Fixed shiny Pokémon not respecting the silent flag on spawn. 2025-11-29 22:50:52 -05:00
violet 6529293d21 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-11-29 02:44:47 -05:00
violet a1fce624ea something was forgotten in the changelog... 2025-11-29 02:44:34 -05:00
geumuandWeblate b7773f7471 Translated using Weblate (Korean)
Currently translated at 79.1% (7144 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-11-28 23:27:54 +00:00
geumuandWeblate f927e47bfe Translated using Weblate (Korean)
Currently translated at 77.9% (7030 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-11-28 15:25:08 +00:00
Watuneni iteiteandWeblate 48a37b2018 Translated using Weblate (Japanese)
Currently translated at 99.2% (8953 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-11-28 15:25:07 +00:00
Guilherme Pereira 29b560d244 Merge branch 'main' of gitlab.com:cable-mc/cobblemon into partner-mark 2025-11-27 18:21:19 -03:00
阿鱼andWeblate f6b2da6ed6 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9022 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-11-27 11:09:47 +00:00
Watuneni iteiteandWeblate 275779b20e Translated using Weblate (Japanese)
Currently translated at 99.2% (8953 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-11-26 22:24:14 +00:00
阿鱼andWeblate 2102f8ff88 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.6% (8991 of 9022 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-11-26 15:42:52 +00:00
JazzMcNade a8949cf1f9 changelog 2025-11-25 21:49:38 -06:00
JazzMcNade 61db6334f9 Restores Combee pathing through leaves 2025-11-25 21:37:57 -06:00
Guilherme Pereira 7cedfb9047 update CHANGELOG.md 2025-11-22 22:02:14 -03:00
Guilherme Pereira a7ab2e37c2 Merge branch 'main' of gitlab.com:cable-mc/cobblemon into partner-mark 2025-11-22 21:57:49 -03:00
Guilherme Pereira 4723f91500 add riding stats 2025-11-22 21:57:25 -03:00
Guilherme Pereira e23aa6b066 add partner mark molang callback 2025-11-22 19:39:21 -03:00
JPAKx4 8f462aab40 Add default_seat option to seat offset posers, and remove shouldRotate as it's not needed for positioning the camera. 2025-11-18 00:56:40 -06:00
Neovitalism 46d278ca55 key \:) 2025-11-17 03:18:02 -06:00
Neovitalism 339038956d Support for deserialization from any key
...I think.
2025-11-17 03:09:21 -06:00
Neovitalism 116a93d8cc Update CHANGELOG.md 2025-11-16 04:19:32 -06:00
Neovitalism acebc35d3f Who are we if not consistent?
Before, this property was called `PokemonFlagProperty`, had the key `tag`, and checked pokémon's labels. It was also in a package by itself.

Now, it's moved down into the properties package, renamed to LabelProperty, and the key "label" was added, keeping "tag" as a secondary for those that are using it currently.
2025-11-16 04:10:45 -06:00
Konoha aee89f1c92 Preliminary Bag Clause implementation 2025-09-09 18:15:37 -04:00
1069 changed files with 53779 additions and 7250 deletions
+15 -12
View File
@@ -1,25 +1,28 @@
# Changelog
## [1.7.2 (MONTH Xth, YEAR)](#1-7-2)
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
### Additions
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
### New rideable Pokémon
### Pokémon Added
### Model updates for the following Pokémon
### Animation updates for the following Pokémon
### Changes
### Fixes
- Fixed orientation not getting reset after crashing while flying and getting back on the Pokémon.
- Fixed Furfrou not being trimmable on NeoForge.
- Fixed honey from Saccharine Leaves being harvestable at less than the max age.
- Items given from interacting with Saccharine Leaves are placed in the active hotbar slot if possible.
- Fix all Pokémon being saved to chunks and never despawning.
- Fixed PokeSnacks crashing if there was no available spawn.
- Fixed Pokémon spawn influences not being specific to regional forms
- Fixed crashing when viewing another player in spectator mode
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
- Fix occasional crash on retrieving revived fossils
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
- Fixed improper validation of Pokémon when performing a trade.
### Developer
### Molang & Datapacks
### Particles
### Localization
- Updated translations for:
- Updated translations for:
+137
View File
@@ -0,0 +1,137 @@
# Changelog
## [1.7.2 (January 25th, 2026)](#1-7-2)
### Additions
- Added Saccharine wood furniture for Adorn.
- Added the Partner Mark, which party Pokémon have a very small chance to earn as the player walks, provided they have enough friendship.
- Added decorative block models for existing items: Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Ether, Elixir, Max Ether, Max Elixir, X Accuracy, X Attack, X Defence, X Special Attack, X Special Defence, X Speed, Dire Hit, Guard Spec, Weakness Policy, Blunder Policy, Cleanse Tag, and Spell Tag.
- Added decorative sign blocks called Plaques.
- Added ride style labels to ride stats in Pokédex, as well as ride style icons in the summary GUI.
- Added more seasonings for sinister tea:
- Mental Herb - Mental Restoration I for 10 seconds, which staves off Insomnia.
- White Herb - Removes negative effects.
- Milk, Moomoo Milk - Removes effects.
- Added dispenser support for:
- Applying a Honey Bottle to Saccharine Logs.
- Washing honey off of Saccharine Slathered Logs.
- Applying and removing honey on Saccharine Leaves.
- Added riding statistics: `Distance by Pokémon on Land`, `Distance by Pokémon in Air` and `Distance by Pokémon in Liquid`.
### New rideable Pokémon
- Honchkrow
- Mantine
### Pokémon Added
#### Gen 5
- Throh
- Sawk
#### Gen 6
- Litleo
- Pyroar
- Spritzee
- Aromatisse
- Swirlix
- Slurpuff
### Model updates for the following Pokémon
- Falinks
- Cottonee
- Whimsicott
- Ferroseed
- Ferrothorn
- Chingling
- Chimecho
### Animation updates for the following Pokémon
- Mantine (ride animations)
- Flygon (ride jump animation)
- Honchkrow (ride animations)
- Croagunk
- Toxicroak
### Changes
- Added a new config option, `Enable In-Flight Dismounting` (default: off), which lets you dismount while riding a Pokémon in the air. This is the textbook definition of caveat emptor.
- Ride sounds have been separated into stereo for passengers and mono for other players. Riding will now sound more spacious.
- Battle AI now uses a smarter threshold for switching, reducing unnecessary switches.
- AI will always use the most damaging move when at low HP and when it is not switching, improving endgame decision-making.
- Items given from interacting with Saccharine Leaves are placed in the active hotbar slot if possible.
- Added `label` as an alternate key to the `tag` property.
### Fixes
- Fixed crashing when viewing another player in spectator mode. Some said this was reason to release this update a month ago. They were probably right.
- Fixed crashing when sending a Pokémon out after editing its form.
- Fixed all Pokémon being saved to chunks and never despawning even when they should be.
- Fixed Poké Snacks crashing if there was no available spawn.
- Fixed Poké Snack / Bait effects so that EV Yield, Type, and Egg Group filters now all apply together when weighting spawns. And you thought you were going crazy.
- Fixed Furfrou not being trimmable on NeoForge specifically.
- Fixed honey from Saccharine Leaves being harvestable at less than the max age.
- Fixed possible error on world generation when Combee are placed in naturally generated bee nests.
- Fixed Galarian Weezing crashing the world if it decides to blink the wrong way. The same could happen to you!
- Fixed Galarian Mr. Mime incorrectly being rideable. Some would argue that regular Mr. Mime shouldn't be rideable, but that would make them wrong.
- Fixed missing apricorn textures for Adorn blocks.
- Fixed Apricorn block model rotation for counters/sinks/cupboards/drawers to match that of most recent version of Adorn.
- Fixed item interaction sometimes not working properly when playing on servers.
- Fixed Mint Leaves not being usable for filling the Resurrection Machine.
- Fixed Super Potion recipes incorrectly using Hondew Berry instead of Aguav Berry. They look similar, to be fair.
- Fixed Wishiwashi not schooling properly.
- Fixed battle item duplication issue. How did it work? Wouldn't you like to know, weather boy.
- Fixed the ordering of Aprijuice's riding stat boosts so they always have the same order.
- Fixed the movesets of certain Pokémon being incorrectly sanitized upon reload, causing some forms to keep losing moves.
- Fixed selected leading Pokémon in battle not being used when levels are set.
- Fixed the bottom half of pasture blocks having an incorrect hit-box when facing north or south.
- Fixed gimmicks not being usable in double and triple battles.
- Fixed gimmick buttons showing when already used in the same turn in double and triple battles.
- Fixed mochi items consuming two items at a time.
- Fixed Fresh Start Mochi's EV reset not syncing health changes properly.
- Fixed Pokémon spawn influences not being specific to regional forms.
- Fixed memory leak from battles not being cleaned up after ending.
- Fixed orientation not getting reset after crashing while flying and getting back on the Pokémon.
- Fixed shiny Pokémon not respecting the `silent` flag on spawn.
- Fixed 'Plain' prefix not being applied to all low-quality Aprijuice.
- Fixed Pokémon interactions not playing sound effects when the volume was not specified.
- Fixed Pokémon interaction cooldowns taking longer than intended when the Pokémon was in the player’s party.
- Fixed Pokémon interactions occasionally crashing the game when spamming an interaction with a 0 cooldown.
- Fixed Pokémon fullness decreasing more than intended when the Pokémon was pastured.
- Fixed Scraggy line, Milcery line, and Morpeko spawning on unnatural blocks.
- Fixed invulnerability-bypassed damage being resisted by invulnerable NPC entities.
- Fixed Fabric network packets not being handled immediately, fixing incompatibilities with Supplementaries, Prometheus, Sleep Tight, and others.
- Fixed killer not being set early enough on a wild Pokémon entity from `WinInstruction`.
- Fixed requirements such as `biomeCondition` crashing the game when used to define Pokémon interactions.
- Fixed an issue that prevented registering custom dispenser behaviours (no longer overrides vanilla behaviour).
- Fixed an issue navigation that prevented Combee from pathing into and out of Saccharine Leaves.
- Added Saccharine Boats and Saccharine Boat with Chests to relevant boat tags.
- Fixed Soothe Bell not being properly tagged as a held item.
- Fixed `/calculateseatpositions` expecting a locator format that even we don't use. It needs an underscore after "seat".
- Fixed the `run_script` Molang function to not fail if the environment's context is null.
- Fixed the Molang functions `date_local_time`, `date_of`, `date_is_after` to use the correct date format.
### Developer
- Changed the `owner` parameter in the `OwnerQueryRequirement` interface from `ServerPlayer` to `Player`. This method is now also called on the client to verify whether a Pokémon interaction succeeded, so make sure to update your implementations to handle both server and client contexts.
- Added the `pnx` to the `BattleFaintedEvent` and `FormeChangeEvent`.
- Added register methods for custom instructions to `ShowdownInterpreter`.
- Changed callback operations in `BattlePokemon` to allow multiple callbacks and be mutable.
- Added `display_name`, `description`, and `max_pp` functions to the `MoveTemplate` struct.
- Changed the implementations of `AIBattleActor` to be open classes to allow extension.
- Changed the resource location of Poké Ball icons in the GUI to use the balls namespace instead of Cobblemon's, allowing mods to properly use their own namespace for these textures.
### Molang & Datapacks
- Ride sounds can now be set as exclusive to passengers.
- Ride sounds no longer play when submerged unless specified with a new setting.
- Exception handling has been added to `run_molang`, resolving some crashes caused by malformed MoLang expressions.
- Added the `create_itemstack`, `has_inventory_space`, `set_inventory_slot`, and `give_item` MoLang functions for item and inventory utility.
- Added the `get_move_from_id` MoLang function for general move info queries without requiring a Moveset object to access.
- Added support for optional message variants in battle activate instructions for more context-specific battle text.
- Example: `this.add('-activate', pokemon, 'ability: example', '[msg]message1');` will parse to lang key `cobblemon.battle.activate.example.message1`.
### Localization
- Updated translations for:
- French
- Canadian French
- Simplified Chinese
- Ukrainian
- Brazilian Portuguese
- Spanish
- Korean
- Japanese
+23
View File
@@ -0,0 +1,23 @@
# Changelog
## [1.7.3 (January 31st, 2026)](#1-7-3)
### Fixes
- Fixed mounted Pokémon not playing sounds.
- Fixed players failing to join servers randomly.
- Fixed some Pokémon having no stamina for flying.
- Fixed `/pctake` not respecting the actual size of the PC.
### Developer
- Poké Ball GUI textures now respect the ball's namespace, enabling proper texture loading for custom balls from other mods.
### Molang & Datapacks
- Added a `chance` requirement type for Pokémon interactions.
### Localization
- Updated translations for:
- French
- Canadian French
- Korean
- Portuguese
- Simplified Chinese
- Spanish
@@ -11,7 +11,9 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonMemories;
import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
import com.cobblemon.mod.common.api.pokemon.PokemonProperties;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.world.feature.SaccharineTreeFeature;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.tags.BlockTags;
@@ -42,7 +44,21 @@ public abstract class BeeOccupantMixin {
if (cir.getReturnValue() == null) {
final BeehiveBlockEntity.Occupant occupant = (BeehiveBlockEntity.Occupant) (Object) this;
CompoundTag compoundTag = occupant.entityData().copyTag();
Entity entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
String id = compoundTag.getString("id");
Entity entity = null;
if (id.equals(SaccharineTreeFeature.Companion.getPOKEMON_PLACEHOLD_ID())) {
// Special case for Pokemon stuffed into a beenest upon world gen.
// For technical reasons we cannot create them as full entities during world gen.
// So we look for this special id tag and complete the entity when it attempts to exit a hive.
if (compoundTag.contains("pokemonProperties")) {
String properties = compoundTag.getString("pokemonProperties");
PokemonProperties pokemon = PokemonProperties.Companion.parse(properties);
entity = pokemon.createEntity(level);
}
} else {
entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
}
if (entity instanceof PokemonEntity pokemonEntity) {
var state = level.getBlockState(pos);
var newPos = Vec3.ZERO;
@@ -11,11 +11,15 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.PlayerSpawnerAccessor;
import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.api.stats.CobblemonStats;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -89,4 +93,32 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
return original && !(entity instanceof PokemonEntity);
}
@WrapOperation(method = "checkRidingStatistics", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getVehicle()Lnet/minecraft/world/entity/Entity;"))
public Entity cobblemon$checkRidingStatistics(ServerPlayer player, Operation<Entity> original, @Local int distance) {
var entity = original.call(player);
if (!(entity instanceof PokemonEntity pokemonEntity)) {
return entity;
}
var ridingController = pokemonEntity.getRidingController();
if (ridingController == null) return entity;
var activeContext = ridingController.getContext();
if (activeContext == null) return entity;
var ridingStyle = activeContext.getStyle();
CobblemonStats.CobblemonStat stat = switch (ridingStyle) {
case RidingStyle.LAND -> CobblemonStats.RIDING_LAND;
case RidingStyle.AIR -> CobblemonStats.RIDING_AIR;
case RidingStyle.LIQUID -> CobblemonStats.RIDING_LIQUID;
default -> null;
};
if (stat != null) {
player.awardStat(CobblemonStats.getStat(stat), distance);
}
return entity;
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.world.item.Item;
import net.minecraft.core.dispenser.DispenseItemBehavior;
import net.minecraft.world.level.block.DispenserBlock;
import java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(DispenserBlock.class)
public interface DispenserBlockAccessor {
@Accessor("DISPENSER_REGISTRY")
static Map<Item, DispenseItemBehavior> getItemBehaviors() {
throw new AssertionError();
}
}
@@ -35,6 +35,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts;
import static com.google.common.primitives.Floats.min;
import static net.minecraft.util.Mth.lerp;
@@ -85,11 +86,13 @@ public abstract class MouseHandlerMixin {
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
} else if (i < 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
}
}
}
@@ -42,8 +42,8 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningZoneGenerator
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.stats.CobblemonStats
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
@@ -65,10 +65,14 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
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.block.DispenserBehaviorRegistry
import com.cobblemon.mod.common.block.dispenser.DispenserBehaviorRegistry
import com.cobblemon.mod.common.client.CobblemonPack
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
@@ -77,8 +81,8 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.CallbackHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.events.StatHandler
@@ -95,11 +99,23 @@ import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatu
import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatureProvider
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.*
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.requestWallpapers
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -107,6 +123,17 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.resources.ResourceKey
@@ -119,17 +146,6 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.LevelResource
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
@@ -170,7 +186,7 @@ object Cobblemon {
val statistics: CobblemonStats = CobblemonStats
@JvmStatic
val builtinPacks = listOf<CobblemonPack>(
val builtinPacks = listOf(
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),
@@ -206,6 +222,7 @@ object Cobblemon {
implementation.registerEntityDataSerializers()
implementation.registerCriteria()
implementation.registerEntitySubPredicates()
implementation.registerMobEffects()
DispenserBehaviorRegistry.registerDispenserBehaviors()
DropEntry.register("command", CommandDropEntry::class.java)
@@ -319,7 +336,7 @@ object Cobblemon {
CustomPokemonProperty.register(UncatchableProperty)
CustomPokemonProperty.register(BattleCloneProperty)
CustomPokemonProperty.register(PokemonFlagProperty)
CustomPokemonProperty.register(LabelProperty)
CustomPokemonProperty.register(HiddenAbilityPropertyType)
CustomPokemonProperty.register(AspectPropertyType)
CustomPokemonProperty.register(UnaspectPropertyType)
@@ -455,7 +472,7 @@ object Cobblemon {
server()?.getLevel(dimension)
} else {
val mc = Minecraft.getInstance()
return mc.singleplayerServer?.getLevel(dimension) ?: mc.level
mc.singleplayerServer?.getLevel(dimension) ?: mc.level
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.block.*
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
import com.cobblemon.mod.common.block.LecternBlock
@@ -41,11 +42,11 @@ import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.BlockSetType
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
import net.minecraft.world.level.block.state.properties.WoodType
import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.minecraft.world.phys.shapes.Shapes
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
@@ -243,9 +244,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
@JvmField
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, false))
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, false))
@JvmField
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
@@ -477,6 +478,99 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val GIMMIGHOUL_CHEST = create("gimmighoul_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.FAKE))
@JvmField
val WHITE_PLAQUE = create("white_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val LIGHT_GRAY_PLAQUE = create("light_gray_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val GRAY_PLAQUE = create("gray_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val BLACK_PLAQUE = create("black_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val BROWN_PLAQUE = create("brown_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val RED_PLAQUE = create("red_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val ORANGE_PLAQUE = create("orange_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val YELLOW_PLAQUE = create("yellow_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val LIME_PLAQUE = create("lime_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val GREEN_PLAQUE = create("green_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val CYAN_PLAQUE = create("cyan_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val LIGHT_BLUE_PLAQUE = create("light_blue_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val BLUE_PLAQUE = create("blue_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val PURPLE_PLAQUE = create("purple_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val MAGENTA_PLAQUE = create("magenta_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val PINK_PLAQUE = create("pink_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
@JvmField
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
@JvmField
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
@JvmField
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
@JvmField
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
@JvmField
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
@JvmField
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
@JvmField
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
@JvmField
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
@JvmField
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
@JvmField
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
@JvmField
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
@JvmField
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
@JvmField
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
@JvmField
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
@JvmField
val DIRE_HIT = create("dire_hit", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val GUARD_SPEC = create("guard_spec", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val X_SPEED = create("x_${Stats.SPEED.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
@JvmField
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
@JvmField
@@ -117,6 +117,11 @@ interface CobblemonImplementation {
fun registerEntitySubPredicates()
/**
* TODO
*/
fun registerMobEffects()
/**
* Add a feature to the current platform implementation.
@@ -507,6 +507,23 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
@JvmField val WHITE_PLAQUE = blockItem("white_plaque", CobblemonBlocks.WHITE_PLAQUE)
@JvmField val LIGHT_GRAY_PLAQUE = blockItem("light_gray_plaque", CobblemonBlocks.LIGHT_GRAY_PLAQUE)
@JvmField val GRAY_PLAQUE = blockItem("gray_plaque", CobblemonBlocks.GRAY_PLAQUE)
@JvmField val BLACK_PLAQUE = blockItem("black_plaque", CobblemonBlocks.BLACK_PLAQUE)
@JvmField val BROWN_PLAQUE = blockItem("brown_plaque", CobblemonBlocks.BROWN_PLAQUE)
@JvmField val RED_PLAQUE = blockItem("red_plaque", CobblemonBlocks.RED_PLAQUE)
@JvmField val ORANGE_PLAQUE = blockItem("orange_plaque", CobblemonBlocks.ORANGE_PLAQUE)
@JvmField val YELLOW_PLAQUE = blockItem("yellow_plaque", CobblemonBlocks.YELLOW_PLAQUE)
@JvmField val LIME_PLAQUE = blockItem("lime_plaque", CobblemonBlocks.LIME_PLAQUE)
@JvmField val GREEN_PLAQUE = blockItem("green_plaque", CobblemonBlocks.GREEN_PLAQUE)
@JvmField val CYAN_PLAQUE = blockItem("cyan_plaque", CobblemonBlocks.CYAN_PLAQUE)
@JvmField val LIGHT_BLUE_PLAQUE = blockItem("light_blue_plaque", CobblemonBlocks.LIGHT_BLUE_PLAQUE)
@JvmField val BLUE_PLAQUE = blockItem("blue_plaque", CobblemonBlocks.BLUE_PLAQUE)
@JvmField val PURPLE_PLAQUE = blockItem("purple_plaque", CobblemonBlocks.PURPLE_PLAQUE)
@JvmField val MAGENTA_PLAQUE = blockItem("magenta_plaque", CobblemonBlocks.MAGENTA_PLAQUE)
@JvmField val PINK_PLAQUE = blockItem("pink_plaque", CobblemonBlocks.PINK_PLAQUE)
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
// Plants
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
@@ -699,15 +716,15 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
val POTION = create("potion", PotionItem(PotionType.POTION, CobblemonBlocks.POTION))
@JvmField
val SUPER_POTION = create("super_potion", PotionItem(PotionType.SUPER_POTION))
val SUPER_POTION = create("super_potion", PotionItem(PotionType.SUPER_POTION, CobblemonBlocks.SUPER_POTION))
@JvmField
val HYPER_POTION = create("hyper_potion", PotionItem(PotionType.HYPER_POTION))
val HYPER_POTION = create("hyper_potion", PotionItem(PotionType.HYPER_POTION, CobblemonBlocks.HYPER_POTION))
@JvmField
val MAX_POTION = create("max_potion", PotionItem(PotionType.MAX_POTION))
val MAX_POTION = create("max_potion", PotionItem(PotionType.MAX_POTION, CobblemonBlocks.MAX_POTION))
@JvmField
val FULL_RESTORE = create("full_restore", PotionItem(PotionType.FULL_RESTORE))
val FULL_RESTORE = create("full_restore", PotionItem(PotionType.FULL_RESTORE, CobblemonBlocks.FULL_RESTORE))
@JvmField
val HEAL_POWDER = create("heal_powder", HealPowderItem())
@@ -792,28 +809,27 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SERIOUS_MINT = mintItem("serious_mint", MintItem(Natures.SERIOUS))
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY))
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK))
@JvmField val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", XStatItem(Stats.DEFENCE))
@JvmField val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", XStatItem(Stats.SPECIAL_ATTACK))
@JvmField val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", XStatItem(Stats.SPECIAL_DEFENCE))
@JvmField val X_SPEED = create("x_${Stats.SPEED.identifier.path}", XStatItem(Stats.SPEED))
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY, block = CobblemonBlocks.X_ACCURACY))
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK, block = CobblemonBlocks.X_ATTACK))
@JvmField val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", XStatItem(Stats.DEFENCE, block = CobblemonBlocks.X_DEFENSE))
@JvmField val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", XStatItem(Stats.SPECIAL_ATTACK, block = CobblemonBlocks.X_SP_ATK))
@JvmField val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", XStatItem(Stats.SPECIAL_DEFENCE, block = CobblemonBlocks.X_SP_DEF))
@JvmField val X_SPEED = create("x_${Stats.SPEED.identifier.path}", XStatItem(Stats.SPEED, block = CobblemonBlocks.X_SPEED))
@JvmField val DIRE_HIT = create("dire_hit", DireHitItem())
@JvmField val GUARD_SPEC = create("guard_spec", GuardSpecItem())
@JvmField val DIRE_HIT = create("dire_hit", DireHitItem(CobblemonBlocks.DIRE_HIT))
@JvmField val GUARD_SPEC = create("guard_spec", GuardSpecItem(CobblemonBlocks.GUARD_SPEC))
@JvmField val BURN_HEAL = create("burn_heal", StatusCureItem("item.cobblemon.burn_heal", Statuses.BURN))
@JvmField val PARALYZE_HEAL = create("paralyze_heal", StatusCureItem("item.cobblemon.paralyze_heal", Statuses.PARALYSIS))
@JvmField val ICE_HEAL = create("ice_heal", StatusCureItem("item.cobblemon.ice_heal", Statuses.FROZEN))
@JvmField val ANTIDOTE = create("antidote", StatusCureItem("item.cobblemon.antidote", Statuses.POISON, Statuses.POISON_BADLY))
@JvmField val AWAKENING = create("awakening", StatusCureItem("item.cobblemon.awakening", Statuses.SLEEP))
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal", block = CobblemonBlocks.FULL_HEAL))
@JvmField val ANTIDOTE = create("antidote", StatusCureItem("item.cobblemon.antidote", Statuses.POISON, Statuses.POISON_BADLY, block = CobblemonBlocks.ANTIDOTE))
@JvmField val AWAKENING = create("awakening", StatusCureItem("item.cobblemon.awakening", Statuses.SLEEP, block = CobblemonBlocks.AWAKENING))
@JvmField val BURN_HEAL = create("burn_heal", StatusCureItem("item.cobblemon.burn_heal", Statuses.BURN, block = CobblemonBlocks.BURN_HEAL))
@JvmField val ICE_HEAL = create("ice_heal", StatusCureItem("item.cobblemon.ice_heal", Statuses.FROZEN, block = CobblemonBlocks.ICE_HEAL))
@JvmField val PARALYZE_HEAL = create("paralyze_heal", StatusCureItem("item.cobblemon.paralyze_heal", Statuses.PARALYSIS, block = CobblemonBlocks.PARALYZE_HEAL))
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal"))
@JvmField val ETHER = create("ether", EtherItem(max = false))
@JvmField val MAX_ETHER = create("max_ether", EtherItem(max = true))
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
@JvmField val ETHER = create("ether", EtherItem(max = false, CobblemonBlocks.ETHER))
@JvmField val MAX_ETHER = create("max_ether", EtherItem(max = true, CobblemonBlocks.MAX_ETHER))
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false, CobblemonBlocks.ELIXIR))
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true, CobblemonBlocks.MAX_ELIXIR))
@JvmField
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
@@ -922,7 +938,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BLACK_SLUDGE = heldItem("black_sludge")
@JvmField
val BLUNDER_POLICY = heldItem("blunder_policy")
val BLUNDER_POLICY = itemNameBlockItem("blunder_policy", CobblemonBlocks.BLUNDER_POLICY)
@JvmField
val CELL_BATTERY = heldItem("cell_battery")
@JvmField
@@ -930,7 +946,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val CHOICE_SCARF = heldItem("choice_scarf")
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
val CLEANSE_TAG = itemNameBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
@@ -1038,7 +1054,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SOFT_SAND = heldItem("soft_sand")
@JvmField
val SPELL_TAG = heldItem("spell_tag")
val SPELL_TAG = itemNameBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
@JvmField
@@ -1056,7 +1072,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = heldItem("weakness_policy")
val WEAKNESS_POLICY = itemNameBlockItem("weakness_policy", CobblemonBlocks.WEAKNESS_POLICY)
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
@@ -1553,6 +1569,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
}
)
private fun heldItem(name: String, item: Item, remappedName: String? = null) = create(
name = name,
entry = item.also {
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.cooking.effects.CleanseAllEffect
import com.cobblemon.mod.common.api.cooking.effects.CleanseNegativeEffect
import com.cobblemon.mod.common.api.cooking.effects.MentalRestorationEffect
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.world.effect.MobEffect
object CobblemonMobEffects : PlatformRegistry<Registry<MobEffect>, ResourceKey<Registry<MobEffect>>, MobEffect>() {
override val registry: Registry<MobEffect> = BuiltInRegistries.MOB_EFFECT
override val resourceKey: ResourceKey<Registry<MobEffect>> = Registries.MOB_EFFECT
@JvmField
val CLEANSE_NEGATIVE = create("cleanse_negative", CleanseNegativeEffect())
@JvmField
val MENTAL_RESTORATION = create("mental_restoration", MentalRestorationEffect())
@JvmField
val CLEANSE_ALL = create("cleanse_all", CleanseAllEffect())
}
@@ -140,13 +140,7 @@ import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
@@ -189,6 +183,7 @@ import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRide
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
@@ -225,6 +220,7 @@ import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandl
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
@@ -401,6 +397,7 @@ object CobblemonNetwork {
// Data registries
list.add(PacketRegisterInfo(AbilityRegistrySyncPacket.ID, AbilityRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CobblemonMechanicsSyncPacket.ID, CobblemonMechanicsSyncPacket::decode, CobblemonMechanicsSyncHandler))
list.add(PacketRegisterInfo(PokemonInteractionsSyncPacket.ID, PokemonInteractionsSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -528,7 +525,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ReleasePCPokemonPacket.ID, ReleasePCPokemonPacket::decode, ReleasePCPokemonHandler))
list.add(PacketRegisterInfo(UnlinkPlayerFromPCPacket.ID, UnlinkPlayerFromPCPacket::decode, UnlinkPlayerFromPCHandler))
list.add(PacketRegisterInfo(RequestRenamePCBoxPacket.Companion.ID, RequestRenamePCBoxPacket.Companion::decode, RequestRenamePCBoxHandler))
list.add(PacketRegisterInfo(RequestRenamePCBoxPacket.ID, RequestRenamePCBoxPacket::decode, RequestRenamePCBoxHandler))
list.add(PacketRegisterInfo(PCBoxWallpapersPacket.ID, PCBoxWallpapersPacket::decode, PCBoxWallpapersHandler))
list.add(PacketRegisterInfo(RequestChangePCBoxWallpaperPacket.ID, RequestChangePCBoxWallpaperPacket::decode, RequestChangePCBoxWallpaperHandler))
list.add(PacketRegisterInfo(MarkPCBoxWallpapersSeenPacket.ID, MarkPCBoxWallpapersSeenPacket::decode, MarkPCBoxWallpapersSeenHandler))
@@ -586,6 +583,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PartyPokemonMoveSelectedPacket.ID, PartyPokemonMoveSelectedPacket::decode, PartyPokemonMoveSelectedHandler))
list.add(PacketRegisterInfo(PartyMoveSelectCancelledPacket.ID, PartyMoveSelectCancelledPacket::decode, PartyMoveSelectCancelledHandler))
//Party move tutorial toasts packets
list.add(PacketRegisterInfo(PartyTutorialPacket.ID, PartyTutorialPacket::decode, PartyTutorialPacketHandler))
// Dialogue packets
list.add(PacketRegisterInfo(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler))
list.add(PacketRegisterInfo(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler))
@@ -304,7 +304,8 @@ open class PokemonBattle(
.forEach{it.pokemon.heal()}
actors.forEach { actor ->
actor.pokemonList.forEach { battlePokemon ->
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperation(entity) }
battlePokemon.postBattlePokemonOperations.forEach { it(battlePokemon) }
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperations.forEach { it(entity) } }
if (battlePokemon.effectedPokemon.entity != null
&& battlePokemon.effectedPokemon.entity?.beamMode == 0
&& battlePokemon.effectedPokemon.entity?.platform != PlatformType.NONE) {
@@ -498,8 +499,8 @@ open class PokemonBattle(
}
fun stop() {
end()
writeShowdownAction(">forcetie") // This will terminate the Showdown connection
end()
}
fun checkForInputDispatch() {
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.cooking.effects
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectCategory
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
class CleanseAllEffect : MobEffect(MobEffectCategory.BENEFICIAL, 0xFFFFFF) {
override fun isInstantenous() = true
override fun applyInstantenousEffect(source: Entity?, indirect: Entity?, target: LivingEntity, amplifier: Int, proximity: Double) {
target.removeAllEffects()
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = false
}
@@ -0,0 +1,27 @@
/*
* 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.cooking.effects
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectCategory
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
class CleanseNegativeEffect : MobEffect(MobEffectCategory.BENEFICIAL, 0xFFFFFF) {
override fun isInstantenous() = true
override fun applyInstantenousEffect(source: Entity?, indirect: Entity?, target: LivingEntity, amplifier: Int, proximity: Double) {
val snapshot = target.activeEffects.toList()
for (ei in snapshot) {
if (ei.effect.value().category == MobEffectCategory.HARMFUL) {
target.removeEffect(ei.effect)
}
}
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int) = false
}
@@ -0,0 +1,33 @@
/*
* 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.cooking.effects
import net.minecraft.server.level.ServerPlayer
import net.minecraft.stats.Stats
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectCategory
import net.minecraft.world.entity.LivingEntity
class MentalRestorationEffect : MobEffect(MobEffectCategory.BENEFICIAL, 0xAA66FF) {
override fun applyEffectTick(entity: LivingEntity, amplifier: Int): Boolean {
if (entity is ServerPlayer) {
val stats = entity.stats
val statType = Stats.CUSTOM.get(Stats.TIME_SINCE_REST)
val current = stats.getValue(statType)
val reduction = 31 * (amplifier + 1)// reduce by half a minute every second per level (30 ticks removed per tick + 1 to account for the tick passing)
val newValue = Math.max(current - reduction, 0)
stats.setValue(entity, statType, newValue)
}
return true
}
override fun shouldApplyEffectTickThisTick(duration: Int, amplifier: Int): Boolean {
return true
}
}
@@ -9,6 +9,7 @@
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.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
@@ -25,7 +26,8 @@ import com.cobblemon.mod.common.util.asArrayValue
data class BattleFaintedEvent(
override val battle: PokemonBattle,
val killed: BattlePokemon,
val context: BattleContext
val context: BattleContext,
val pnx: String
) : BattleEvent {
val structContext = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
@@ -33,6 +35,7 @@ data class BattleFaintedEvent(
"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 context.getStruct()
"context" to context.getStruct(),
"pnx" to StringValue(pnx)
)
}
}
@@ -25,7 +25,8 @@ import com.cobblemon.mod.common.util.asArrayValue
data class FormeChangeEvent(
override val battle: PokemonBattle,
val pokemon: BattlePokemon,
val formeName: String
val formeName: String,
val pnx: String
) : BattleEvent {
val context = mutableMapOf(
"battle" to battle.struct,
@@ -33,6 +34,7 @@ data class FormeChangeEvent(
"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)
"forme" to StringValue(formeName),
"pnx" to StringValue(pnx)
)
}
}
@@ -9,13 +9,14 @@
package com.cobblemon.mod.common.api.interaction
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.requirement.OwnerQueryRequirement
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.data.PokemonInteractionsSyncPacket
import com.cobblemon.mod.common.pokemon.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.requirements.PokemonPropertiesRequirement
@@ -29,6 +30,11 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.util.LowerCaseEnumTypeAdapterFactory
import net.minecraft.world.item.Item
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.levelgen.structure.Structure
import net.minecraft.world.level.material.Fluid
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
override val id = cobblemonResource("pokemon_interactions")
@@ -43,40 +49,42 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
val speciesInteractions = mutableListOf<PokemonInteractionSet>()
val generalInteractions = mutableListOf<PokemonInteractionSet>()
val interactions = mutableListOf<PokemonInteractionSet>()
override fun sync(player: ServerPlayer) {}
override fun sync(player: ServerPlayer) {
PokemonInteractionsSyncPacket(interactions).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>) {
speciesInteractions.clear()
generalInteractions.clear()
val split = data.entries.partition { it.value.requirements.any { requirement -> requirement is PokemonPropertiesRequirement && requirement.target.species != null } }
speciesInteractions.addAll(split.first.map {it.value})
generalInteractions.addAll(split.second.map {it.value})
Cobblemon.LOGGER.info("Loaded {} Pokémon interaction sets", data.size)
interactions.clear()
val split = data.entries
.partition {
it.value.requirements.any { requirement -> requirement is PokemonPropertiesRequirement && requirement.target.species != null }
}
// Put the species specific interactions first as they take priority
interactions.addAll(split.first.map {it.value})
interactions.addAll(split.second.map {it.value})
Cobblemon.LOGGER.info("Loaded {} Pokémon interaction sets", interactions.size)
}
fun findInteraction(pokemon: PokemonEntity): PokemonInteraction? {
val setCheck: (PokemonInteractionSet) -> Boolean = { it.requirements.all { req -> req.check(pokemon.pokemon) }}
val interactionCheck: (PokemonInteraction) -> Boolean = { it.requirements.all { req -> req.check(pokemon.pokemon) } && !pokemon.pokemon.isOnInteractionCooldown(it.grouping) }
// species-specific interactions take priority
val validInteractions = speciesInteractions
return interactions
.filter(setCheck)
.flatMap { it.interactions }
.filter(interactionCheck)
.toMutableList()
if (validInteractions.isEmpty()) { // if all species-specific interactions are absent/on cooldown, fallback to more generic ones
validInteractions.addAll(generalInteractions.filter(setCheck).flatMap { it.interactions }
.filter(interactionCheck)
)
}
return validInteractions.randomOrNull()
.firstOrNull(interactionCheck)
}
}
@@ -15,11 +15,11 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.giveOrDropItemStack
import com.cobblemon.mod.common.util.withPlayerValue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.pokemon.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.util.*
import com.google.gson.annotations.SerializedName
import net.minecraft.core.registries.Registries
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource
@@ -28,30 +28,75 @@ import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.ItemStack
/**
* A
*/
data class PokemonInteractionSet(
val requirements: List<Requirement> = listOf(),
val interactions: List<PokemonInteraction> = listOf()
)
val interactions: List<PokemonInteraction> = listOf(),
) {
fun encode(buffer: FriendlyByteBuf) {
buffer.writeCollection(requirements) { it, requirement ->
CobblemonRequirementAdapter.encode(requirement, it)
}
buffer.writeCollection(interactions) { it, interaction ->
interaction.encode(it)
}
}
companion object {
fun decode(buffer: FriendlyByteBuf): PokemonInteractionSet {
val requirements = buffer.readList(CobblemonRequirementAdapter::decode)
val interactions = buffer.readList(PokemonInteraction::decode)
return PokemonInteractionSet(requirements, interactions)
}
}
}
data class PokemonInteraction(
val grouping: ResourceLocation,
val requirements: List<Requirement> = listOf(),
val effects: List<InteractionEffect> = listOf(),
val cooldown: ExpressionLike = "0".asExpressionLike()
)
) {
fun encode(buffer: FriendlyByteBuf) {
buffer.writeResourceLocation(grouping)
buffer.writeCollection(requirements) { it, requirement ->
CobblemonRequirementAdapter.encode(requirement, it)
}
buffer.writeExpressionLike(cooldown)
}
companion object {
fun decode(buffer: FriendlyByteBuf): PokemonInteraction {
val grouping = buffer.readResourceLocation()
val requirements = buffer.readList { it ->
CobblemonRequirementAdapter.decode(it)
}
// No need to sync these for now
val effects = emptyList<InteractionEffect>()
val cooldown = buffer.readExpressionLike()
return PokemonInteraction(
grouping,
requirements,
effects,
cooldown,
)
}
}
}
interface InteractionEffect {
fun applyEffect(pokemon: PokemonEntity, player: ServerPlayer)
}
class DropItemEffect(val item: ResourceLocation, val amount: IntRange?): InteractionEffect {
class DropItemEffect(val item: ResourceLocation, val amount: IntRange?) : InteractionEffect {
override fun applyEffect(
pokemon: PokemonEntity,
player: ServerPlayer
) {
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(item) ?: throw IllegalArgumentException("Cannot load item with id: $item")
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(item) ?: throw IllegalArgumentException(
"Cannot load item with id: $item"
)
val stack = ItemStack(item)
stack.count = amount?.randomOrNull() ?: 1
if (stack.isEmpty)
@@ -68,12 +113,14 @@ class DropItemEffect(val item: ResourceLocation, val amount: IntRange?): Interac
}
}
class GiveItemEffect(val item: ResourceLocation, val amount: IntRange?): InteractionEffect {
class GiveItemEffect(val item: ResourceLocation, val amount: IntRange?) : InteractionEffect {
override fun applyEffect(
pokemon: PokemonEntity,
player: ServerPlayer
) {
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(item) ?: throw IllegalArgumentException("Cannot load item with id: $item")
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(item) ?: throw IllegalArgumentException(
"Cannot load item with id: $item"
)
val stack = ItemStack(item)
stack.count = amount?.randomOrNull() ?: 1
if (stack.isEmpty)
@@ -86,25 +133,54 @@ class GiveItemEffect(val item: ResourceLocation, val amount: IntRange?): Interac
}
}
class PlaySoundEffect(val sound: ResourceLocation, val soundSource: SoundSource?, val playAround: Boolean = true, val distance: Double = 64.0, val volume: Float = 1.0F, val pitch: Float = 1.0F): InteractionEffect {
class PlaySoundEffect(
val sound: ResourceLocation,
val soundSource: SoundSource?,
@SerializedName("playAround") private val _playAround: Boolean?,
@SerializedName("distance") private val _distance: Double?,
@SerializedName("volume") private val _volume: Float?,
@SerializedName("pitch") private val _pitch: Float?,
) : InteractionEffect {
override fun applyEffect(
pokemon: PokemonEntity,
player: ServerPlayer
) {
val packet = UnvalidatedPlaySoundS2CPacket(sound, soundSource ?: SoundSource.NEUTRAL, pokemon.x, pokemon.y, pokemon.z, volume, pitch)
val packet = UnvalidatedPlaySoundS2CPacket(
sound,
soundSource ?: SoundSource.NEUTRAL,
pokemon.x,
pokemon.y,
pokemon.z,
volume,
pitch
)
if (playAround) {
packet.sendToPlayersAround( pokemon.x, pokemon.y, pokemon.z, distance, pokemon.level().dimension())
packet.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, distance, pokemon.level().dimension())
} else {
packet.sendToPlayer(player)
}
}
val playAround: Boolean
get() = _playAround != false
val distance: Double
get() = _distance ?: 64.0
val volume: Float
get() = _volume ?: 1.0F
val pitch: Float
get() = _pitch ?: 1.0F
companion object {
val ID = "play_sound"
}
}
class ShrinkItemEffect(val amount: Int = 1): InteractionEffect {
class ShrinkItemEffect(val amount: Int = 1) : InteractionEffect {
override fun applyEffect(
pokemon: PokemonEntity,
player: ServerPlayer
@@ -120,7 +196,7 @@ class ShrinkItemEffect(val amount: Int = 1): InteractionEffect {
}
}
class ScriptEffect(val script: ExpressionLike): InteractionEffect {
class ScriptEffect(val script: ExpressionLike) : InteractionEffect {
override fun applyEffect(
pokemon: PokemonEntity,
player: ServerPlayer
@@ -311,7 +311,7 @@ object MoLangFunctions {
runtime.environment.variable = params.environment.variable
val args = params.params.subList(1, params.params.size)
runtime.environment.context = ContextStruct(
params.environment.context.map + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
(params.environment.context?.map ?: emptyMap()) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
)
val script = params.getString(0).asIdentifierDefaultingNamespace()
// store the args in the
@@ -324,13 +324,24 @@ object MoLangFunctions {
runtime.environment.context = params.environment.context
val expression = params.getString(0).asExpressionLike()
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
fun evaluate(): MoValue {
return try {
runtime.resolve(expression)
} catch (ex: Exception) {
Cobblemon.LOGGER.warn("Failed to evaluate MoLang expression${if (delayInSeconds > 0.0f) " (delayed)" else ""}: $expression", ex)
DoubleValue.ZERO
}
}
if (delayInSeconds > 0.0f) {
val tracker = if (Cobblemon.implementation.environment() == Environment.SERVER) ServerTaskTracker else ClientTaskTracker
tracker.after(delayInSeconds) {
runtime.resolve(expression)
evaluate()
}
DoubleValue.ONE
} else {
runtime.resolve(expression)
evaluate()
}
},
"system_time_millis" to java.util.function.Function { _ ->
@@ -340,23 +351,28 @@ object MoLangFunctions {
"date_local_time" to java.util.function.Function { _ ->
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
StringValue(formatted)
},
"date_of" to java.util.function.Function { params ->
val time = params.getDouble(0).toLong()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
StringValue(formatted)
},
"date_is_after" to java.util.function.Function { params ->
val dateA = params.getString(0)
val dateB = params.getString(1)
val format = java.text.SimpleDateFormat("DD/MM/YYYY")
val format = java.text.SimpleDateFormat("dd/MM/yyyy")
val a = format.parse(dateA)
val b = format.parse(dateB)
DoubleValue(a.after(b))
},
"get_move_from_id" to java.util.function.Function { params ->
val moveId = params.getString(0)
val moveTemplate = Moves.getByName(moveId)
return@Function moveTemplate?.struct ?: DoubleValue.ZERO
},
"create_simple_party_provider" to java.util.function.Function { params ->
val partyProvider = SimplePartyProvider()
return@Function partyProvider.struct
@@ -367,6 +383,13 @@ object MoLangFunctions {
val pickupItem = ObtainableItem(item = item, pickupPriority = pickupPriority)
return@Function pickupItem.struct
},
"create_itemstack" to java.util.function.Function { params ->
val itemId = params.getString(0).asIdentifierDefaultingNamespace()
val item = BuiltInRegistries.ITEM.get(itemId) ?: return@Function DoubleValue.ZERO
val count = params.getIntOrNull(1) ?: 1
val itemStack = ItemStack(item, count)
return@Function ObjectValue(itemStack)
},
"file" to java.util.function.Function { MoLangLoadedFilesCache.struct }
)
val biomeFunctions = mutableListOf<(Holder<Biome>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
@@ -572,6 +595,62 @@ object MoLangFunctions {
}
return@put items
}
map.put("has_inventory_space") { _ ->
val inventory = player.inventory
return@put if (inventory.getFreeSlot() != -1) DoubleValue.ONE else DoubleValue.ZERO
}
map.put("set_inventory_slot") { params ->
val slot = params.getInt(0)
val inventory = player.inventory
if (slot !in 0 until inventory.containerSize) return@put DoubleValue.ZERO
val value = params.getOrNull<MoValue>(1) ?: return@put DoubleValue.ZERO
val stack: ItemStack? = when (value) {
is ObjectValue<*> -> value.obj as? ItemStack
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id) ?: return@put DoubleValue.ZERO
ItemStack(item)
}
else -> null
}
if (stack == null) return@put DoubleValue.ZERO
inventory.setItem(slot, stack)
return@put DoubleValue.ONE
}
map.put("give_item") { params ->
val inventory = player.inventory
val freeSlot = inventory.getFreeSlot()
val value = params.getOrNull<MoValue>(0) ?: return@put DoubleValue.ZERO
val dropIfFull = params.getBooleanOrNull(1) ?: false
val stack: ItemStack? = when (value) {
is ObjectValue<*> -> value.obj as? ItemStack
is StringValue -> {
val id = ResourceLocation.parse(value.value)
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id) ?: return@put DoubleValue.ZERO
ItemStack(item)
}
else -> null
}
if (stack == null) return@put DoubleValue.ZERO
if (freeSlot == -1) {
if (dropIfFull) {
player.drop(stack, false)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
inventory.setItem(freeSlot, stack)
return@put DoubleValue.ONE
}
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
@@ -615,8 +694,7 @@ object MoLangFunctions {
}
map.put("active_dialogue") { _ ->
if (player.isInDialogue) {
player.activeDialogue?.dialogueId.toString()
return@put DoubleValue.ONE
return@put player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
} else {
DoubleValue.ZERO
}
@@ -779,6 +857,20 @@ object MoLangFunctions {
battleStartResult.ifSuccessful { returnValue = it.struct }
return@put returnValue
}
map.put("get_custom_stat") { params ->
val statName = params.getString(0)
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@put DoubleValue.ZERO
val statResourceLocation = BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@put DoubleValue.ZERO
val exists = net.minecraft.stats.Stats.CUSTOM.contains(statResourceLocation)
if (!exists) return@put DoubleValue.ZERO
val stat = net.minecraft.stats.Stats.CUSTOM.get(statResourceLocation)
val value = player.stats.getValue(stat)
DoubleValue(value)
}
}
map
}
@@ -1739,22 +1831,35 @@ object MoLangFunctions {
pokemon.removeCosmeticItem()
}
map.put("add_marks") { params ->
var appliedMark = false
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
val mark = Marks.getByIdentifier(identifier)
if (mark != null) pokemon.exchangeMark(mark, true)
if (mark != null) {
pokemon.exchangeMark(mark, true)
appliedMark = true
}
}
DoubleValue(appliedMark)
}
map.put("add_marks_with_chance") { params ->
var appliedMark = false
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
val mark = Marks.getByIdentifier(identifier)
mark?.let {
val probability = it.chance.coerceIn(0F, 1F) * 100
val randomValue = Random.nextDouble(0.0, 100.0)
if (randomValue < probability) pokemon.exchangeMark(it, true)
if (randomValue < probability) {
pokemon.exchangeMark(it, true)
appliedMark = true
}
}
}
DoubleValue(appliedMark)
}
map.put("add_potential_marks") { params ->
for (param in params.params) {
@@ -2710,8 +2815,8 @@ object MoLangFunctions {
}
variable
}
else -> null
} ?: throw IllegalArgumentException("Invalid NBT element type: ${nbt.type}")
else -> VariableStruct()
}
}
}
@@ -58,12 +58,15 @@ open class MoveTemplate(
val struct: MoStruct by lazy {
QueryStruct(hashMapOf())
.addFunction("name") { StringValue(name) }
.addFunction("display_name") { StringValue(displayName.string) }
.addFunction("description") { StringValue(description.string) }
.addFunction("type") { StringValue(elementalType.showdownId) }
.addFunction("damage_category") { StringValue(damageCategory.name) }
.addFunction("power") { DoubleValue(power) }
.addFunction("target") { StringValue(target.name) }
.addFunction("accuracy") { DoubleValue(accuracy) }
.addFunction("pp") { DoubleValue(pp) }
.addFunction("max_pp") { DoubleValue(maxPp) }
.addFunction("priority") { DoubleValue(priority) }
.addFunction("crit_ratio") { DoubleValue(critRatio) }
}
@@ -80,7 +80,9 @@ open class ChoiceSpeciesFeatureProvider(
internal constructor(): this(emptyList())
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
override fun get(pokemon: Pokemon): StringSpeciesFeature? {
return pokemon.features.filterIsInstance<StringSpeciesFeature>().find { it.name in keys }
}
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
val existing = get(pokemon)
@@ -101,15 +103,15 @@ open class ChoiceSpeciesFeatureProvider(
}
override fun invoke(nbt: CompoundTag): StringSpeciesFeature? {
return if (nbt.contains(keys.first())) {
StringSpeciesFeature(keys.first(), "").also { it.loadFromNBT(nbt) }
} else null
val key = keys.find { nbt.contains(it) }
if (key == null) return null
return StringSpeciesFeature(key, "").also { it.loadFromNBT(nbt) }
}
override fun invoke(json: JsonObject): StringSpeciesFeature? {
return if (json.has(keys.first())) {
StringSpeciesFeature(keys.first(), "").also { it.loadFromJSON(json) }
} else null
val key = keys.find { json.has(it) }
if (key == null) return null
return StringSpeciesFeature(key, "").also { it.loadFromJSON(json) }
}
override fun fromString(value: String?): StringSpeciesFeature? {
@@ -131,7 +133,7 @@ open class ChoiceSpeciesFeatureProvider(
override fun provide(properties: PokemonProperties): Set<String> {
return if (isAspect) {
val feature = properties.customProperties.filterIsInstance<StringSpeciesFeature>().find { it.name == keys.first() }
val feature = properties.customProperties.filterIsInstance<StringSpeciesFeature>().find { it.name in keys }
if (feature != null) {
setOf(getAspect(feature))
} else {
@@ -132,7 +132,9 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
constructor(vararg keys: String) : this(keys.toList())
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
override fun get(pokemon: Pokemon): FlagSpeciesFeature? {
return pokemon.features.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
}
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
return get(pokemon)
@@ -144,15 +146,15 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
}
override fun invoke(nbt: CompoundTag): FlagSpeciesFeature? {
return if (nbt.contains(keys.first())) {
FlagSpeciesFeature(keys.first(), false).also { it.loadFromNBT(nbt) }
} else null
val key = keys.find { nbt.contains(it) }
if (key == null) return null
return FlagSpeciesFeature(key, false).also { it.loadFromNBT(nbt) }
}
override fun invoke(json: JsonObject): FlagSpeciesFeature? {
return if (json.has(keys.first())) {
FlagSpeciesFeature(keys.first(), false).also { it.loadFromJSON(json) }
} else null
val key = keys.find { json.has(it) }
if (key == null) return null
return FlagSpeciesFeature(key, false).also { it.loadFromJSON(json) }
}
override fun fromString(value: String?): FlagSpeciesFeature? {
@@ -170,16 +172,26 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
}
override fun provide(pokemon: Pokemon): Set<String> {
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.enabled == true) {
setOf(keys.first())
return if (isAspect) {
val feature = pokemon.features.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
if (feature != null && feature.enabled) {
setOf(keys.first())
} else {
emptySet()
}
} else {
emptySet()
}
}
override fun provide(properties: PokemonProperties): Set<String> {
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
setOf(keys.first())
return if (isAspect) {
val feature = properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
if (feature != null && feature.enabled) {
setOf(keys.first())
} else {
emptySet()
}
} else {
emptySet()
}
@@ -142,19 +142,15 @@ class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesF
}
override fun invoke(nbt: CompoundTag): IntSpeciesFeature? {
return if (nbt.contains(keys.first())) {
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
} else {
null
}
val key = keys.find { nbt.contains(it) }
if (key == null) return null
return IntSpeciesFeature(keys.first(), nbt.getInt(key));
}
override fun invoke(json: JsonObject): IntSpeciesFeature? {
return if (json.has(keys.first())) {
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
} else {
null
}
val key = keys.find { json.has(it) }
if (key == null) return null
return IntSpeciesFeature(keys.first(), json.get(key).asInt)
}
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
@@ -102,6 +102,12 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
@JvmStatic
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
@JvmStatic
fun registerType(key: String, clazz: Class<out SpeciesFeatureProvider<*>>) {
types[key] = clazz
}
private fun registerFromAssets(identifier: ResourceLocation, provider: SpeciesFeatureProvider<*>) = register(identifier.path, provider, isCoded = false)
@JvmStatic
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
/**
* A [Requirement] that expects a [LivingEntity] to be attached to the [Pokemon].
@@ -26,7 +27,7 @@ interface OwnerQueryRequirement : Requirement {
override fun check(pokemon: Pokemon): Boolean {
val owner = pokemon.getOwnerEntity() ?: return false
return when (owner) {
is ServerPlayer -> {
is Player -> {
this.checkPlayer(pokemon, owner)
}
is NPCEntity -> {
@@ -37,13 +38,13 @@ interface OwnerQueryRequirement : Requirement {
}
/**
* Checks if the given [Pokemon] & [ServerPlayer] satisfies the requirement.
* Checks if the given [Pokemon] & [Player] satisfies the requirement.
*
* @param pokemon The [Pokemon] being queried.
* @param owner The [ServerPlayer] that owns this [Pokemon].
* @param owner The [Player] that owns this [Pokemon].
* @return If the requirement was satisfied.
*/
fun checkPlayer(pokemon: Pokemon, owner: ServerPlayer): Boolean
fun checkPlayer(pokemon: Pokemon, owner: Player): Boolean
/**
* Checks if the given [Pokemon] & [NPCEntity] satisfies the requirement.
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.reactive
import com.cobblemon.mod.common.api.events.Cancelable
open class EventObservable<T> : SimpleObservable<T>() {
@JvmOverloads
inline fun post(vararg events: T, then: (T) -> Unit = {}) {
// Issuing generic-typed pass-throughs to varargs doesn't work
// properly unless the source is a vararg as well, complicated story.
@@ -41,10 +41,6 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
toState: RidingBehaviourState,
toSettings: RidingBehaviourSettings
) {
if (vehicle.level().isClientSide) {
val fromSettings = if (settings.defaultBehaviour == toSettings) settings.alternateBehaviour else settings.defaultBehaviour
}
toState.stamina.set(fromState.stamina.get())
toState.rideVelocity.set(fromState.rideVelocity.get())
fromState.reset()
@@ -187,17 +187,16 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
return
}
var newStam = stam
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHorse.staminaExpr!!)).toFloat() / 20.0f
if (state.sprinting.get()) {
newStam = max(0.0f,stam - stamDrainRate)
val newStam = if (state.sprinting.get()) {
max(0.0f,stam - stamDrainRate)
} else {
newStam = min(1.0f,stam + stamDrainRate * 4)
min(1.0f,stam + stamDrainRate * 4)
}
state.stamina.set(newStam)
state.stamina.set(newStam.coerceIn(0F, 1F))
}
override fun updatePassengerRotation(
@@ -100,7 +100,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
if (vehicle.level().isClientSide) {
handleBoosting(state, vehicle)
tickStamina(settings, state, vehicle)
}
@@ -147,7 +147,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
else min(1.0f,stam + stamDrainRate)
else (stam + stamDrainRate).coerceIn(0F, 1F)
state.stamina.set(newStam)
}
@@ -12,6 +12,7 @@ package com.cobblemon.mod.common.api.riding.sound
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getIsSubmerged
import com.cobblemon.mod.common.util.resolveDouble
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
@@ -65,6 +66,21 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
return
}
// Mute the sound if it's not meant to play for the passenger
if (this.isPassenger && !this.soundSettings.playForPassengers) {
this.volume = 0.0f
return
}
// Mute sounds when submerged unless they support underwater sounds, underwater sounds don't play above water
if (this.ride.getIsSubmerged() && !this.soundSettings.submerged) {
this.volume = 0.0f
return
} else if (!this.ride.getIsSubmerged() && this.soundSettings.submerged) {
this.volume = 0.0f
return
}
// Perform data driven pitch and volume calculation
this.volume = ((soundSettings.volumeExpr.let { ride.runtime.resolveDouble(it) })).toFloat()
this.volume *= this.fade
@@ -27,7 +27,9 @@ data class RideSoundSettings(
val soundLocation: ResourceLocation,
@SerializedName("volumeExpr") private val _volumeExpr: Expression? = "1.0".asExpression(),
@SerializedName("pitchExpr") private val _pitchExpr: Expression? = "1.0".asExpression(),
val playForPassengers: Boolean = true,
val playForNonPassengers: Boolean = false,
val submerged: Boolean = false,
val muffleEnabled: Boolean = false,
@SerializedName("attenuationModel") private val _attenuationModel: RideAttenuationModel? = RideAttenuationModel.NONE
): Encodable {
@@ -46,7 +48,9 @@ data class RideSoundSettings(
buffer.writeResourceLocation(soundLocation)
buffer.writeExpression(volumeExpr)
buffer.writeExpression(pitchExpr)
buffer.writeBoolean(playForPassengers)
buffer.writeBoolean(playForNonPassengers)
buffer.writeBoolean(submerged)
buffer.writeBoolean(muffleEnabled)
buffer.writeEnum(attenuationModel)
}
@@ -57,7 +61,9 @@ data class RideSoundSettings(
val soundLocation = buffer.readResourceLocation()
val volumeExpr = buffer.readExpression()
val pitchExpr = buffer.readExpression()
val playForPassengers = buffer.readBoolean()
val playForNonPassengers = buffer.readBoolean()
val submerged = buffer.readBoolean()
val muffleEnabled = buffer.readBoolean()
val attenuationModel = buffer.readEnum(RideAttenuationModel::class.java)
@@ -65,7 +71,9 @@ data class RideSoundSettings(
soundLocation,
volumeExpr,
pitchExpr,
playForPassengers,
playForNonPassengers,
submerged,
muffleEnabled,
attenuationModel
)
@@ -26,7 +26,7 @@ import net.minecraft.world.entity.Entity
/**
* A [SpawningInfluence] that applies some number of SpawnBait effects.
*
* @author Hiroku, Plastered_Crab
* @author Hiroku, Plastered_Crab, ppVon
* @since March 18th, 2025
*/
open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time: Int, entity: PokemonEntity?) -> Unit = { _, _ -> }) : SpawningInfluence {
@@ -50,85 +50,84 @@ open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (
}
}
// EV related bait effects
// EV, Type, and Egg Group related bait effects
override fun affectWeight(detail: SpawnDetail, spawnablePosition: SpawnablePosition, weight: Float): Float {
val merged = SpawnBaitUtils.mergeEffects(effects)
val hasRelevantEffects =
merged.any { it.type == Effects.EV } ||
merged.any { it.type == Effects.TYPING } ||
merged.any { it.type == Effects.EGG_GROUP }
if (!hasRelevantEffects) {
return super.affectWeight(detail, spawnablePosition, weight)
}
val pokemonDetail = detail as? PokemonSpawnDetail
?: return super.affectWeight(detail, spawnablePosition, weight)
val detailSpecies = pokemonDetail.pokemon.species
?.let { PokemonSpecies.getByName(it) }
?: return super.affectWeight(detail, spawnablePosition, weight)
val formData = detailSpecies.getForm(pokemonDetail.pokemon.aspects)
var newWeight = weight
var used = false
// if bait exists and any effects are related to EV yields
if (merged.any { it.type == Effects.EV }){
if (detail is PokemonSpawnDetail) {
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
val baitEVStat = effects.firstOrNull { it.type == Effects.EV }?.subcategory?.path?.let { Stats.getStat(it) }
if (merged.any { it.type == Effects.EV }) {
val evEffect = effects.firstOrNull { it.type == Effects.EV }
val baitEVStat = evEffect?.subcategory?.path?.let { Stats.getStat(it) }
if (detailSpecies != null && baitEVStat != null) {
val detailAspects = detail.pokemon.aspects
val formData = detailSpecies.getForm(detailAspects)
val evYieldValue = formData.evYield[baitEVStat]?.toFloat() ?: 0f
return when {
evYieldValue > 0 -> {
markUsed()
super.affectWeight(detail, spawnablePosition, weight)
}
else -> {
markUsed()
super.affectWeight(detail, spawnablePosition, 0f)
}
}
if (baitEVStat != null) {
val evYieldValue = formData.evYield[baitEVStat]?.toFloat() ?: 0f
used = true
if (evYieldValue <= 0f) {
newWeight = 0f
} else {
LOGGER.debug("Boosted weight value");
}
}
}
// if bait exists and any effects are related to Typing
if (merged.any { it.type == Effects.TYPING }){
if (detail is PokemonSpawnDetail) {
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
val baitEffect = effects.firstOrNull { it.type == Effects.TYPING }
val baitTypingEffect = baitEffect?.subcategory?.path?.let { ElementalTypes.get(it) }
if (newWeight > 0f && merged.any { it.type == Effects.TYPING }) {
val baitEffect = effects.firstOrNull { it.type == Effects.TYPING }
val baitTypingEffect = baitEffect?.subcategory?.path?.let { ElementalTypes.get(it) }
if (detailSpecies != null && baitTypingEffect != null) {
val detailAspects = detail.pokemon.aspects
val formData = detailSpecies.getForm(detailAspects)
val isMatchingType = formData.types.contains(baitTypingEffect)
return when {
isMatchingType -> {
markUsed()
super.affectWeight(detail, spawnablePosition, weight * baitEffect.value.toFloat())
}
else -> super.affectWeight(detail, spawnablePosition, weight)
}
if (baitEffect != null && baitTypingEffect != null) {
val isMatchingType = formData.types.contains(baitTypingEffect)
used = true
if (isMatchingType) {
newWeight *= baitEffect.value.toFloat()
}
}
}
// if bait exists and any effects are related to Egg Groups
if (merged.any { it.type == Effects.EGG_GROUP }) {
if (detail is PokemonSpawnDetail) {
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
if (newWeight > 0f && merged.any { it.type == Effects.EGG_GROUP }) {
val eggGroupEffects = effects.filter { it.type == Effects.EGG_GROUP }
if (detailSpecies != null) {
val detailAspects = detail.pokemon.aspects
val formData = detailSpecies.getForm(detailAspects)
// Collect all the egg group effects
val eggGroupEffects = effects.filter { it.type == Effects.EGG_GROUP }
// Check if any of the egg group effects match the species' egg groups
val matchingEffect = eggGroupEffects.firstOrNull { effect ->
val effectEggGroupKey = effect.subcategory?.path ?: return@firstOrNull false
val eggGroup = EggGroup.fromIdentifier(effectEggGroupKey)
if (eggGroup == null) {
LOGGER.warn("Unknown egg group identifier: $effectEggGroupKey")
return@firstOrNull false
}
formData.eggGroups.contains(eggGroup)
}
if (matchingEffect != null) {
markUsed()
val multiplier = matchingEffect.value
return super.affectWeight(detail, spawnablePosition, (weight * multiplier).toFloat())
}
val matchingEffect = eggGroupEffects.firstOrNull { effect ->
val key = effect.subcategory?.path ?: return@firstOrNull false
val eggGroup = EggGroup.fromIdentifier(key)
if (eggGroup == null) {
LOGGER.warn("Unknown egg group identifier: $key")
return@firstOrNull false
}
formData.eggGroups.contains(eggGroup)
}
if (matchingEffect != null) {
used = true
newWeight *= matchingEffect.value.toFloat()
}
}
return super.affectWeight(detail, spawnablePosition, weight)
if (used) markUsed()
return super.affectWeight(detail, spawnablePosition, newWeight)
}
}
@@ -8,30 +8,58 @@
package com.cobblemon.mod.common.api.stats
import com.cobblemon.mod.common.Cobblemon
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.stats.StatFormatter
object CobblemonStats {
val stats = mutableMapOf<String, ResourceLocation>()
val stats = mutableMapOf<String, CobblemonStat>()
var CAPTURED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "captured") //makeCustomStat("captured", StatFormatter.DEFAULT)
val SHINIES_CAPTURED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "shinies_captured") //makeCustomStat("shinies_captured", StatFormatter.DEFAULT)
val RELEASED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "released") //makeCustomStat("released", StatFormatter.DEFAULT)
val EVOLVED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "evolved") //makeCustomStat("evolved", StatFormatter.DEFAULT)
val LEVEL_UP: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "level_up") //makeCustomStat("level_up", StatFormatter.DEFAULT)
val BATTLES_WON: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "battles_won") //makeCustomStat("battles_won", StatFormatter.DEFAULT)
val BATTLES_LOST: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "battles_lost") //makeCustomStat("battles_lost", StatFormatter.DEFAULT)
val BATTLES_FLED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "battles_fled") //makeCustomStat("battles_fled", StatFormatter.DEFAULT)
val BATTLES_TOTAL: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "battles_total") //makeCustomStat("battles_total", StatFormatter.DEFAULT)
val DEX_ENTRIES: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "dex_entries") //makeCustomStat("dex_entries", StatFormatter.DEFAULT)
val EGGS_COLLECTED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "eggs_collected") //makeCustomStat("eggs_collected", StatFormatter.DEFAULT)
val EGGS_HATCHED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "eggs_hatched") //makeCustomStat("eggs_hatched", StatFormatter.DEFAULT)
val TRADED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "traded") //makeCustomStat("traded", StatFormatter.DEFAULT)
val FOSSILS_REVIVED: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "fossils_revived") //makeCustomStat("fossils_revived", StatFormatter.DEFAULT)
val TIMES_RIDDEN: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "times_ridden")
val ROD_CASTS: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "rod_casts")
val REEL_INS: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "reel_ins")
val POKEMON_INTERACTED_WITH: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", "pokemon_interacted_with") //makeCustomStat("pokemon_interacted_with", StatFormatter.DEFAULT)
//TODO block, stats (interact, gimmi), riding (styles?)
@JvmField
var CAPTURED: CobblemonStat = CobblemonStat("captured")
@JvmField
val SHINIES_CAPTURED: CobblemonStat = CobblemonStat("shinies_captured")
@JvmField
val RELEASED: CobblemonStat = CobblemonStat("released")
@JvmField
val EVOLVED: CobblemonStat = CobblemonStat("evolved")
@JvmField
val LEVEL_UP: CobblemonStat = CobblemonStat("level_up")
@JvmField
val BATTLES_WON: CobblemonStat = CobblemonStat("battles_won")
@JvmField
val BATTLES_LOST: CobblemonStat = CobblemonStat("battles_lost")
@JvmField
val BATTLES_FLED: CobblemonStat = CobblemonStat("battles_fled")
@JvmField
val BATTLES_TOTAL: CobblemonStat = CobblemonStat("battles_total")
@JvmField
val DEX_ENTRIES: CobblemonStat = CobblemonStat("dex_entries")
@JvmField
val EGGS_COLLECTED: CobblemonStat = CobblemonStat("eggs_collected")
@JvmField
val EGGS_HATCHED: CobblemonStat = CobblemonStat("eggs_hatched")
@JvmField
val TRADED: CobblemonStat = CobblemonStat("traded")
@JvmField
val FOSSILS_REVIVED: CobblemonStat = CobblemonStat("fossils_revived")
@JvmField
val TIMES_RIDDEN: CobblemonStat = CobblemonStat("times_ridden")
@JvmField
val ROD_CASTS: CobblemonStat = CobblemonStat("rod_casts")
@JvmField
val REEL_INS: CobblemonStat = CobblemonStat("reel_ins")
@JvmField
val POKEMON_INTERACTED_WITH: CobblemonStat = CobblemonStat("pokemon_interacted_with")
@JvmField
val RIDING_LAND: CobblemonStat = CobblemonStat("riding_land", StatFormatter.DISTANCE)
@JvmField
val RIDING_AIR: CobblemonStat = CobblemonStat("riding_air", StatFormatter.DISTANCE)
@JvmField
val RIDING_LIQUID: CobblemonStat = CobblemonStat("riding_liquid", StatFormatter.DISTANCE)
//TODO block, stats (interact, gimmi)
fun registerStats() {
stats["captured"] = CAPTURED
@@ -52,5 +80,22 @@ object CobblemonStats {
stats["rod_casts"] = ROD_CASTS
stats["reel_ins"] = REEL_INS
stats["pokemon_interacted_with"] = POKEMON_INTERACTED_WITH
stats["riding_land"] = RIDING_LAND
stats["riding_air"] = RIDING_AIR
stats["riding_liquid"] = RIDING_LIQUID
}
@JvmStatic
fun getStat(cobblemonStat: CobblemonStat) : ResourceLocation {
val resourceLocation = cobblemonStat.resourceLocation
val stat = BuiltInRegistries.CUSTOM_STAT.get(resourceLocation)
if (stat == null) {
Cobblemon.LOGGER.error("Could not find stat with id {}", resourceLocation)
}
return stat ?: throw NullPointerException("Could not find stat with id $resourceLocation")
}
data class CobblemonStat(val path: String, val formatter: StatFormatter = StatFormatter.DEFAULT) {
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath("cobblemon", path);
}
}
@@ -258,17 +258,22 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
@JvmOverloads
fun toBattleTeam(clone: Boolean = false, healPokemon: Boolean = false, leadingPokemon: UUID? = null) : List<BattlePokemon> {
var leadingPokemonId = leadingPokemon
val result = this.mapNotNull {
return@mapNotNull if (clone) {
BattlePokemon.safeCopyOf(it)
BattlePokemon.safeCopyOf(it).also { copy ->
if (leadingPokemon != null && it.uuid == leadingPokemonId) {
leadingPokemonId = copy.uuid
}
}
} else {
BattlePokemon.playerOwned(it)
}.also { if (healPokemon) it.effectedPokemon.heal() }
}.toMutableList()
// reposition lead to front of the party
if (leadingPokemon != null && result.first().uuid != leadingPokemon) {
result.find { it.uuid == leadingPokemon }?.let { lead ->
if (leadingPokemonId != null && result.first().uuid != leadingPokemonId) {
result.find { it.uuid == leadingPokemonId }?.let { lead ->
result.remove(lead)
result.add(0, lead)
}
@@ -154,12 +154,12 @@ open class PlayerPartyStore(
// Metabolism for Fullness
if (pokemon.currentFullness > 0) {
pokemon.tickMetabolism()
pokemon.tickMetabolism(20)
}
// Interaction Cooldown
if (pokemon.interactionCooldowns.any()) {
pokemon.tickInteractionCooldown()
pokemon.tickInteractionCooldown(20)
}
pokemon.features.filterIsInstance<TickingSpeciesFeature>().forEach { it.onSecondPassed(player.serverLevel(), pokemon, null) }
@@ -28,6 +28,7 @@ data class GeneralPlayerData(
var starterUUID: UUID?,
var keyItems: MutableSet<ResourceLocation>,
var battleTheme: ResourceLocation?,
var partySelectTutorialDone: Boolean = false,
val extraData: MutableMap<String, PlayerDataExtension>,
) : InstancedPlayerData {
var advancementData: PlayerAdvancementData = PlayerAdvancementData()
@@ -53,7 +54,8 @@ data class GeneralPlayerData(
starterSelected,
starterUUID,
advancementData.totalBattleVictoryCount == 0,
battleTheme
battleTheme,
partySelectTutorialDone
)
}
@@ -36,7 +36,8 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
starterUUID = null,
keyItems = Cobblemon.config.defaultKeyItems,
extraData = mutableMapOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
battleTheme = CobblemonSounds.PVP_BATTLE.location,
partySelectTutorialDone = false
)}
override val gson = GsonBuilder()
@@ -37,7 +37,8 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
starterUUID = null,
keyItems = Cobblemon.config.defaultKeyItems,
extraData = mutableMapOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
battleTheme = CobblemonSounds.PVP_BATTLE.location,
partySelectTutorialDone = false
)}
override val gson = GsonBuilder()
@@ -34,7 +34,8 @@ data class ClientGeneralPlayerData(
var starterSelected: Boolean = false,
var starterUUID: UUID? = null,
var showChallengeLabel: Boolean = true,
val battleTheme: ResourceLocation? = null
val battleTheme: ResourceLocation? = null,
var partySelectTutorialDone: Boolean = false
) : ClientInstancedPlayerData {
override fun encode(buf: RegistryFriendlyByteBuf) {
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
buf.writeBoolean(partySelectTutorialDone)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
@@ -57,6 +59,7 @@ data class ClientGeneralPlayerData(
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
val battleTheme = buffer.readNullable { it.readIdentifier() }
val partySelectTutorialDone = buffer.readBoolean()
val data = ClientGeneralPlayerData(
resetStarterPrompt,
promptStarter,
@@ -64,7 +67,8 @@ data class ClientGeneralPlayerData(
starterSelected,
starterUUID,
showChallengeLabel,
battleTheme
battleTheme,
partySelectTutorialDone
)
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
//Should be fine, as long as decode doesn't get run on the server for some reason
@@ -42,6 +42,7 @@ object CobblemonItemTags {
@JvmField val DAWN_STONE_ORES = create("dawn_stone_ores")
@JvmField val POTTERY_SHERDS = create("decorated_pot_sherds")
@JvmField val DEEP_SEAS = create("deep_seas")
@JvmField val DISPLAY_CASE_ITEMS = create("display_case_items")
@JvmField val DUSK_STONE_ORES = create("dusk_stone_ores")
@JvmField val ETHERS = create("ethers")
@JvmField val EVOLUTION_ITEMS = create("evolution_items")
@@ -57,6 +58,7 @@ object CobblemonItemTags {
@JvmField val HANGING_SIGNS = create("hanging_signs")
@JvmField val HERBS = create("herbs")
@JvmField val ICE_STONE_ORES = create("ice_stone_ores")
@JvmField val ITEM_BLOCKS = create("item_blocks")
@JvmField val LEAF_STONE_ORES = create("leaf_stone_ores")
@JvmField val MACHINES = create("machines")
@JvmField val MINT_LEAF = create("mint_leaf")
@@ -65,6 +67,7 @@ object CobblemonItemTags {
@JvmField val MOCHIS = create("mochis")
@JvmField val MOON_STONE_ORES = create("moon_stone_ores")
@JvmField val MUTATED_BERRIES = create("mutated_berries")
@JvmField val PLAQUES = create("plaques")
@JvmField val PLANTS = create("plants")
@JvmField val POKE_BALLS = create("poke_balls")
@JvmField val POKE_RODS = create("poke_rods")
@@ -63,8 +63,8 @@ object BattleBuilder {
): BattleStartResult {
val adjustLevel = battleFormat.adjustLevel
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer1).sortedBy { it.health <= 0 }
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer2).sortedBy { it.health <= 0 }
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer1)
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer2)
val battlePartyStores = mutableListOf<PlayerPartyStore>()
@@ -105,12 +105,18 @@ data class BattleFormat(
}
fun toFormatJSON(): String {
/*
Bag clause does not exist in showdown as a rule because showdown simply has no concept of a 'bag'.
If we try sending it, the battle will error. So this simply omits that from ever getting sent.
If we ever find a case where we are doing this for more than just bag clause, we should make a proper abstraction.
*/
val rulesToSendToShowdown = ruleSet.filter { it != BattleRules.BAG_CLAUSE }
return """
{
"mod": "$mod",
"gameType": "${battleType.name}",
"gen": $gen,
"ruleset": [${ruleSet.joinToString { "\"$it\"" }}],
"ruleset": [${rulesToSendToShowdown.joinToString { "\"$it\"" }}],
"effectType": "Format"
}
""".trimIndent().replace("\n", "")
@@ -221,6 +221,7 @@ object BattleRegistry {
fun closeBattle(battle: PokemonBattle) {
battle.onEndHandlers.forEach { it(battle) }
battleMap.remove(battle.battleId)
ShowdownService.service.endBattle(battle)
}
@JvmStatic
@@ -12,6 +12,7 @@ object BattleRules {
const val OBTAINABLE = "Obtainable"
const val PAST = "+Past"
const val UNOBTAINABLE = "+Unobtainable"
const val BAG_CLAUSE = "Bag Clause"
const val TEAM_PREVIEW = "Team Preview"
const val ENDLESS_BATTLE_CLAUSE = "Endless Battle Clause"
const val CANCEL_MOD = "Cancel Mod"
@@ -289,6 +289,8 @@ class ShowdownMoveset {
var canDynamax = false
var maxMoves: List<InBattleGimmickMove?>? = null
var canTerastallize: String? = null
/** This stores a list of gimmicks that have already been used this turn by a previous Pokemon, but the full turn has not been processed (ie. double battles). */
val pendingGimmickUsedThisTurn = mutableListOf<Gimmick>()
fun saveToBuffer(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
@@ -300,4 +300,35 @@ object ShowdownInterpreter {
} ?:
MissingContext()
}
}
/**
* Registers a new update instruction parser.
* @param id The ID of the instruction to register.
* @param callback The callback function to create the instruction.
*/
@JvmStatic
fun registerUpdateInstructionParser(id: String, callback: (PokemonBattle, InstructionSet, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction) {
updateInstructionParser[id] = callback
}
/**
* Registers a new split instruction parser.
* @param id The ID of the instruction to register.
* @param callback The callback function to create the instruction.
*/
@JvmStatic
fun registerSplitInstructionParser(id: String, callback: (PokemonBattle, BattleActor, InstructionSet, BattleMessage, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction) {
splitInstructionParser[id] = callback
}
/**
* Registers a new side instruction parser.
* @param id The ID of the instruction to register.
* @param callback The callback function to create the instruction.
*/
@JvmStatic
fun registerSideInstructionParser(id: String, callback: (PokemonBattle, BattleActor, InstructionSet, BattleMessage) -> InterpreterInstruction) {
sideInstructionParser[id] = callback
}
}
@@ -18,7 +18,7 @@ import java.util.UUID
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Component
class MultiPokemonBattleActor(
open class MultiPokemonBattleActor(
pokemonList: List<BattlePokemon>,
artificialDecider: BattleAI = RandomBattleAI(),
uuid: UUID = UUID.randomUUID()
@@ -24,17 +24,14 @@ import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.phys.Vec3
class PokemonBattleActor(
open class PokemonBattleActor(
uuid: UUID,
val pokemon: BattlePokemon,
override val fleeDistance: Float,
artificialDecider: BattleAI = RandomBattleAI()
artificialDecider: BattleAI = RandomBattleAI(),
) : AIBattleActor(uuid, listOf(pokemon), artificialDecider), EntityBackedBattleActor<PokemonEntity>, FleeableBattleActor {
override val initialPos: Vec3?
init {
initialPos = entity?.position()
}
override val initialPos: Vec3? = pokemon.entity?.position()
override fun getName() = pokemon.effectedPokemon.species.translatedName
override fun nameOwned(name: String): MutableComponent = Component.literal(name)
override fun getWorldAndPosition(): Pair<ServerLevel, Vec3>? {
@@ -17,7 +17,7 @@ import com.cobblemon.mod.common.util.battleLang
import java.util.UUID
import net.minecraft.network.chat.MutableComponent
class TrainerBattleActor(
open class TrainerBattleActor(
val trainerName: String,
uuid: UUID,
pokemonList: List<BattlePokemon>,
@@ -31,6 +31,7 @@ import kotlin.collections.associateWith
import kotlin.collections.partition
import kotlin.math.absoluteValue
/**
* AI that tries to choose the best move for the given situations. Based off of the Pokemon Trainer Tournament Simulator Github
* https://github.com/cRz-Shadows/Pokemon_Trainer_Tournament_Simulator/blob/main/pokemon-showdown/sim/examples/Simulation-test-1.ts#L330
@@ -271,6 +272,11 @@ class StrongBattleAI(skill: Int) : BattleAI {
}
activeTrackerPokemon.firstTurn = false
// If HP is below 30% and not switching out, always use the most damaging move
if (activeTrackerPokemon.currentHpPercent < 0.3 && !shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)?.let { return it }
}
// Decision-making based on move availability and switch-out condition
if (!shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
val nRemainingMons = activeTracker.alliedSide.actors.sumOf { actor -> actor.party.filter { it.currentHpPercent > 0 }.size }
@@ -521,6 +527,19 @@ class StrongBattleAI(skill: Int) : BattleAI {
val opponentActiveTracker = activeTracker.opponentSide.activePokemon
val opponentSpeedEstimations = opponentActiveTracker.map { statEstimationActive(it, Stats.SPEED) }
// Only switch if a switch-in is significantly better than the current Pokémon
val currentScore = estimateMatchup(activeBattlePokemon, side, battle)
val bestSwitchScore = availableSwitches.maxOfOrNull { estimateMatchup(activeBattlePokemon, side, battle, it) } ?: currentScore
val improvementThreshold = currentScore.absoluteValue * 0.25 + 2
if (bestSwitchScore <= currentScore + improvementThreshold) {
return false
}
if(bestSwitchScore < 1) {
return false
}
// todo add some way to keep track of the player's boosting to see if it needs to switch out to something that can stop it
// if slower speed stat than the opposing pokemon and HP is less than 20% don't switch out
@@ -548,6 +567,8 @@ class StrongBattleAI(skill: Int) : BattleAI {
if (availableSwitches.isEmpty() || availableSwitches.none { estimateMatchup(activeBattlePokemon, side, battle, it) > 0 })
return false
// ...and a 'good' reason to switch out
if (opponentActiveTracker.any { it.boosts.getOrDefault(Stats.ACCURACY, 0) <= accuracySwitchThreshold } ||
opponentActiveTracker.any { it.boosts.getOrDefault(Stats.DEFENCE, 0) <= -3 } ||
@@ -114,7 +114,14 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
}
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
"shadowforce", "hyperspacefury", "hyperspacehole" -> battleLang("activate.phantomforce", pokemonName)
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
else -> {
val msgVariant = message.optionalArgument("msg")
if (msgVariant != null) {
battleLang("activate.${effect.id}.$msgVariant", pokemonName, sourceName, extraEffect)
} else {
battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
}
}
}
battle.broadcastChatMessage(lang)
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
@@ -52,7 +52,7 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
preamble.thenAccept {
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.sendUpdate()
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context, pnx))
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
faintingPokemon.contextManager.add(context)
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.util.battleLang
class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction {
override fun invoke(battle: PokemonBattle) {
val (pnx, _) = message.pnxAndUuid(0) ?: return
val battlePokemon = message.battlePokemon(0, battle) ?: return
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
val speciesName = details.substringBefore('-')
@@ -37,7 +38,7 @@ class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction
battle.dispatchWaiting {
battle.minorBattleActions[battlePokemon.uuid] = message
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName, pnx))
val pokemonName = battlePokemon.getName()
val lang = when(formName) {
"busted", "hero", "complete" -> return@dispatchWaiting
@@ -166,6 +166,11 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
// If we can't find the entity for some reason then we're going to skip the recall animation
val sendOutFuture = CompletableFuture<Unit>()
(pokemonEntity?.recallWithAnimation() ?: CompletableFuture.completedFuture(Unit)).thenApply {
if (battle.ended) {
sendOutFuture.complete(Unit)
return@thenApply
}
// Queue actual swap and send-in after the animation has ended
actor.pokemonList.swap(actor.activePokemon.indexOf(activePokemon), actor.pokemonList.indexOf(newPokemon))
activePokemon.battlePokemon = newPokemon
@@ -43,17 +43,17 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
val losersText = losers.map { it.getName() }.reduce { acc, next -> acc + " & " + next }
val wasCaught = battle.showdownMessages.any { "capture" in it }
battle.dispatch {
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
if (battle.isPvW) {
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
if (battle.isPvW) {
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
}
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
}
}
battle.dispatch {
// broadcast victory / defeat
if (!wasCaught) {
val blackedOut = battle.isPvW && losers.any { it is PlayerBattleActor }
@@ -36,10 +36,18 @@ import java.util.function.Function
open class BattlePokemon(
val originalPokemon: Pokemon,
val effectedPokemon: Pokemon = originalPokemon,
val postBattleEntityOperation: (PokemonEntity) -> Unit = {}
var postBattlePokemonOperations: MutableList<(BattlePokemon) -> Unit> = mutableListOf(),
var postBattleEntityOperations: MutableList<(PokemonEntity) -> Unit> = mutableListOf()
) {
lateinit var actor: BattleActor
constructor(originalPokemon: Pokemon, effectedPokemon: Pokemon, postBattleEntityOperation: (PokemonEntity) -> Unit) : this(
originalPokemon = originalPokemon,
effectedPokemon = effectedPokemon,
postBattlePokemonOperations = mutableListOf<(BattlePokemon) -> Unit>(),
postBattleEntityOperations = mutableListOf(postBattleEntityOperation)
)
companion object {
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
//TOOD figure out a closer registry access (might have to break some method signatures for this (1.7?)
@@ -27,6 +27,7 @@ interface ShowdownService {
fun openConnection()
fun closeConnection()
fun startBattle(battle: PokemonBattle, messages: Array<String>)
fun endBattle(battle: PokemonBattle)
fun send(battleId: UUID, messages: Array<String>)
fun getRegistryData(type: String): JsonArray
fun sendRegistryData(data: Map<String, String>, type: String)
@@ -129,6 +129,11 @@ class GraalShowdownService : ShowdownService {
startBattleFunction.execute(this, battle.battleId.toString(), messages)
}
override fun endBattle(battle: PokemonBattle) {
val endBattleFunction = context.getBindings("js").getMember("endBattle")
endBattleFunction.execute(battle.battleId.toString())
}
override fun send(battleId: UUID, messages: Array<String>) {
sendToShowdown(battleId, messages)
}
@@ -62,6 +62,11 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
send(battle.battleId, messages)
}
override fun endBattle(battle: PokemonBattle) {
writer.write(">endbattle ${battle.battleId}\n")
acknowledge { Cobblemon.LOGGER.error("Failed to end battle ${battle.battleId}!") }
}
override fun send(battleId: UUID, messages: Array<String>) {
for (message in messages) {
writer.write("$battleId~$message\n")
@@ -1,42 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import net.minecraft.world.item.Item
import net.minecraft.core.dispenser.DispenseItemBehavior
import net.minecraft.world.item.Items
import net.minecraft.world.item.alchemy.PotionContents
import net.minecraft.world.item.alchemy.Potions
import net.minecraft.world.level.block.DispenserBlock
object DispenserBehaviorRegistry {
private val behaviors = mutableMapOf<Item, DispenseItemBehavior>()
fun register(items: List<Item>, behaviorProvider: (Item) -> DispenseItemBehavior) {
for (item in items) {
behaviors[item] = behaviorProvider(item)
}
}
fun applyBehaviors() {
behaviors.forEach { (item, behavior) ->
DispenserBlock.registerBehavior(item, behavior)
}
}
// TODO: Seems like there can only 1 behavior per item, so registering the leaves with the honey bottle overrode the honey bottle behavior for the log.
// Registering a new behavior with `DispenserBlock.registerBehavior` will override any existing ones for that item, possibly even ones from Minecraft and other mods
// Will probably need to use an alternate way of implementing this to avoid conflicts.
fun registerDispenserBehaviors() {
// register(listOf(PotionContents.createItemStack(Items.POTION, Potions.WATER).item)) { SaccharineLogSlatheredBlock.createBehavior() }
// register(listOf(Items.HONEY_BOTTLE)) { SaccharineLogBlock.createBehavior() }
// register(listOf(Items.HONEY_BOTTLE, Items.GLASS_BOTTLE)) { item -> SaccharineLeafBlock.createBehavior(item)}
applyBehaviors()
}
}
@@ -19,6 +19,7 @@ import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.BaseEntityBlock
@@ -110,6 +111,8 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun getShadeBrightness(state: BlockState, level: BlockGetter, pos: BlockPos): Float = 0.75F
override fun getAnalogOutputSignal(state: BlockState, world: Level, pos: BlockPos): Int {
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
@@ -29,6 +29,7 @@ import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.block.state.properties.IntegerProperty
@@ -43,7 +44,6 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
val CODEC = simpleCodec(::HeartyGrainsBlock)
const val MATURE_AGE = 6
const val AGE_AFTER_HARVEST = 3 // Last single block stage
val WATERLOGGED = BlockStateProperties.WATERLOGGED
val AGE: IntegerProperty = IntegerProperty.create("age", 0, MATURE_AGE)
val HALF: EnumProperty<DoubleBlockHalf> = BlockStateProperties.DOUBLE_BLOCK_HALF
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.voxelShape
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@@ -71,10 +72,10 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
private val WEST_AABB_TOP = buildCollider(top = true, Direction.WEST)
private val EAST_AABB_TOP = buildCollider(top = true, Direction.EAST)
private val SOUTH_AABB_BOTTOM = buildCollider(top = false, Direction.SOUTH)
private val NORTH_AABB_BOTTOM = buildCollider(top = false, Direction.NORTH)
private val WEST_AABB_BOTTOM = buildCollider(top = false, Direction.WEST)
private val EAST_AABB_BOTTOM = buildCollider(top = false, Direction.EAST)
private val SOUTH_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.SOUTH, EAST_AABB_BOTTOM)
private val NORTH_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.NORTH, EAST_AABB_BOTTOM)
private val WEST_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.WEST, EAST_AABB_BOTTOM)
private fun buildCollider(top: Boolean, direction: Direction): VoxelShape {
if (top) {
@@ -0,0 +1,128 @@
/*
* 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.block
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock
import net.minecraft.world.level.block.SimpleWaterloggedBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.AttachFace
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class PlaqueBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBlock(properties), SimpleWaterloggedBlock {
companion object {
val CODEC: MapCodec<PlaqueBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::PlaqueBlock)
}
val FLOOR_NORTH = Shapes.or(
Shapes.box(0.0625, 0.25, 0.4375, 0.9375, 1.0, 0.5625),
Shapes.box(0.125, 0.0, 0.4375, 0.25, 0.25, 0.5625),
Shapes.box(0.75, 0.0, 0.4375, 0.875, 0.25, 0.5625),
)
val WALL_NORTH = Shapes.box(0.0625, 0.125, 0.9375, 0.9375, 0.875, 1.0)
val CEILING_NORTH = Shapes.box(0.0625, 0.0, 0.4375, 0.9375, 0.75, 0.5625)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.NORTH)
.setValue(FACE, AttachFace.WALL)
.setValue(WATERLOGGED, false)
)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING, FACE, WATERLOGGED)
}
override fun getFluidState(blockState: BlockState): FluidState? {
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val fluidState = context.level.getFluidState(context.clickedPos)
for (direction in context.nearestLookingDirections) {
val blockState: BlockState
if (direction.getAxis() === Direction.Axis.Y) {
blockState = (defaultBlockState()
.setValue(FACE, if (direction == Direction.UP) AttachFace.CEILING else AttachFace.FLOOR) as BlockState)
.setValue(FACING, context.horizontalDirection.opposite)
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
) as BlockState
} else {
blockState = (defaultBlockState()
.setValue(FACE, AttachFace.WALL) as BlockState)
.setValue(FACING, direction.opposite)
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
) as BlockState
}
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
return blockState
}
}
return null
}
override fun codec(): MapCodec<PlaqueBlock> = CODEC
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
val direction = state.getValue(FACING)
return when (state.getValue(FACE)) {
AttachFace.FLOOR ->
if (direction.axis === Direction.Axis.X) {
rotateShape(Direction.NORTH, Direction.WEST, FLOOR_NORTH)
} else FLOOR_NORTH
AttachFace.WALL ->
when (direction) {
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_NORTH
else -> rotateShape(Direction.NORTH, direction, WALL_NORTH)
}
AttachFace.CEILING ->
if (direction.axis === Direction.Axis.X) {
rotateShape(Direction.NORTH, Direction.WEST, CEILING_NORTH)
} else CEILING_NORTH
}
}
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (getConnectedDirection(state).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
return if (state.getValue(FACE) == AttachFace.CEILING) {
val direction = getConnectedDirection(state)
canSupportCenter(level, pos.relative(direction.opposite), direction)
} else {
super.canSurvive(state, level, pos)
}
}
}
@@ -28,6 +28,8 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.alchemy.PotionContents
import net.minecraft.world.item.alchemy.Potions
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
@@ -67,9 +69,16 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
if (blockState.block is SaccharineLeafBlock) {
val currentAge = blockState.getValue(AGE)
val newAge = when {
item == Items.HONEY_BOTTLE && currentAge < MAX_AGE -> (currentAge + 2).coerceAtMost(MAX_AGE)
item == Items.GLASS_BOTTLE && currentAge > MIN_AGE -> (currentAge - 2).coerceAtLeast(MIN_AGE)
val waterBottle = PotionContents.createItemStack(Items.POTION, Potions.WATER).item
val newAge = when (item) {
Items.HONEY_BOTTLE if currentAge < MAX_AGE ->
(currentAge + 2).coerceAtMost(MAX_AGE)
waterBottle if currentAge > MIN_AGE ->
(currentAge - 2).coerceAtLeast(MIN_AGE)
else -> currentAge
}
@@ -79,21 +88,23 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
stack.shrink(1)
val dispenserEntity = source.blockEntity
val outputItem = if (item == Items.HONEY_BOTTLE) Items.GLASS_BOTTLE else Items.HONEY_BOTTLE
val outputItem = Items.GLASS_BOTTLE
val outputStack = ItemStack(outputItem)
var added = false
for (i in 0 until dispenserEntity.containerSize) {
val slotStack = dispenserEntity.getItem(i)
if (dispenserEntity != null) {
for (i in 0 until dispenserEntity.containerSize) {
val slotStack = dispenserEntity.getItem(i)
if (slotStack.isEmpty) {
dispenserEntity.setItem(i, outputStack.copy())
added = true
break
} else if (slotStack.`is`(outputItem) && slotStack.count < slotStack.maxStackSize) {
slotStack.grow(1)
added = true
break
if (slotStack.isEmpty) {
dispenserEntity.setItem(i, outputStack.copy())
added = true
break
} else if (slotStack.`is`(outputItem) && slotStack.count < slotStack.maxStackSize) {
slotStack.grow(1)
added = true
break
}
}
}
@@ -25,6 +25,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.DispenserBlock
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.RotatedPillarBlock
import net.minecraft.world.level.block.state.BlockState
@@ -37,7 +38,7 @@ class SaccharineLogBlock(properties: Properties) : RotatedPillarBlock(properties
fun createBehavior(): DispenseItemBehavior {
return DispenseItemBehavior { source, stack ->
val level = source.level
val facing = source.state.getValue(HorizontalDirectionalBlock.FACING)
val facing = source.state.getValue(DispenserBlock.FACING)
val pos = source.pos.relative(facing)
val blockState = level.getBlockState(pos)
@@ -50,7 +51,13 @@ class SaccharineLogBlock(properties: Properties) : RotatedPillarBlock(properties
}
}
fun changeLogTypeDispenser(level: ServerLevel, pos: BlockPos, newState: BlockState, stack: ItemStack, source: BlockSource) {
fun changeLogTypeDispenser(
level: ServerLevel,
pos: BlockPos,
newState: BlockState,
stack: ItemStack,
source: BlockSource
) {
level.setBlock(pos, newState, 3)
level.gameEvent(null, GameEvent.BLOCK_CHANGE, pos)
@@ -75,11 +82,23 @@ class SaccharineLogBlock(properties: Properties) : RotatedPillarBlock(properties
}
if (!added) {
Containers.dropItemStack(level, source.pos.x.toDouble(), source.pos.y.toDouble(), source.pos.z.toDouble(), glassBottle)
Containers.dropItemStack(
level,
source.pos.x.toDouble(),
source.pos.y.toDouble(),
source.pos.z.toDouble(),
glassBottle
)
}
}
fun changeLogType(level: Level, pos: BlockPos, newState: BlockState, player: Player? = null, itemStack: ItemStack? = null) {
fun changeLogType(
level: Level,
pos: BlockPos,
newState: BlockState,
player: Player? = null,
itemStack: ItemStack? = null
) {
level.setBlock(pos, newState, 3)
level.gameEvent(null, GameEvent.BLOCK_CHANGE, pos)
@@ -93,7 +112,15 @@ class SaccharineLogBlock(properties: Properties) : RotatedPillarBlock(properties
}
}
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hitResult: BlockHitResult): ItemInteractionResult {
override fun useItemOn(
stack: ItemStack,
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hand: InteractionHand,
hitResult: BlockHitResult
): ItemInteractionResult {
val axis = state.getValue(AXIS)
val itemStack = player.getItemInHand(hand)
if (!level.isClientSide && itemStack.`is`(Items.HONEY_BOTTLE) && axis == Direction.Axis.Y) {
@@ -0,0 +1,204 @@
/*
* 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.block
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.SimpleWaterloggedBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import kotlin.math.min
class StackableItemBlock(settings: Properties, val type: StackableItemBlockType): HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
enum class StackableItemBlockType: StringRepresentable {
BATTLE_ITEM,
ETHER,
MAX_POTION,
POTION,
STATUS_RESTORE;
override fun getSerializedName(): String = this.name.lowercase()
companion object {
val CODEC = StringRepresentable.fromEnum(::values)
}
}
companion object {
val MAX_AMOUNT: Int = 4
val AMOUNT: IntegerProperty = IntegerProperty.create("amount", 1, MAX_AMOUNT)
val CODEC: MapCodec<StackableItemBlock> = RecordCodecBuilder.mapCodec { it.group(
propertiesCodec(),
StackableItemBlockType.CODEC.fieldOf("type").forGetter(StackableItemBlock::type)
).apply(it, ::StackableItemBlock) }
val northShapeMap: Map<StackableItemBlockType, List<VoxelShape>> = mapOf(
StackableItemBlockType.BATTLE_ITEM to listOf(
Shapes.box(0.34375, 0.0, 0.34375, 0.65625, 0.4375, 0.65625),
Shapes.or(
Shapes.box(0.09375, 0.0, 0.34375, 0.40625, 0.4375, 0.65625),
Shapes.box(0.59375, 0.0, 0.40625, 0.90625, 0.4375, 0.71875)
),
Shapes.or(
Shapes.box(0.59375, 0.0, 0.59375, 0.90625, 0.4375, 0.90625),
Shapes.box(0.09375, 0.0, 0.53125, 0.40625, 0.4375, 0.84375),
Shapes.box(0.34375, 0.0, 0.09375, 0.65625, 0.4375, 0.40625)
),
Shapes.or(
Shapes.box(0.09375, 0.0, 0.09375, 0.40625, 0.4375, 0.40625),
Shapes.box(0.59375, 0.0, 0.15625, 0.90625, 0.4375, 0.46875),
Shapes.box(0.65625, 0.0, 0.59375, 0.96875, 0.4375, 0.90625),
Shapes.box(0.15625, 0.0, 0.53125, 0.46875, 0.4375, 0.84375)
)
),
StackableItemBlockType.ETHER to listOf(
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5, 0.6875),
Shapes.or(
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5, 0.75),
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5, 0.6875)
),
Shapes.or(
Shapes.box(0.375, 0.0, 0.125, 0.625, 0.5, 0.5),
Shapes.box(0.125, 0.0, 0.5625, 0.375, 0.5, 0.9375),
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 1.0)
),
Shapes.or(
Shapes.box(0.125, 0.0, 0.0625, 0.375, 0.5, 0.4375),
Shapes.box(0.625, 0.0, 0.125, 0.875, 0.5, 0.5),
Shapes.box(0.6875, 0.0, 0.625, 0.9375, 0.5, 1.0),
Shapes.box(0.1875, 0.0, 0.5625, 0.4375, 0.5, 0.9375)
)
),
StackableItemBlockType.MAX_POTION to listOf(
Shapes.box(0.34375, 0.0, 0.3125, 0.65625, 0.5, 0.6875),
Shapes.or(
Shapes.box(0.09375, 0.0, 0.3125, 0.40625, 0.5, 0.6875),
Shapes.box(0.59375, 0.0, 0.375, 0.90625, 0.5, 0.75)
),
Shapes.or(
Shapes.box(0.34375, 0.0, 0.0625, 0.65625, 0.5, 0.4375),
Shapes.box(0.09375, 0.0, 0.5625, 0.40625, 0.5, 0.9375),
Shapes.box(0.59375, 0.0, 0.625, 0.90625, 0.5, 1.0)
),
Shapes.or(
Shapes.box(0.09375, 0.0, 0.0625, 0.40625, 0.5, 0.4375),
Shapes.box(0.15625, 0.0, 0.5625, 0.46875, 0.5, 0.9375),
Shapes.box(0.65625, 0.0, 0.625, 0.96875, 0.5, 1.0),
Shapes.box(0.59375, 0.0, 0.125, 0.90625, 0.5, 0.5)
).singleEncompassing()
),
StackableItemBlockType.POTION to listOf(
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5625, 0.75),
Shapes.or(
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5625, 0.75),
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5625, 0.8125)
),
Shapes.or(
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.5625, 0.5),
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.5625, 1.0),
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.5625, 0.9375)
),
Shapes.or(
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.5625, 0.4375),
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.5625, 0.5),
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.5625, 1.0),
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.5625, 0.9375)
)
),
StackableItemBlockType.STATUS_RESTORE to listOf(
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.4375, 0.6875),
Shapes.or(
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.4375, 0.75),
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.4375, 0.75)
),
Shapes.or(
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.4375, 0.875),
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.4375, 0.9375),
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.4375, 0.4375)
),
Shapes.or(
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.4375, 0.875),
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.4375, 0.375),
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.4375, 0.4375),
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.4375, 0.9375)
)
)
)
fun getShape(type: StackableItemBlockType, count: Int): VoxelShape = northShapeMap[type]?.let { list -> list[(count - 1).coerceIn(0, list.lastIndex)] } ?: Shapes.block()
fun getMaxAmount(type: StackableItemBlockType): Int = min(northShapeMap[type]?.size ?: 1 , MAX_AMOUNT)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(AMOUNT, 1)
.setValue(WATERLOGGED, false))
}
override fun codec(): MapCodec<StackableItemBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, AMOUNT, WATERLOGGED)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
val count = state.getValue(AMOUNT)
return rotateShape(Direction.NORTH, direction, getShape(type, count))
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return getCollisionShape(state, level, pos, context).singleEncompassing()
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val blockState = context.level.getBlockState(context.clickedPos)
val fluidState = context.level.getFluidState(context.clickedPos)
return if (blockState.`is`(this)) blockState.setValue(AMOUNT, min(getMaxAmount(type), (blockState.getValue(AMOUNT) + 1)))
else super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
}
override fun canBeReplaced(state: BlockState, useContext: BlockPlaceContext): Boolean {
return if (
!useContext.isSecondaryUseActive
&& useContext.itemInHand.`is`(this.asItem())
&& state.getValue(AMOUNT) <= getMaxAmount(type)
) true else super.canBeReplaced(state, useContext)
}
}
@@ -0,0 +1,81 @@
/*
* 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.block
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.SimpleWaterloggedBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
open class WallAttachedDirectionalBlock(settings: Properties): HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
companion object {
val CODEC = simpleCodec(::WallAttachedDirectionalBlock)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(WATERLOGGED, false))
}
override fun codec(): MapCodec<out WallAttachedDirectionalBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, WATERLOGGED)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
var direction = context.clickedFace
if (direction === null || direction.axis === Direction.Axis.Y) {
direction = context.horizontalDirection.opposite
}
val fluidState = context.level.getFluidState(context.clickedPos)
val blockState = this.defaultBlockState()
.setValue(HORIZONTAL_FACING, direction)
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
) as BlockState
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
return blockState
}
return null
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
return canAttach(level, pos, state.getValue(HORIZONTAL_FACING))
}
fun canAttach(reader: LevelReader, pos: BlockPos, direction: Direction): Boolean {
val blockPos = pos.relative(direction.opposite)
return reader.getBlockState(blockPos).isFaceSturdy(reader, blockPos, direction)
}
}
@@ -0,0 +1,68 @@
/*
* 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.block
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class WallAttachedDirectionalShapeBlock(settings: Properties, val width: Int = 16, val height: Int = 16, val depth: Int = 16): WallAttachedDirectionalBlock(settings) {
companion object {
/**
* Calculates a [VoxelShape] based on a center point of (8, 8, 16).
*
* The resulting shape is centered X horizontally and Y vertically
* expanding inwards/North along the Z-axis from the South face (16).
*
* @param width The width of the shape from 0-16, along the X-axis.
* @param height The height of the shape from 0-16 along the Y-axis.
* @param depth The depth of the shape from 0-16 along the Z-axis, extending inwards from 16.
* @return A VoxelShape.
*/
fun convertToShape(sizeX: Int, sizeY: Int, sizeZ: Int): VoxelShape {
val x = sizeX.coerceIn(0, 16).toDouble()
val y = sizeY.coerceIn(0, 16).toDouble()
val z = sizeZ.coerceIn(0, 16).toDouble()
val minX = (8.0 - (x / 2.0)) / 16.0
val maxX = (8.0 + (x / 2.0)) / 16.0
val minY = (8.0 - (y / 2.0)) / 16.0
val maxY = (8.0 + (y / 2.0)) / 16.0
val minZ = (16.0 - z) / 16.0
return Shapes.box(minX, minY, minZ, maxX, maxY, 1.0)
}
}
override fun codec(): MapCodec<WallAttachedDirectionalShapeBlock> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
propertiesCodec(),
Codec.INT.fieldOf("width").forGetter { it.width },
Codec.INT.fieldOf("height").forGetter { it.height },
Codec.INT.fieldOf("depth").forGetter { it.depth }
).apply(instance, { properties, width, height, depth ->
WallAttachedDirectionalShapeBlock(properties, width, height, depth)
})
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(FACING)
return rotateShape(Direction.NORTH, direction, convertToShape(width, height, depth))
}
}
@@ -0,0 +1,100 @@
/*
* 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.block
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import kotlin.math.min
class WallAttachedStackableItemBlock(settings: Properties, val type: WallStackableItemBlockType): WallAttachedDirectionalBlock(settings) {
enum class WallStackableItemBlockType: StringRepresentable {
TAG;
override fun getSerializedName(): String = this.name.lowercase()
companion object {
val CODEC = StringRepresentable.fromEnum(::values)
}
}
companion object {
val MAX_AMOUNT: Int = 8
val AMOUNT: IntegerProperty = IntegerProperty.create("amount", 1, MAX_AMOUNT)
val CODEC: MapCodec<WallAttachedStackableItemBlock> = RecordCodecBuilder.mapCodec { it.group(
propertiesCodec(),
WallStackableItemBlockType.CODEC.fieldOf("type").forGetter(WallAttachedStackableItemBlock::type)
).apply(it, ::WallAttachedStackableItemBlock) }
val northShapeMap: Map<WallStackableItemBlockType, List<VoxelShape>> = mapOf(
WallStackableItemBlockType.TAG to listOf(
Shapes.box(0.375, 0.25, 0.9375, 0.625, 0.6875, 1.0),
Shapes.box(0.375, 0.0625, 0.9375, 1.0, 0.6875, 1.0),
Shapes.box(0.0, 0.0625, 0.9375, 1.0, 0.9375, 1.0),
Shapes.box(0.0, 0.0625, 0.9375, 1.0, 1.0, 1.0),
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0)
)
)
fun getShape(type: WallStackableItemBlockType, count: Int): VoxelShape = northShapeMap[type]?.let { list -> list[(count - 1).coerceIn(0, list.lastIndex)] } ?: Shapes.block()
fun getMaxAmount(type: WallStackableItemBlockType): Int = min(northShapeMap[type]?.size ?: 1 , MAX_AMOUNT)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(AMOUNT, 1)
.setValue(WATERLOGGED, false))
}
override fun codec(): MapCodec<WallAttachedStackableItemBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(AMOUNT)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
val count = state.getValue(AMOUNT)
return rotateShape(Direction.NORTH, direction, getShape(type, count))
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val blockState = context.level.getBlockState(context.clickedPos)
return if (blockState.`is`(this)) blockState.setValue(AMOUNT, min(getMaxAmount(type), (blockState.getValue(AMOUNT) + 1)))
else super.getStateForPlacement(context)
}
override fun canBeReplaced(state: BlockState, useContext: BlockPlaceContext): Boolean {
return if (
!useContext.isSecondaryUseActive
&& useContext.itemInHand.`is`(this.asItem())
&& state.getValue(AMOUNT) <= getMaxAmount(type)
) true else super.canBeReplaced(state, useContext)
}
}
@@ -0,0 +1,57 @@
/*
* 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.block.dispenser
import net.minecraft.core.dispenser.BlockSource
import net.minecraft.core.dispenser.DispenseItemBehavior
import net.minecraft.world.item.ItemStack
import java.util.concurrent.CopyOnWriteArrayList
class ChainedDispenseItemBehavior(private val base: DispenseItemBehavior) : DispenseItemBehavior {
private val extras = CopyOnWriteArrayList<DispenseItemBehavior>()
fun add(behavior: DispenseItemBehavior) {
extras.add(behavior)
}
override fun dispense(blockSource: BlockSource, itemStack: ItemStack): ItemStack {
var current = itemStack
for (b in extras) {
val prev = current.copy()
val next = try {
b.dispense(blockSource, current)
} catch (t: Throwable) {
continue
}
current = next
if (handled(prev, current)) {
return current
}
}
val beforeBase = current.copy()
val afterBase = try {
base.dispense(blockSource, current)
} catch (_: Throwable) {
return current
}
if (handled(beforeBase, afterBase)) {
return afterBase
}
return afterBase
}
private fun handled(before: ItemStack, after: ItemStack): Boolean {
return !ItemStack.matches(before, after)
}
}
@@ -0,0 +1,90 @@
/*
* 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.block.dispenser
import com.cobblemon.mod.common.block.SaccharineLeafBlock
import com.cobblemon.mod.common.block.SaccharineLogBlock
import com.cobblemon.mod.common.block.SaccharineLogSlatheredBlock
import com.cobblemon.mod.common.mixin.accessor.DispenserBlockAccessor
import net.minecraft.core.dispenser.DispenseItemBehavior
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraft.world.item.alchemy.PotionContents
import net.minecraft.world.item.alchemy.Potions
import net.minecraft.world.level.block.DispenserBlock
object DispenserBehaviorRegistry {
private val behaviors = mutableMapOf<Item, DispenseItemBehavior>()
fun register(items: List<Item>, behaviorProvider: (Item) -> DispenseItemBehavior) {
register(behaviorProvider, items = items)
}
fun register(vararg behaviorProviders: (Item) -> DispenseItemBehavior, items: List<Item>) {
for (item in items) {
val providedBehaviors = behaviorProviders.map { provider -> provider(item) }
val existingLocal = behaviors[item]
if (existingLocal is ChainedDispenseItemBehavior) {
providedBehaviors.forEach { existingLocal.add(it) }
continue
}
val currentRegistered = getCurrentRegisteredBehavior(item)
if (currentRegistered is ChainedDispenseItemBehavior) {
behaviors[item] = currentRegistered
providedBehaviors.forEach { currentRegistered.add(it) }
continue
}
val base = currentRegistered ?: DispenseItemBehavior.NOOP
val chained = ChainedDispenseItemBehavior(base)
providedBehaviors.forEach { chained.add(it) }
behaviors[item] = chained
DispenserBlock.registerBehavior(item, chained)
}
}
private fun getCurrentRegisteredBehavior(item: Item): DispenseItemBehavior? {
return try {
val map = DispenserBlockAccessor.getItemBehaviors()
map[item]
} catch (t: Throwable) {
DispenseItemBehavior.LOGGER.error("Failed to read existing dispenser behaviors via mixin accessor", t)
null
}
}
fun registerDispenserBehaviors() {
register(
{ SaccharineLogSlatheredBlock.createBehavior() },
items = listOf(
PotionContents.createItemStack(
Items.POTION,
Potions.WATER
).item
)
)
register(
{ SaccharineLogBlock.createBehavior() },
items = listOf(Items.HONEY_BOTTLE)
)
register(
{ item -> SaccharineLeafBlock.createBehavior(item) },
items = listOf(
Items.HONEY_BOTTLE,
PotionContents.createItemStack(
Items.POTION,
Potions.WATER
).item
)
)
}
}
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.pasture.PastureLinkManager
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.textClickHandlers
import com.cobblemon.mod.common.block.PastureBlock
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
@@ -110,10 +109,10 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
for (tetheredPokemon in tetheredPokemonList) {
val pokemon = tetheredPokemon.getPokemon()!!
if (pokemon.currentFullness > 0) {
pokemon.tickMetabolism()
pokemon.tickMetabolism(1)
}
if (pokemon.interactionCooldowns.any()) {
pokemon.tickInteractionCooldown()
pokemon.tickInteractionCooldown(1)
}
}
blockEntity.togglePastureOn(blockEntity.getInRangeViewerCount(world, blockEntity.blockPos) > 0)
@@ -128,11 +128,11 @@ class FossilMultiblockStructure (
if(pokemon != null) {
pokemon.caughtBall = ballType
player.party().add(pokemon)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, 1.0F, 1.0F)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, player))
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, pokemon)
player.party().add(pokemon)
}
// Turn the monitor off
@@ -24,9 +24,10 @@ import com.cobblemon.mod.common.block.entity.TintBlockEntity
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
import com.cobblemon.mod.common.client.gui.RideOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
@@ -111,7 +112,7 @@ object CobblemonClient {
val overlay: PartyOverlay by lazy { PartyOverlay() }
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
val rideControlsOverlay: RideOverlay by lazy { RideOverlay() }
fun onLogin() {
clientPlayerData = ClientGeneralPlayerData()
@@ -128,6 +129,8 @@ object CobblemonClient {
battleOverlay.onLogout()
ClientTaskTracker.clear()
checkedStarterScreen = false
overlay.resetAttachedToast()
PartyTutorialToasts.reset()
// CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
@@ -165,13 +168,12 @@ object CobblemonClient {
PlatformEvents.CLIENT_TICK_POST.subscribe { event ->
val player = event.client.player
if (player != null) {
var selectedItem = player.inventory.getItem(player.inventory.selected)
val selectedItem = player.inventory.getItem(player.inventory.selected)
if (pokedexUsageContext.scanningGuiOpen &&
!(selectedItem.`is`(CobblemonItemTags.POKEDEX)) &&
!(player.offhandItem.`is`(CobblemonItemTags.POKEDEX) &&
player.isUsingItem == true &&
player.usedItemHand == InteractionHand.OFF_HAND
)
player.isUsingItem &&
player.usedItemHand == InteractionHand.OFF_HAND)
) {
// Stop using Pokédex in main hand if player switches to a different slot in hotbar
pokedexUsageContext.stopUsing(PokedexUsageContext.OPEN_SCANNER_BUFFER_TICKS + 1)
@@ -216,7 +218,7 @@ object CobblemonClient {
}
fun registerColors() {
this.implementation.registerBlockColors(BlockColor { blockState, view, blockPos, tintIndex ->
this.implementation.registerBlockColors(BlockColor { _, view, blockPos, _ ->
blockPos?.let { pos ->
view?.getBlockEntity(pos)?.let { blockEntity ->
if (blockEntity is TintBlockEntity) return@BlockColor blockEntity.getTint()
@@ -319,7 +321,48 @@ object CobblemonClient {
CobblemonBlocks.PINK_CAMPFIRE_POT,
CobblemonBlocks.RED_CAMPFIRE_POT,
CobblemonBlocks.WHITE_CAMPFIRE_POT,
CobblemonBlocks.YELLOW_CAMPFIRE_POT
CobblemonBlocks.YELLOW_CAMPFIRE_POT,
CobblemonBlocks.WHITE_PLAQUE,
CobblemonBlocks.LIGHT_GRAY_PLAQUE,
CobblemonBlocks.GRAY_PLAQUE,
CobblemonBlocks.BLACK_PLAQUE,
CobblemonBlocks.BROWN_PLAQUE,
CobblemonBlocks.RED_PLAQUE,
CobblemonBlocks.ORANGE_PLAQUE,
CobblemonBlocks.YELLOW_PLAQUE,
CobblemonBlocks.LIME_PLAQUE,
CobblemonBlocks.GREEN_PLAQUE,
CobblemonBlocks.CYAN_PLAQUE,
CobblemonBlocks.LIGHT_BLUE_PLAQUE,
CobblemonBlocks.BLUE_PLAQUE,
CobblemonBlocks.PURPLE_PLAQUE,
CobblemonBlocks.MAGENTA_PLAQUE,
CobblemonBlocks.PINK_PLAQUE,
CobblemonBlocks.BLUNDER_POLICY,
CobblemonBlocks.WEAKNESS_POLICY,
CobblemonBlocks.POTION,
CobblemonBlocks.SUPER_POTION,
CobblemonBlocks.HYPER_POTION,
CobblemonBlocks.MAX_POTION,
CobblemonBlocks.FULL_RESTORE,
CobblemonBlocks.ANTIDOTE,
CobblemonBlocks.AWAKENING,
CobblemonBlocks.BURN_HEAL,
CobblemonBlocks.ICE_HEAL,
CobblemonBlocks.PARALYZE_HEAL,
CobblemonBlocks.FULL_HEAL,
CobblemonBlocks.ETHER,
CobblemonBlocks.MAX_ETHER,
CobblemonBlocks.ELIXIR,
CobblemonBlocks.MAX_ELIXIR,
CobblemonBlocks.DIRE_HIT,
CobblemonBlocks.GUARD_SPEC,
CobblemonBlocks.X_ACCURACY,
CobblemonBlocks.X_ATTACK,
CobblemonBlocks.X_DEFENSE,
CobblemonBlocks.X_SP_ATK,
CobblemonBlocks.X_SP_DEF,
CobblemonBlocks.X_SPEED
)
this.createBoatModelLayers()
@@ -11,10 +11,11 @@ package com.cobblemon.mod.common.client.battle
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.ForcePassActionResponse
import com.cobblemon.mod.common.battles.MoveActionResponse
import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ShowdownMoveset
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
@@ -61,8 +62,15 @@ class ClientBattle(
if (index != -1) {
while(index < pendingActionRequests.size) {
val request = pendingActionRequests[index]
if (request.response != PassActionResponse && request.response !is ForcePassActionResponse)
if (request.response != PassActionResponse && request.response !is ForcePassActionResponse) {
val response = request.response
// If we're cancelling a turn that used a gimmick, we re-enable the ability for that gimmick to be used.
if (response is MoveActionResponse && response.gimmickID != null) {
val gimmick = ShowdownMoveset.Gimmick.entries.find { it.id == response.gimmickID }
pendingActionRequests.forEach { it.moveSet?.pendingGimmickUsedThisTurn?.remove(gimmick) }
}
request.response = null
}
index++
}
}
@@ -507,9 +507,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
override fun positionRider(passenger: Entity, positionUpdater: MoveFunction) {
val locatorName = getSeatLocator(passenger)
val locator = this.locatorStates[locatorName]
if (locator == null) return
val locator = this.locatorStates[locatorName] ?: return
val offset = locator.matrix.getTranslation(Vector3f())
.sub(
@@ -18,10 +18,10 @@ import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.keybind.keybinds.*
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.toAssetPath
import java.util.UUID
import net.minecraft.client.DeltaTracker
import net.minecraft.client.Minecraft
@@ -38,6 +39,7 @@ import net.minecraft.client.gui.components.toasts.AdvancementToast
import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.screens.ChatScreen
import net.minecraft.client.gui.screens.Screen
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import kotlin.math.ceil
import kotlin.math.floor
@@ -148,6 +150,9 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
val portraitFrameOffsetY = 2
val selectedSlot = CobblemonClient.storage.selectedSlot
val partySize = party.slots.count { it != null }
PartyTutorialToasts.onOverlayRender(partySize)
party.forEachIndexed { index, pokemon ->
if (pokemon != null) {
val selectedOffsetX = if (selectedSlot == index) 6 else 0
@@ -467,7 +472,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
)
}
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
val ballIcon = pokemon.caughtBall.name.toAssetPath("textures/gui/ball/", ".png")
val ballHeight = 22
blitk(
matrixStack = matrices,
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
@@ -19,6 +21,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinState
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asTranslated
@@ -30,17 +33,28 @@ import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.ChatScreen
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.player.Input
import net.minecraft.client.player.LocalPlayer
import net.minecraft.client.renderer.BiomeColors
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.BlockAndTintGetter
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.round
class RideControlsOverlay : Gui(Minecraft.getInstance()) {
class RideOverlay : Gui(Minecraft.getInstance()) {
companion object {
private const val CONTROLS_FADE_FRAMES = 20F
private const val BASELINE_FPS = 60F
private const val MOTION_FADE_FRAMES = 10
private const val MOTION_FRAMES = 10
private const val MOTION_OVERLAY_MAX_OPACITY = 0.6F
private const val HALF_SCALE = 0.5F
private val mouseResource = cobblemonResource("textures/gui/riding/mouse.png")
@@ -59,6 +73,8 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
private val keyJumpResource = cobblemonResource("textures/gui/riding/key_jump.png")
private val keySneakWideResource = cobblemonResource("textures/gui/riding/key_sneak_wide.png")
private val keyJumpWideResource = cobblemonResource("textures/gui/riding/key_jump_wide.png")
private val motionLinesResource = cobblemonResource("textures/gui/riding/motion_lines_overlay.png")
}
private val screenExemptions: List<Class<out Screen>> = listOf(ChatScreen::class.java)
@@ -67,6 +83,9 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
private var controlsDurationFrames = controlsMaxDurationFrames
private var controlsFadeFrames = CONTROLS_FADE_FRAMES
private var motionFrameIntervals = 0F
private var motionFadeFrames = 0
private var lastMouseX = 0
private var lastMouseY = 0
@@ -81,43 +100,70 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
override fun render(context: GuiGraphics, tickCounter: DeltaTracker) {
val minecraft = Minecraft.getInstance()
// Hiding if a Screen is open and not exempt
minecraft.screen?.let { screen ->
if ((!screenExemptions.contains(screen.javaClass as Class<out Screen>))) return
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) return
minecraft.player?.let { player ->
// Hiding if a Screen is open and not exempt
minecraft.screen?.let { screen ->
if ((!screenExemptions.contains(screen.javaClass as Class<out Screen>))) return
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) return
val riddenEntity = player.vehicle
if (riddenEntity != null && riddenEntity is PokemonEntity) {
// Only show controls to driver
if (riddenEntity === player.controlledVehicle) {
renderControls(minecraft, context, tickCounter, riddenEntity)
}
val ridingBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
setCurrentRideBehaviour(ridingBehaviourSettings, ridingBehaviourState)
val tickDelta = tickCounter.realtimeDeltaTicks.takeIf { !minecraft.isPaused } ?: 0F
val updateInterval = (tickDelta / 20F) * BASELINE_FPS
renderMotionOverlay(minecraft, player, riddenEntity, context, updateInterval, ridingBehaviourSettings, ridingBehaviourState)
renderRideControls(minecraft, context, updateInterval, ridingBehaviourSettings, ridingBehaviourState)
} else {
resetControlsOverlayState()
}
}
}
private fun renderControls(minecraft: Minecraft, context: GuiGraphics, tickCounter: DeltaTracker, riddenEntity: PokemonEntity) {
// Update max duration if user has changed config
val configDuration = Cobblemon.config.displayControlSeconds * BASELINE_FPS
if (controlsMaxDurationFrames != configDuration) {
controlsMaxDurationFrames = configDuration
resetControlsOverlayState()
fun renderMotionOverlay(minecraft: Minecraft, player: LocalPlayer, riddenEntity: Entity, context: GuiGraphics, updateInterval: Float, ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
if (currentBehaviourKey == JetBehaviour.KEY || currentBehaviourKey == DolphinBehaviour.KEY) {
val isBoosting = if (currentBehaviourKey == JetBehaviour.KEY) (ridingBehaviourState as JetState).boosting.get()
else (ridingBehaviourState as DolphinState).boosting.get()
val interval = round(updateInterval).toInt()
motionFadeFrames = (motionFadeFrames + (interval * (if (isBoosting) 1 else -1))).coerceIn(0, MOTION_FADE_FRAMES)
val opacity = (motionFadeFrames * (1F / MOTION_FADE_FRAMES)).coerceIn(0F, MOTION_OVERLAY_MAX_OPACITY)
val tint = if (player.isUnderWater && currentBehaviourKey == DolphinBehaviour.KEY) BiomeColors.getAverageWaterColor(minecraft.level as BlockAndTintGetter, riddenEntity.blockPosition()) else 0xFFFFFF
motionFrameIntervals += updateInterval * (if (minecraft.options.cameraType.isMirrored) -1 else 1)
val frameIndex = floor(motionFrameIntervals).toInt() % MOTION_FRAMES
blitk(
matrixStack = context.pose(),
texture = motionLinesResource,
x = 0,
y = 0,
textureHeight = 2560,
width = minecraft.window.guiScaledWidth,
height = minecraft.window.guiScaledHeight,
vOffset = 256 * frameIndex,
alpha = opacity,
red = FastColor.ARGB32.red(tint) / 255F,
green = FastColor.ARGB32.green(tint) / 255F,
blue = FastColor.ARGB32.blue(tint) / 255F
)
} else if (motionFadeFrames > 0) {
motionFadeFrames = 0
}
}
// Don't render if duration config set to 0 or less
if (controlsMaxDurationFrames <= 0F) return
private fun setCurrentRideBehaviour(ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
val rideBehaviourKey = ridingBehaviourSettings.key
val rideBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
val rideBehaviourKey = rideBehaviourSettings.key
// Update current ride behaviour if changed
// If composite, get active behaviour instead
// Update current ride behaviour if changed.
// If behaviour is composite, get active behaviour instead
if (rideBehaviourKey == CompositeBehaviour.KEY) {
val activeBehaviour = (ridingBehaviourState as CompositeState).activeBehaviour.get()
if (currentBehaviourKey != activeBehaviour) {
@@ -128,12 +174,21 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
currentBehaviourKey = rideBehaviourKey
resetControlsOverlayState()
}
}
private fun renderRideControls(minecraft: Minecraft, context: GuiGraphics, updateInterval: Float, ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
// Update max duration if user has changed config
val configDuration = Cobblemon.config.displayControlSeconds * BASELINE_FPS
if (controlsMaxDurationFrames != configDuration) {
controlsMaxDurationFrames = configDuration
resetControlsOverlayState()
}
// Don't render if duration config set to 0 or less
if (controlsMaxDurationFrames <= 0F) return
// Render elements if entity has ride behaviour and if display duration has not ended yet
if (rideBehaviourSettings != null && controlsFadeFrames > 0) {
val tickDelta = tickCounter.realtimeDeltaTicks.takeIf { !minecraft.isPaused } ?: 0F
val updateInterval = (tickDelta / 20F) * BASELINE_FPS
if (ridingBehaviourSettings != null && controlsFadeFrames > 0) {
controlsDurationFrames = max(controlsDurationFrames - updateInterval, 0F)
if (controlsDurationFrames == 0F && controlsFadeFrames > 0) controlsFadeFrames = max(controlsFadeFrames - updateInterval, 0F)
@@ -68,7 +68,7 @@ class BattleMoveSelection(
var moveTiles = baseTiles
val backButton = BattleBackButton(x - 11F, Minecraft.getInstance().window.guiScaledHeight - 22F)
val gimmickButtons = moveSet.getGimmicks().mapIndexed { index, gimmick ->
val gimmickButtons = moveSet.getGimmicks().filter { it !in moveSet.pendingGimmickUsedThisTurn }.mapIndexed { index, gimmick ->
val initOff = BattleBackButton.WIDTH * 0.65F
val xOff = initOff + BattleGimmickButton.SPACING * index
BattleGimmickButton.create(gimmick, this, backButton.x + xOff, backButton.y)
@@ -31,12 +31,14 @@ import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import com.cobblemon.mod.common.util.toAssetPath
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.narration.NarratableEntry
import net.minecraft.client.gui.narration.NarrationElementOutput
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.resources.ResourceLocation
import org.joml.Quaternionf
import org.joml.Vector3f
@@ -231,7 +233,7 @@ class BattleSwitchPokemonSelection(
textureHeight = SELECT_HEIGHT * 2,
)
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
val ballIcon = pokemon.caughtBall.name.toAssetPath("textures/gui/ball/", ".png")
val ballHeight = 22
blitk(
matrixStack = matrixStack,
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.InBattleGimmickMove
import com.cobblemon.mod.common.battles.InBattleMove
import com.cobblemon.mod.common.battles.MoveActionResponse
import com.cobblemon.mod.common.battles.ShowdownMoveset
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
@@ -127,7 +128,7 @@ class BattleTargetSelection(
private val isMultiTarget = multiTargetList?.firstOrNull { it.getPNX() == target.getPNX() } != null
private val isCurrentPokemon = request.activePokemon.battlePokemon?.uuid == target.battlePokemon!!.uuid
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget)
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget, gimmickID)
val state = FloatingState()
open val selectable: Boolean get() = isMultiTarget || (responseTarget != null)
@@ -311,6 +312,13 @@ class BattleTargetSelection(
fun onClick() {
if (!selectable) return
targetSelection.playDownSound(Minecraft.getInstance().soundManager)
// If a gimmick is used, remove the ability to use it on any pending action requests ahead of it
if (response.gimmickID != null) {
val gimmick = ShowdownMoveset.Gimmick.entries.first { it.id == response.gimmickID }
CobblemonClient.battle?.pendingActionRequests?.forEach {
it.moveSet?.pendingGimmickUsedThisTurn?.add(gimmick)
}
}
targetSelection.battleGUI.selectAction(targetSelection.request, response)
}
}
@@ -24,11 +24,13 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import com.cobblemon.mod.common.util.toAssetPath
import com.google.common.collect.Lists
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FormattedCharSequence
import net.minecraft.world.item.ItemStack
import org.joml.Quaternionf
@@ -111,7 +113,7 @@ class PartySlotButton(
)
context.pose().popPose()
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.pokeball!!.asIdentifierDefaultingNamespace().path + ".png")
val ballIcon = pokemon.pokeball!!.asIdentifierDefaultingNamespace().toAssetPath("textures/gui/ball/", ".png")
val ballHeight = 22
blitk(
matrixStack = matrices,
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.party
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.DownShiftPartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
import com.cobblemon.mod.common.client.keybind.keybinds.UpShiftPartyBinding
import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialPacket
import com.cobblemon.mod.common.util.lang
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.components.toasts.AdvancementToast
import net.minecraft.network.chat.Component
import net.minecraft.util.Mth
object PartyTutorialToasts {
private var toastAttached = false
val upKey = UpShiftPartyBinding.boundKey()
val downKey = DownShiftPartyBinding.boundKey()
val upDisplay =
if (upKey.type == InputConstants.Type.KEYSYM && upKey.value == InputConstants.KEY_UP)
Component.literal("↑")
else upKey.displayName
val downDisplay =
if (downKey.type == InputConstants.Type.KEYSYM && downKey.value == InputConstants.KEY_DOWN)
Component.literal("↓")
else downKey.displayName
private val toast = CobblemonToast(
Mth.createInsecureUUID(),
CobblemonItems.POKEDEX_RED.defaultInstance,
lang("ui.party.party_select_title").red(),
lang(
"ui.party.party_select_description",
upDisplay,
downDisplay,
PartySendBinding.boundKey().displayName
).darkGray(),
AdvancementToast.BACKGROUND_SPRITE,
-1F,
0
)
fun reset() {
toastAttached = false
}
fun onOverlayRender(partySize: Int) {
if (partySize <= 1) return
if (!CobblemonClient.clientPlayerData.partySelectTutorialDone) {
showToast()
}
}
fun onArrowKeySwitchedSlot() {
if (CobblemonClient.clientPlayerData.partySelectTutorialDone) return
if (CobblemonClient.storage.party.count { it != null } < 2) return
PartyTutorialPacket().sendToServer()
hideToast()
}
fun onHoldKeyScrollSwitchedSlot() {
if (CobblemonClient.clientPlayerData.partySelectTutorialDone) return
if (CobblemonClient.storage.party.count { it != null } < 2) return
PartyTutorialPacket().sendToServer()
hideToast()
}
private fun showToast() {
val minecraft = Minecraft.getInstance()
if (!toastAttached) {
toast.nextVisibility = Toast.Visibility.SHOW
minecraft.toasts.addToast(toast)
toastAttached = true
}
}
private fun hideToast() {
if (toastAttached) toast.nextVisibility = Toast.Visibility.HIDE
}
}
@@ -40,6 +40,7 @@ import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.toAssetPath
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
@@ -463,7 +464,7 @@ class PCGUI(
)
// Poké Ball
val ballResource = cobblemonResource("textures/item/poke_balls/" + pokemon.caughtBall.name.path + ".png")
val ballResource = pokemon.caughtBall.name.toAssetPath("textures/item/poke_balls/", ".png")
blitk(
matrixStack = matrices,
texture = ballResource,
@@ -237,9 +237,11 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 10, height = 27.5, width = 1)
blitk(matrixStack = matrices, texture = divider, x = pX + 9, y = pY + 2, height = 6, width = 121)
val rideStyle = selectedBehaviour.value.key.path.toString().split("/").last().lowercase()
drawScaledText(
context = context,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
text = lang("ui.ride_style.${selectedBehaviour.key.toString().lowercase()}.${rideStyle}")
.append(" | ").append(lang("ui.ride_style.${selectedBehaviour.key.toString().lowercase()}")).bold(),
x = pX + 69.5,
y = pY + 3,
colour = 0x3A96B6,
@@ -45,6 +45,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.toAssetPath
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
@@ -558,7 +559,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
}
// Poké Ball
val ballResource = cobblemonResource("textures/item/poke_balls/" + selectedPokemon.caughtBall.name.path + ".png")
val ballResource = selectedPokemon.caughtBall.name.toAssetPath("textures/item/poke_balls/", ".png")
blitk(
matrixStack = matrices,
texture = ballResource,
@@ -24,6 +24,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import com.cobblemon.mod.common.util.toAssetPath
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
@@ -107,7 +108,7 @@ class PartySlotWidget(
)
}
val ballIcon = cobblemonResource("textures/gui/ball/" + slotPokemon.caughtBall.name.path + ".png")
val ballIcon = slotPokemon.caughtBall.name.toAssetPath("textures/gui/ball/", ".png")
val ballHeight = 22
blitk(
matrixStack = matrices,
@@ -19,11 +19,11 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import java.math.RoundingMode
import java.text.DecimalFormat
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import java.math.RoundingMode
import java.text.DecimalFormat
class MovesWidget(
pX: Int, pY: Int,
@@ -66,6 +66,7 @@ class StatWidget(
private const val MARKER_WIDTH = 8
private const val WIDTH = 134
private const val HEIGHT = 148
private const val RIDE_ICON_SIZE = 32
const val SCALE = 0.5F
private const val WHITE = 0x00FFFFFF
@@ -336,14 +337,16 @@ class StatWidget(
colour = pentagonColour
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = x + (WIDTH / 2),
y = y + 66,
shadow = true,
centered = true
val rideStyle = selectedBehaviour.value.key.path.toString().split("/").last().lowercase()
val iconResource = cobblemonResource("textures/gui/summary/icon_ride_${selectedBehaviour.key.toString().lowercase()}_${rideStyle}.png")
blitk(
matrixStack = matrices,
texture = iconResource,
x= (x + 59) / SCALE,
y = (y + 62.5) / SCALE,
width = RIDE_ICON_SIZE,
height = RIDE_ICON_SIZE,
scale = SCALE
)
// Stat Labels
@@ -368,6 +371,16 @@ class StatWidget(
verticesOffset = pentagonVerticesOffset,
offsetY = 5.5
)
if (pMouseX >= (x + 59) && pMouseY >= (y + 62.5) && pMouseX <= ((x + 59) + (RIDE_ICON_SIZE * SCALE)) && pMouseY <= ((y + 62.5) + (RIDE_ICON_SIZE * SCALE))) {
context.renderTooltip(
Minecraft.getInstance().font,
lang("ui.ride_style.${selectedBehaviour.key.toString().lowercase()}.${rideStyle}")
.append(" | ").append(lang("ui.ride_style.${selectedBehaviour.key.toString().lowercase()}")),
pMouseX,
pMouseY
)
}
}
} else {
@@ -20,6 +20,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import java.util.*
import kotlin.math.min
import net.minecraft.client.gui.Font
import net.minecraft.util.FormattedCharSequence
@Environment(EnvType.CLIENT)
class CobblemonToast(
@@ -38,11 +40,18 @@ class CobblemonToast(
private var lastTime = 0L
internal var nextVisibility: Toast.Visibility = Toast.Visibility.SHOW
fun getLines(font: Font, maxWidth: Int): List<FormattedCharSequence> {
return font.split(this.description, maxWidth)
}
override fun render(context: GuiGraphics, manager: ToastComponent, startTime: Long): Toast.Visibility {
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height())
val maxWidth = 125
val textRenderer = manager.minecraft.font
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height() + (getLines(textRenderer, maxWidth).size - 1) * 10)
context.drawString(textRenderer, this.title, 30, 7, this.title.style.color?.value ?: -1, false)
context.drawString(textRenderer, this.description, 30, 18, this.description.style.color?.value ?: -1, false)
context.drawWordWrap(textRenderer, this.description, 30, 18, maxWidth, this.description.style.color?.value ?: -1)
context.renderFakeItem(this.icon, 8, 8)
if (this.hasProgressBar()) {
context.fill(3, 28, 157, 29, -1)
@@ -32,6 +32,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.toAssetPath
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
@@ -584,7 +585,7 @@ class TradeGUI(
// Poké Ball
val nameXOffset = if (isOpposing) 75 else 0
val ballResource = cobblemonResource("textures/item/poke_balls/" + pokemon.caughtBall.name.path + ".png")
val ballResource = pokemon.caughtBall.name.toAssetPath("textures/item/poke_balls/", ".png")
blitk(
matrixStack = context.pose(),
texture = ballResource,
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
import com.cobblemon.mod.common.client.keybind.KeybindCategories
import com.mojang.blaze3d.platform.InputConstants
@@ -21,5 +22,6 @@ object DownShiftPartyBinding : CobblemonKeyBinding(
) {
override fun onPress() {
CobblemonClient.storage.shiftSelected(true)
PartyTutorialToasts.onArrowKeySwitchedSlot()
}
}
@@ -143,18 +143,18 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
}
private fun canAttemptDismount(player: LocalPlayer, selectedPartyPokemon: Pokemon?): Boolean {
if (player.vehicle !is PokemonEntity) return false
val vehicle = player.vehicle as PokemonEntity
if (player != vehicle.controllingPassenger) {
return true
}
val vehicle = player.vehicle as? PokemonEntity ?: return false
if (player != vehicle.controllingPassenger) return true
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
return if (isAirRide && hasLandRide) {
false
} else {
vehicle.pokemon.uuid == selectedPartyPokemon?.uuid
if (!Cobblemon.config.enableInFlightDismounting && isAirRide && hasLandRide) {
return false
}
return vehicle.pokemon.uuid == selectedPartyPokemon?.uuid
}
private fun isRidingPokemon(player: LocalPlayer, ignoreControlling: Boolean = false): Boolean {
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
import com.cobblemon.mod.common.client.keybind.KeybindCategories
import com.mojang.blaze3d.platform.InputConstants
@@ -21,5 +22,6 @@ object UpShiftPartyBinding : CobblemonKeyBinding(
) {
override fun onPress() {
CobblemonClient.storage.shiftSelected(false)
PartyTutorialToasts.onArrowKeySwitchedSlot()
}
}
@@ -23,7 +23,7 @@ import net.minecraft.world.phys.Vec3
object CalculateSeatPositionsHandler : ClientNetworkPacketHandler<CalculateSeatPositionsPacket> {
private const val FORMAT = "%.2f"
private const val SEAT_PREFIX = "seat"
private const val SEAT_PREFIX = "seat_"
private const val SIMULATION_PERIOD_TICKS = 15 * 20F
class SeatRanges(
@@ -16,10 +16,8 @@ object SetClientPlayerDataHandler : ClientNetworkPacketHandler<SetClientPlayerDa
override fun handle(packet: SetClientPlayerDataPacket, client: Minecraft) {
if (packet.isIncremental) {
packet.type.incrementalAfterDecodeAction.invoke(packet.playerData)
}
else {
} else {
packet.type.afterDecodeAction.invoke(packet.playerData)
}
}
}

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