Compare commits

..
Author SHA1 Message Date
tmetcalfe89 43c0159471 mock up for it 2024-10-13 16:57:59 -07:00
Hiroku 654c4341ef Merge branch 'bugfix/hiro-fixes-packets' into 'main'
Delay deserialization of Pokemon S2C codec until it's in the handler for...

See merge request cable-mc/cobblemon!1196
2024-10-13 09:40:48 +00:00
Hiroku 2c3112d0d4 Merge branch 'FishingSendoutFix' into 'main'
Closes #1011 Fishing sendout fix

Closes #1011

See merge request cable-mc/cobblemon!1200
2024-10-13 07:06:42 +00:00
JazzMcNade bb4d0298a6 Merge branch 'main' into FishingSendoutFix 2024-10-13 01:46:44 -05:00
JazzMcNade b5f9dfd66d Fixed player pokemon being sent out at the player's feet when initiating a fishing battle. 2024-10-13 01:42:54 -05:00
Paul 0d8bc4351a Merge remote-tracking branch 'origin/main' 2024-10-13 16:08:15 +10:00
Paul f4276bbcc0 Removed temporary raft code, indirectly closes #1087 2024-10-13 16:08:00 +10:00
Paul a2c6d6fa30 Fixed shoulder renderer not applying a pokemon context, closes #1083 2024-10-13 16:03:40 +10:00
Patch Notes GuyandWeblate 2c3ac5da05 Translated using Weblate (Czech)
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/cs/
2024-10-13 02:45:20 +00:00
Patch Notes GuyandWeblate ad9c86372d Translated using Weblate (Japanese)
Currently translated at 91.5% (5841 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2024-10-13 02:45:20 +00:00
Patch Notes GuyandWeblate f531c1eb64 Translated using Weblate (Chinese (Traditional Han script, Hong Kong))
Currently translated at 67.6% (4318 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant_HK/
2024-10-13 02:45:20 +00:00
Patch Notes GuyandWeblate 0a8743158d Translated using Weblate (Portuguese (Portugal))
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate d344ad0bc6 Translated using Weblate (Italian)
Currently translated at 85.8% (5477 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/it/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 62c3ec8c72 Translated using Weblate (Thai)
Currently translated at 72.9% (4653 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/th/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate b4919fea4d Translated using Weblate (English (United Kingdom))
Currently translated at 0.1% (9 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/en_GB/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 2ebb8e64c4 Translated using Weblate (English (en_PT))
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/en_PT/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 379bddc338 Translated using Weblate (Korean)
Currently translated at 86.9% (5551 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 0335eccfb5 Translated using Weblate (Russian)
Currently translated at 94.4% (6028 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 0497489292 Translated using Weblate (Spanish (Mexico))
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es_MX/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 2c168bbce0 Translated using Weblate (Esperanto (eo_EO))
Currently translated at 17.2% (1099 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/eo_EO/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate f6dcbc0d26 Translated using Weblate (Greek (el_CY))
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/el_CY/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate cc54eb8f3f Translated using Weblate (Hungarian)
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/hu/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 65314f7d1d Translated using Weblate (Spanish)
Currently translated at 85.7% (5470 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate bf7f84a94e Translated using Weblate (French)
Currently translated at 91.9% (5871 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2024-10-13 02:45:19 +00:00
MiaandWeblate da64a9c186 Translated using Weblate (German)
Currently translated at 91.8% (5865 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/de/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate a8147beae1 Translated using Weblate (Polish)
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pl/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 25da152ebf Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 71.2% (4550 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2024-10-13 02:45:19 +00:00
Patch Notes GuyandWeblate 1cc3d64c79 Translated using Weblate (Ukrainian)
Currently translated at 88.7% (5663 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/uk/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate 30063e96b5 Translated using Weblate (Dutch)
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/nl/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate 3bd7d09b34 Translated using Weblate (Portuguese (Brazil))
Currently translated at 24.0% (1533 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate c3d5773cc9 Translated using Weblate (Turkish)
Currently translated at 11.2% (718 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/tr/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate c688fbabc4 Translated using Weblate (Swedish)
Currently translated at 17.2% (1099 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/sv/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate 4b313ef54c Translated using Weblate (French (Canada))
Currently translated at 88.7% (5664 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2024-10-13 02:45:18 +00:00
Patch Notes GuyandWeblate 01c2c4efae Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 90.3% (5767 of 6382 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2024-10-13 02:45:18 +00:00
Hiroku aab6fcf891 Merge branch 'Fix1035RestorationExplosions' into 'main'
Closes #1035 restoration explosions

Closes #1035

See merge request cable-mc/cobblemon!1199
2024-10-13 01:53:03 +00:00
JazzMcNade 86ae831d74 Merge branch 'main' into Fix1035RestorationExplosions 2024-10-12 20:35:04 -05:00
JazzMcNade 9d12ce8a33 Fixed tank drops in survival/creative. Reduced code. 2024-10-12 20:34:38 -05:00
Paul 57ef15dea2 Remove reference to non-existent preset 2024-10-13 10:30:56 +10:00
Paul 5650cdc6ed Fixed NPC presets loading from NPCs folder (OOPS), fixed variations not applying to NPC classes, fixed variations not updating aspects after init 2024-10-13 10:30:27 +10:00
Paul 0048622693 Fixed WeightedAspect adapter not being registered for NPC presets 2024-10-13 09:08:44 +10:00
Paul 7940b60d77 Fixed Amaura fossil crash, closes #1085 2024-10-13 08:43:52 +10:00
Paul 886c0c7026 Fixed presets not able to deserialize variation providers 2024-10-13 07:40:25 +10:00
Hiroku 199ab9f4ee Merge branch 'RemoveUnnecessaryGenericHitEffects' into 'main'
Moves with Action Effects no longer display the generic hit effect.

See merge request cable-mc/cobblemon!1197
2024-10-12 21:33:29 +00:00
Paul 573b8cbcf9 Fix SpawnPokemonPacket decoding Species on net thread 2024-10-13 07:32:28 +10:00
JazzMcNade 6583168366 Moves with Action Effects no longer display the generic hit effect. Created generic effect for non-move damage sources. 2024-10-12 14:49:29 -05:00
Paul 8951816ddd Delay deserialization of Pokemon S2C codec until it's in the handler for SetPCPokemonPacket, SetPCBoxPokemonPacket, SetPartyPokemonPacket 2024-10-12 18:18:32 +10:00
Paul bc668d84c9 Fixed advancement display of pokemon renders, closes #976 2024-10-12 16:59:48 +10:00
MiaandWeblate b1290cbc38 Translated using Weblate (German)
Currently translated at 87.5% (5579 of 6375 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/de/
2024-10-12 02:00:08 +00:00
Paul 2261cf1077 Conflicto fixo 2024-10-12 11:55:50 +10:00
Paul 0ae427fd14 Fixed NPC pokemon still dropping items on faint 2024-10-12 10:18:47 +10:00
Paul 37a25c6b5a Fixed applyplayertexture not working on NPCs that delegate to standard but are not standard 2024-10-12 09:12:24 +10:00
MiaandWeblate 597080aac2 Translated using Weblate (German)
Currently translated at 87.4% (5578 of 6375 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/de/
2024-10-11 21:41:28 +00:00
Paul 2ae2d9f53b Initialize NPCs at level 1 by default from /spawnnpc 2024-10-11 20:23:46 +10:00
Paul 38df6f4833 Fixed deserialization of custom_script NPC interactions 2024-10-11 19:51:11 +10:00
Paul bed2e3035b Added /spawnnpc and /spawnnpcat commands 2024-10-11 16:33:39 +10:00
Paul a784aa38cb Lang update 2024-10-11 16:09:46 +10:00
Hiroku c18d853a55 Merge branch 'fuck-assets' into 'main'
move best spawner config & showdown data to resources/data

See merge request cable-mc/cobblemon!1191
2024-10-10 20:45:29 +00:00
Xander Forrest d6581e9931 try with resources block when copying from stream 2024-10-11 00:14:47 +07:00
Nick d7962e7d48 Merge branch 'feature/more-bugs-fixed' into 'main'
more smaller bugs fixed (neoforge users rejoice)

Closes #1068, #1072, and #1073

See merge request cable-mc/cobblemon!1192
2024-10-10 16:57:59 +00:00
PolymetaandNick f3f6a9744b more smaller bugs fixed (neoforge users rejoice) 2024-10-10 16:57:59 +00:00
Nick 715112f8d9 Merge branch 'feature/shinies' into 'main'
Shiny Pokémon Particles and Sound Effects! ✨✨✨💖

See merge request cable-mc/cobblemon!1183
2024-10-10 16:52:39 +00:00
TyzillionandNick 278d0d6f96 Shiny Pokémon Particles and Sound Effects! ✨✨✨💖 2024-10-10 16:52:39 +00:00
Xander Forrest 95c26448bc move best-spawner-config & showdown zip/metadata to resources/data instead of assets, where they should not be
add FileUtils.copyInternalToExternal to move things from internal to, you guessed it, external paths.
2024-10-10 17:20:12 +07:00
Hiroku 8d99ebbcde Merge branch 'feature/bug-galore' into 'main'
various bug fixes

Closes #994, #1037, #1057, #1060, #1070, and #420

See merge request cable-mc/cobblemon!1188
2024-10-10 10:09:19 +00:00
PolymetaandHiroku 4c024ac947 various bug fixes 2024-10-10 10:09:19 +00:00
Hiroku b4946e13f2 Merge branch 'misc' into 'main'
add Upper Hand name/description

See merge request cable-mc/cobblemon!1190
2024-10-10 09:25:16 +00:00
Xander Forrest bc4af40a34 remove gb specific move name 2024-10-10 16:24:21 +07:00
Xander Forrest 64ecec1a9a add Upper Hand name/description 2024-10-10 16:10:05 +07:00
Hiroku 7f8ef38b64 Merge branch 'clone' into 'main'
regen tethering ID as well as UUID in Pokemon.clone()

See merge request cable-mc/cobblemon!1189
2024-10-10 08:52:19 +00:00
Hiroku e4360ca9b5 Play animation sound keyframes through the entity where possible, the way Arceus intended 2024-10-10 08:47:09 +00:00
Xander Forrest bc27ec7288 remove tethering id not randomly generate 2024-10-10 15:44:41 +07:00
Xander Forrest 875f2d99ab regen tethering ID as well as UUID in Pokemon.clone() 2024-10-10 14:24:05 +07:00
Hiroku a4d18eba89 Merge branch 'sound' into 'main'
Sound bugfix and small volume tweaks

Closes #1069

See merge request cable-mc/cobblemon!1187
2024-10-07 06:36:58 +00:00
Sterrezo edffc248f4 Small volume tweaks to Pokedex sounds 2024-10-07 08:29:51 +02:00
Sterrezo 600bbfc5f3 Prevented the zoom sound from playing when unable to increase or decrease zoom further 2024-10-07 08:28:40 +02:00
JazzMcNade 4cfaf27ac7 Fixes 1035 2024-10-06 19:58:38 -05:00
Hiroku 692f1de510 Merge branch 'Updating-Structures-to-1.21' into 'main'
1.21.1 structure fixes

See merge request cable-mc/cobblemon!1184
2024-10-06 20:55:42 +00:00
Polymeta e230dd740d Merge branch 'refs/heads/main' into Updating-Structures-to-1.21
# Conflicts:
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/desert/houses.json
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/plains/houses.json
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/savanna/houses.json
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/snowy/houses.json
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/taiga/houses.json
2024-10-06 20:46:40 +01:00
Hiroku e24008c89e Merge branch 'keybind-shmebind' into 'main'
Remove PokedexBinding.kt and references.

See merge request cable-mc/cobblemon!1185
2024-10-06 02:48:08 +00:00
Hiroku ed29801820 Merge branch 'feature/pokedex-sync-fixes' into 'main'
fix pokedex sync and other issues

Closes #1000, #1041, and #1051

See merge request cable-mc/cobblemon!1186
2024-10-06 02:47:53 +00:00
Polymeta a5fe51b8fa various pokedex fixes
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-10-06 03:37:46 +01:00
Hiroku 600cc48073 Merge branch 'slated' into 'main'
Added Missing Recipe

Closes #1033

See merge request cable-mc/cobblemon!1180
2024-10-06 00:08:09 +00:00
JPAKx4 a4aa8f3778 Remove PokedexBinding.kt and references. 2024-10-05 14:38:21 -05:00
Slippy 2f16490aa8 Hopefully this fixed village centers from not having loot and generating very uncommonly 2024-10-04 23:06:11 -05:00
Hiroku ed82a72919 Merge branch 'unblank_descriptions' into 'main'
Fix scrolling issue in InfoTextScrollWidget.kt

Closes #1023

See merge request cable-mc/cobblemon!1182
2024-10-05 03:29:48 +00:00
JPAKx4 953163b12e Fix scrolling issue in InfoTextScrollWidget.kt 2024-10-04 22:20:26 -05:00
Test aa57cd7e3e prevent cry animation stacking on button click 2024-10-04 22:32:48 -04:00
dhi_holo 863baa5ae6 Added Missing Recipe
Resolves #1033
2024-09-30 18:34:32 +10:00
Hiroku 913ca3d467 Merge branch 'sound' into 'main'
Tweaked evolution sound logic

See merge request cable-mc/cobblemon!1179
2024-09-29 08:44:30 +00:00
Sterrezo 22b541af10 Tweaked evolution sound logic
Added a click sound to the evolve widget. Evolution ui sound now plays only when evolving from the ui, not with the Pokemon sent out.
2024-09-29 10:43:28 +02:00
Hiroku c1a706ca69 Merge branch 'scan-fixes' into 'main'
scan and dex fixes

See merge request cable-mc/cobblemon!1178
2024-09-29 02:42:23 +00:00
TestandHiroku 47f8647d48 scan and dex fixes 2024-09-29 02:42:22 +00:00
Hiroku a0e01b9652 Merge branch '#1013-immortal-pokemon' into 'main'
Fixes Pokemon being immortal after breaking free from capture

Closes #1013

See merge request cable-mc/cobblemon!1177
2024-09-28 04:23:40 +00:00
Deltric 9d88fb7ca2 Resets beam mode to 0 after a Pokemon is finished breaking free from capture. Prevents them from staying immortal. 2024-09-27 22:49:32 -05:00
Paul ca0170f1ed Several camera mode fixes, fixing several move effects. Closes #1010 2024-09-28 11:36:45 +10:00
Paul 050f7e6161 Added property on PokemonEntity to toggle pose type recalculation 2024-09-27 12:20:57 +10:00
Paul 658ec95e6b Fixed pressing E for a nickname closing the GUI, closes #1006 2024-09-27 11:28:57 +10:00
Hiroku 883f8149da Merge branch 'BattleBugStomping' into 'main'
BattleBugStomping Bugaloo

See merge request cable-mc/cobblemon!1167
2024-09-27 00:06:53 +00:00
Hiroku e5e4871c96 Merge branch 'feature/bugfixes' into 'main'
small fixes

Closes #1001 and #999

See merge request cable-mc/cobblemon!1176
2024-09-27 00:05:34 +00:00
JazzMcNade e074411596 Cleaned up Revival Blessing logic 2024-09-26 01:53:51 -05:00
JazzMcNade adf0ba7c9f Merge branch 'main' into BattleBugStomping 2024-09-26 01:22:11 -05:00
JazzMcNade 3d8c22fd68 Fixed client sync issues with triple battle's auto shift to center. 2024-09-26 00:18:03 -05:00
Polymeta a966f0f9a6 dont reset state with empty state causing the loss of aspects
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-25 23:32:01 +01:00
Polymeta da74990909 only rotate collection when leading pokemon is passed in to preserve original party order
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-25 22:43:00 +01:00
JazzMcNade 0e3102f5e4 Fixed a bug in which not having enough pokemon to fill all fainted slots would cause a soft lock. Fixed Revival Blessing. 2024-09-25 00:45:41 -05:00
Nick 36e3816bf3 Merge branch 'feature/pokemon-debug-utils' into 'main'
debug galore for pokemon

See merge request cable-mc/cobblemon!1175
2024-09-24 23:05:41 +00:00
PolymetaandNick 99e47e4cb7 debug galore for pokemon 2024-09-24 23:05:41 +00:00
Nick aa875b06ab Merge branch 'feature/form-dfu' into 'main'
introduce feature DFU to prevent loss of forms and other features when migrating from 1.20

Closes #993 and #541

See merge request cable-mc/cobblemon!1174
2024-09-24 17:24:29 +00:00
PolymetaandNick dfb08c8db0 introduce feature DFU to prevent loss of forms and other features when migrating from 1.20 2024-09-24 17:24:29 +00:00
Nick eefff036b7 Merge branch 'more-npcs' into 'main'
Many NPC improvements for foundation

See merge request cable-mc/cobblemon!1171
2024-09-23 23:02:00 +00:00
HirokuandNick 43050b4276 Many NPC improvements for foundation 2024-09-23 23:02:00 +00:00
Nick b1b8c6e9db Merge branch 'bugfix/fix-for-slippy' into 'main'
Fixed Jigsaw structure maximums

See merge request cable-mc/cobblemon!1173
2024-09-23 22:06:16 +00:00
Paul 583a973e53 Fixed Jigsaw structure maximums 2024-09-23 15:51:58 +10:00
Hiroku c0b945e3da Merge branch 'parchment' into 'main'
Added parchment mappings for param names

See merge request cable-mc/cobblemon!1172
2024-09-23 02:10:44 +00:00
apion fa73fb8e46 Added parchment mappings for param names 2024-09-22 22:00:49 -04:00
Hiroku 1fd9128b33 Merge branch 'DoubledPokeballSendOutParticlesFix' into 'main'
DoubledPokeballSendOutParticlesFix

See merge request cable-mc/cobblemon!1169
2024-09-23 00:34:43 +00:00
Hiroku f9799ef55e Merge branch 'feature/small-tweaks' into 'main'
remove redundant tracker and hardcode battle ui navigation to leftclick

Closes #992

See merge request cable-mc/cobblemon!1170
2024-09-22 21:25:15 +00:00
PolymetaandHiroku ab69c4f095 remove redundant tracker and hardcode battle ui navigation to leftclick 2024-09-22 21:25:15 +00:00
JazzMcNade a9a0fff643 PokemonClientDelagate now checks if beammode 1 in currently in progress before applying its effects. 2024-09-22 12:27:11 -05:00
Apion 9bdb8102c1 Merge branch 'bugfix/1-6-fixes' into 'main'
Dex variation definitions for many pokes, show all pose types in dex

See merge request cable-mc/cobblemon!1168
2024-09-21 23:01:33 +00:00
HirokuandApion febcb7092a Dex variation definitions for many pokes, show all pose types in dex 2024-09-21 23:01:33 +00:00
Apion 4ec8b01efb Merge branch 'feature/jarring-changes' into 'main'
JarInJar fabric kotlin instead of shadowing kotlin libs to avoid compatibility issues

Closes #933

See merge request cable-mc/cobblemon!1161
2024-09-21 22:44:42 +00:00
PolymetaandApion 95b15d50c3 JarInJar fabric kotlin instead of shadowing kotlin libs to avoid compatibility issues 2024-09-21 22:44:42 +00:00
JazzMcNade 88d7b38a90 Cleaned up math 2024-09-21 17:44:21 -05:00
Apion 471eef6c31 Merge branch 'SinglesSendoutAdjustments' into 'main'
SinglesSendoutAdjustments

See merge request cable-mc/cobblemon!1166
2024-09-21 22:40:15 +00:00
JazzMcNadeandApion 513910e448 SinglesSendoutAdjustments 2024-09-21 22:40:15 +00:00
JazzMcNade 528f1d68b9 Fixed bug in which a fainted pokemon could be sent out in multibattles. Fixed actor display test when the first slot is fainted. Fixed timings of gui items now that the BattleOverlay tick speed has been fixed. 2024-09-21 17:32:36 -05:00
apion 1613250f5d Add method for in PlatformRegistry for sidemod usage 2024-09-21 18:27:14 -04:00
Hiroku f8a2802a8e Merge branch 'feature/sneaky-label' into 'main'
Tweak pokemon label display and additional config setting

See merge request cable-mc/cobblemon!1164
2024-09-21 22:16:44 +00:00
PolymetaandHiroku fdcbca4410 Tweak pokemon label display and additional config setting 2024-09-21 22:16:44 +00:00
Hiroku 85b96268be Merge branch 'Uh-Oh-Slippy-is-Messing-with-Code-Again' into 'main'
Uh oh slippy is messing with code again

See merge request cable-mc/cobblemon!1162
2024-09-21 00:17:15 +00:00
Hiroku 0a94c00859 Merge branch 'bugfix/1-6-fixes' into 'main'
Fixes

Closes #973 and #989

See merge request cable-mc/cobblemon!1165
2024-09-21 00:16:55 +00:00
Paul b7a2091c3e Fixed battle overlay animations absolutely zooooming 2024-09-21 10:01:45 +10:00
Polymeta 2dcd9467ec fix compile errors
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-20 13:08:50 +01:00
Polymeta 4fc6d35aab Merge branch 'Myslippy-main-patch-44800' into 'Uh-Oh-Slippy-is-Messing-with-Code-Again'
Update CHANGELOG.md (I apologize for my sins hiro please forgive me)

See merge request cable-mc/cobblemon!1163
2024-09-20 11:41:48 +00:00
Paul 89d62606d3 Merge remote-tracking branch 'origin/main' into bugfix/1-6-fixes 2024-09-20 19:54:21 +10:00
Myslippy c424e3a8c5 Update CHANGELOG.md (I apologize for my sins hiro please forgive me) 2024-09-20 09:36:35 +00:00
Slippy 792a8795ed more adjustments 2024-09-20 04:20:48 -05:00
Slippy 05d3c6d928 updated village pokecenter structure nbt files 2024-09-20 04:01:33 -05:00
Slippy 3f56af4b86 updated village pokecenter structure nbt files 2024-09-20 03:50:25 -05:00
Apion fe0e32de76 Merge branch 'Whaet' into 'main'
Update treasure.json

See merge request cable-mc/cobblemon!1160
2024-09-20 06:43:35 +00:00
apion 7456afac58 Fix crash 2024-09-20 02:28:13 -04:00
dhi_holo ee07204bae Merge branch 'main' into Whaet 2024-09-20 15:18:50 +10:00
dhi_holo 252296bf71 Update treasure.json
Made it so you can't fish obscene numbers of Poké Rod Smithing Templates anymore, but you can still get more from higher enchanted rods
2024-09-20 15:18:09 +10:00
Apion 3c4da18040 Merge branch 'bugfix/1-6-fixes' into 'main'
1.6 fixes

Closes #991, #973, and #989

See merge request cable-mc/cobblemon!1159
2024-09-20 05:16:46 +00:00
HirokuandApion 88ff54b97b 1.6 fixes 2024-09-20 05:16:45 +00:00
Hiroku 1f1590a49f Merge branch 'sidemoddable-storage-types' into 'main'
Moved PlayerInstancedDataStoreType to not be an enum (so sidemods can add new ones)

See merge request cable-mc/cobblemon!1158
2024-09-20 05:14:36 +00:00
apion c9476efe6f Licenses (please have mercy) 2024-09-20 00:12:00 -04:00
apion ac77262c88 Moved PlayerInstancedDataStoreType to not be an enum (so sidemods can add new ones) 2024-09-20 00:05:21 -04:00
Paul ba649652da Fixed shiny property not syncing properly #991 2024-09-20 13:25:32 +10:00
Paul c63dff32a0 Fixed capture pokeballs in battle UI spinning forever, fixes #973 2024-09-20 12:51:56 +10:00
Paul c253683506 Made relic coin pouches waterloggable, fixes #989 2024-09-20 12:24:46 +10:00
Hiroku d7788e277f Merge branch 'dialogue' into 'main'
cleanup dialog

See merge request cable-mc/cobblemon!1152
2024-09-19 23:54:48 +00:00
TestandHiroku 44cd81eb24 cleanup dialog 2024-09-19 23:54:47 +00:00
Hiroku 0987afe645 Merge branch 'feature/post-port-trauma' into 'main'
1.21.1 port fixes and other things

Closes #941, #214, #959, #986, #978, #975, and #952

See merge request cable-mc/cobblemon!1155
2024-09-19 23:47:01 +00:00
Polymeta d407561978 add datafixer for pokemon model components
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-19 17:04:06 +01:00
Polymeta dfe6c19e01 also apply regenerator on force switches
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-19 16:43:55 +01:00
Polymeta 978e625ba2 fix regenrator ability HP desync
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-19 15:35:20 +01:00
Polymeta d401009dbb restore some "lost" default pokemon nbt so it can be targeted by selectors
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 23:53:55 +01:00
Polymeta 0d16619eb8 create BeastBallModel.kt and assign it so the beast ball gets correct throwing animation and small cleanup
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 23:10:16 +01:00
Polymeta fd8feca60b merge 2 item renderer mixin classes into one and refactor out equality method to use for both dex and pokeball
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:54 +01:00
Polymeta d13908a8e7 fix pasture pokemon dropping loot when owner is not "present"
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:14 +01:00
Polymeta 1aae6fcc57 PR feedback
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:14 +01:00
Polymeta 6331742f93 fix mixin doing instance based comparison into something a little more concrete (resource location) also fixed boat crash
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:13 +01:00
Polymeta a21d99c871 make the interaction wheel slightly bigger to prevent overlapping buttons
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:13 +01:00
Polymeta c8f230ee8f update multiblock to contain all 3 parts as valid blocks for block entity also fix shoulder mount showing pre-evo and make pokemon smarter on leash
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-18 21:45:08 +01:00
Nick 8cf6645cdd Merge branch 'dfu-fun' into 'main'
DataFixers for a bunch of stuff

See merge request cable-mc/cobblemon!1156
2024-09-18 17:36:37 +00:00
ApionandNick 2b4b8fe13a DataFixers for a bunch of stuff 2024-09-18 17:36:37 +00:00
Slippy 3fcfac1cf6 Bug Fixes for ruins and fossil structures 2024-09-16 23:01:40 -05:00
Hiroku c73bfe67bf Merge branch 'no_ai-and-freeze_frame' into 'main'
Added no_ai and freeze_frame props, respect noai=true, added /freezepokemon

See merge request cable-mc/cobblemon!1154
2024-09-16 00:08:10 +00:00
Paul 25bdef0510 Cleanup isNoAi references since it's not needed 2024-09-16 08:16:18 +10:00
Paul d0c9093f41 Added no_ai and freeze_frame props, respect noai=true, added /freezepokemon 2024-09-15 20:12:45 +10:00
Hiroku 98e38836c5 Merge branch 'implement_pokemon' into 'main'
Petilil animation fix

See merge request cable-mc/cobblemon!1140
2024-09-15 09:51:19 +00:00
Frank The FarmerandHiroku b3a3064e1b Petilil animation fix 2024-09-15 09:51:19 +00:00
Paul a8caaf4654 Added changelog for breaking changes, use 'maxHealth' instead of 'hp' with temporary backcompat 2024-09-15 16:31:28 +10:00
Apion 232e0f478a Merge branch 'fix/storage-registry-support' into 'main'
Fixed NBT on pokemon held items not serializing, updated to 1.21.1

See merge request cable-mc/cobblemon!1153
2024-09-15 06:17:18 +00:00
Hiroku c039bf7f20 Merge branch 'another-minor-tag-push' into 'main'
Tag updates

See merge request cable-mc/cobblemon!1151
2024-09-15 03:36:59 +00:00
Paul 88d6c17c90 Fixed NBT on pokemon held items not serializing, updated to 1.21.1 2024-09-15 13:26:33 +10:00
dhi_holo 4e76871046 Tag updates
Forgot to add some blocks to some tags, so now that's fixed :)
2024-09-15 12:30:29 +10:00
Hiroku 598d016172 Merge branch 'feature/turn-off-dex-with-hotbar' into 'main'
ensure that when using hotbar, the scanning gui disappears

Closes #981

See merge request cable-mc/cobblemon!1150
2024-09-15 02:09:41 +00:00
PolymetaandHiroku 0c5baec179 ensure that when using hotbar, the scanning gui disappears 2024-09-15 02:09:41 +00:00
Hiroku 4729d4204c Merge branch 'more-json-fixes' into 'main'
Flash Fire Lang Addition

See merge request cable-mc/cobblemon!1149
2024-09-14 23:44:38 +00:00
dhi_holo 1a07b21b8d Merge branch 'main' into more-json-fixes 2024-09-15 09:17:32 +10:00
Hiroku b10bee6a3e Merge branch 'feature/fix-pipeline' into 'main'
make sure to deploy maven artefacts from main

See merge request cable-mc/cobblemon!1148
2024-09-14 23:01:00 +00:00
PolymetaandHiroku 44889a6c7d make sure to deploy maven artefacts from main 2024-09-14 23:01:00 +00:00
Hiroku 705eeae700 Merge branch 'BattleTargetSelectionRevision' into 'main'
Battle target selection revision / player interaction options consistency

See merge request cable-mc/cobblemon!1144
2024-09-14 22:11:00 +00:00
Hiroku a2122c7b24 Merge branch 'feature/advanced-advancements!' into 'main'
fix interaction advancement json files and ensure resurrection advancement is triggered by machine

Closes #985 and #957

See merge request cable-mc/cobblemon!1147
2024-09-14 22:10:19 +00:00
PolymetaandHiroku 940ed1450d fix interaction advancement json files and ensure resurrection advancement is triggered by machine 2024-09-14 22:10:18 +00:00
Hiroku 265ecd3749 Merge branch 'sound_tracker' into 'main'
Client sound tracking for entities & casting sound preemption

See merge request cable-mc/cobblemon!1119
2024-09-14 22:09:08 +00:00
Polymeta 561ded8747 merge artefacts...
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-14 22:27:31 +01:00
Hiroku 4e695cf020 Merge branch 'Corrected-natural-tag' into 'main'
Paul is fired for breaking the spawns!

See merge request cable-mc/cobblemon!1146
2024-09-14 21:06:07 +00:00
Segfault GuyandPolymeta f9288efb31 i forgot to add licenses because i dont care 2024-09-14 22:00:52 +01:00
Segfault GuyandPolymeta f69ffe9d99 Preemptively discarding bobber entity stops cast sound. 2024-09-14 22:00:52 +01:00
Segfault GuyandPolymeta 6ec81e6af2 Implement SoundTracker for active client sounds + fix rod casting sounds. 2024-09-14 22:00:43 +01:00
FrankTheFarmer c6baf6eafb Paul is fired for breaking the spawns! 2024-09-14 21:23:23 +10:00
dhi_holo 31fc59236f Lang addition and failed advancement fix
Issue #985 is relevant to some of the changes
2024-09-14 20:41:28 +10:00
JazzMcNade c4fa18ef41 Merge branch 'main' into BattleTargetSelectionRevision 2024-09-14 01:35:02 -05:00
Hiroku 2d34b7446e Merge branch 'feature/poke-rod-crash' into 'main'
fix pokerods crashing servers when being cast

See merge request cable-mc/cobblemon!1145
2024-09-14 05:52:07 +00:00
Paul eee994831c Merge remote-tracking branch 'origin/main' 2024-09-14 15:09:21 +10:00
Paul 72768ed1ac Changelog cleanup 2024-09-14 15:09:10 +10:00
Hiroku 635279c076 Merge branch 'sculk_sensor' into 'main'
Made PC and Pasture click the Sculk sensor

See merge request cable-mc/cobblemon!1137
2024-09-14 05:08:52 +00:00
Hiroku ad9f758b2f Merge branch 'feature/pokemon-label-name-config' into 'main'
Pokemon label is working again and new config setting

See merge request cable-mc/cobblemon!1142
2024-09-14 05:03:41 +00:00
PolymetaandHiroku 6a804792e3 Pokemon label is working again and new config setting 2024-09-14 05:03:41 +00:00
Hiroku 2104bf28b1 Merge branch 'UI_close_like_minecraft' into 'main'
Ui close like minecraft

See merge request cable-mc/cobblemon!1127
2024-09-14 05:00:34 +00:00
JazzMcNade 2300581493 Merge branch 'main' into BattleTargetSelectionRevision 2024-09-13 20:14:42 -05:00
Paul 7ce6fb065a Merge remote-tracking branch 'origin/main' 2024-09-14 10:27:21 +10:00
Paul dcea385b66 Created a #cobblemon:natural tag so spawns are faster 2024-09-14 10:24:41 +10:00
Polymeta d680e998ce rip out soundinstance from bobber as it was clientOnly and therefore not working properly anyways - also tidy up
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2024-09-13 17:16:40 +01:00
Hiroku 23ea4c6474 Merge branch 'feature/primary-click-batte-ui' into 'main'
use primary click only when navigating battle UI

Closes #954

See merge request cable-mc/cobblemon!1141
2024-09-13 05:56:04 +00:00
PolymetaandHiroku abf106ce94 use primary click only when navigating battle UI 2024-09-13 05:56:04 +00:00
JazzMcNade 1b3343589f Updated Triples Shift Button 2024-09-13 00:55:23 -05:00
Hiroku 96c6af8d59 Merge branch 'where-are-they' into 'main'
Recipes and Loot Tables

See merge request cable-mc/cobblemon!1143
2024-09-13 05:10:05 +00:00
Hiroku 82bbcfad26 Merge branch 'career-development' into 'main'
Decrease unemployment rate among villagers

See merge request cable-mc/cobblemon!1138
2024-09-13 05:09:18 +00:00
TestandHiroku 70e55626a0 Decrease unemployment rate among villagers 2024-09-13 05:09:17 +00:00
dhi_holo 107665c415 Merge branch 'main' into where-are-they 2024-09-13 12:04:36 +10:00
dhi_holo c8dd002e4a Some Tag Additions
Just a few new standard namespace tag additions for our items, and fixed an error with the poke rods tag
2024-09-13 12:04:20 +10:00
Paul fd2d06458b Merge remote-tracking branch 'origin/main' 2024-09-13 11:26:58 +10:00
Paul 29cb1276eb Clear calculated values on the client when data changes in pokedex 2024-09-13 11:26:40 +10:00
dhi_holo 0e0f6cee27 Merge branch 'main' into where-are-they 2024-09-13 10:21:12 +10:00
Apion 9c3a0704b9 Merge branch 'feature/bug-squashing' into 'main'
various smaller bugfixes

Closes #969, #970, #971, #972, and #967

See merge request cable-mc/cobblemon!1135
2024-09-12 23:28:49 +00:00
PolymetaandApion e9db3fc70c various smaller bugfixes 2024-09-12 23:28:49 +00:00
Slippy a41c3cb805 Updated village loot table 2024-09-12 13:46:51 -05:00
Slippy dd9fc85100 Updated structure files of village pokecenters 2024-09-12 03:42:24 -05:00
Slippy 00483ef54c Adjusted Decorated Pot Loot Table 2024-09-12 01:59:21 -05:00
dhi_holo 03210f0465 More Loot Table Fixes
Apricorn Leaves cleaned up, Medicinal Leek cleaned up, Tumblestone Clusters, Buddling Tumblestones, and basic Tumblestone Block fixed to behave properly.
2024-09-12 16:39:33 +10:00
JazzMcNade 26dc76a4f3 Fixed inconsistent ordering of options in PlayerInteractOptionsPacket 2024-09-12 01:35:26 -05:00
dhi_holo ae5e63aac9 Gilded Chest Update
Updated Gilded Chest's loot tables to use updated component data formatting, however this didn't fix them not dropping. Will need further investigation.
2024-09-12 15:53:12 +10:00
dhi_holo 08c8dd0f99 Added Missing Recipes
with tags and advancements related to the missing recipes too.
2024-09-12 14:18:51 +10:00
JazzMcNade 2044862edd Implemented tile based target selection, removed battle portrait target selection 2024-09-11 20:18:51 -05:00
apion 148b3c4271 Fixes #977 (Sherds not rendering) 2024-09-11 20:37:38 -04:00
Slippy 870cd0ec52 Updated structure files in taiga village and snowy village 2024-09-11 12:41:59 -05:00
Hiroku ef58d38c12 Merge branch 'loot-oh' into 'main'
Loot Table Fixes

See merge request cable-mc/cobblemon!1139
2024-09-11 10:10:14 +00:00
dhi_holo 7250b1a83c Loot Table Fixes
Ores now drop stuff correctly again, and Relic Coin Sacks (due to having an unused Natural blockstate) now can drop 20-64 relic coins when broken with the Natural blockstate set to true
2024-09-11 17:22:09 +10:00
Erusel 710b3f0303 Even better, centralised function 2024-09-11 02:32:01 +02:00
Erusel 57cf6f09fd Thanks to Hiroku's advice, it's more graceful. 2024-09-11 02:06:12 +02:00
Hiroku ec6820fa9d Merge branch 'holographic-cleanups' into 'main'
Holographic Cleanups

Closes #964

See merge request cable-mc/cobblemon!1136
2024-09-10 20:38:37 +00:00
Slippy bf4b211c4f removed "adapt noise" field. removed forest gate template pool. removed "start_height" field from structure sets. removed birch tree structures from oak tree template pool. 2024-09-10 06:25:07 -05:00
Slippy 3a2accb239 Ruin structures have been given loot tables for decorated pots 2024-09-10 02:46:00 -05:00
dhi_holo 6508d39e22 Merge branch 'main' into holographic-cleanups 2024-09-10 11:35:29 +10:00
Hiroku d0c7862103 Merge branch 'party-control-tweaks' into 'main'
Tweaks and fixes of sendout and other R key functions

See merge request cable-mc/cobblemon!1113
2024-09-09 02:47:52 +00:00
Paul f12962fd24 Changelog cleanup 2024-09-09 12:38:23 +10:00
Paul 9c7db81e22 Merge remote-tracking branch 'origin/main' into party-control-tweaks 2024-09-09 12:18:28 +10:00
Hiroku 6be1caf41a Merge branch 'sound' into 'main'
Fixed sound stuff lost in merges

See merge request cable-mc/cobblemon!1133
2024-09-09 02:17:55 +00:00
Hiroku d582507a6e Merge branch 'implement_pokemon' into 'main'
Some rather important animation fixes

See merge request cable-mc/cobblemon!1134
2024-09-09 02:17:13 +00:00
Frank The FarmerandHiroku 302c0e6fe7 Some rather important animation fixes 2024-09-09 02:17:13 +00:00
Erusel 97761ba1e3 Made PC and Pasture click the Sculk sensor 2024-09-09 00:10:04 +02:00
Slippy a75b578954 Updated Ruin Structure template_pool files to contain the new "override_liquid_settings" field. 2024-09-07 23:43:24 -05:00
dhi_holo 44ade828fa Update en_us.json
Updated Smithing Templates to the 1.21.2 style (because it's so much more usable, and means less updating work later)
Improved names of Gone Fishin' and A True Antiquarian advancements
2024-09-08 11:05:45 +10:00
dhi_holo 4c1a5f12b7 Merge branch 'main' into holographic-cleanups 2024-09-08 10:51:06 +10:00
Hiroku bbe7f96332 Merge branch 'BallBuffs' into 'main'
Ball buffs

See merge request cable-mc/cobblemon!1131
2024-09-08 00:37:11 +00:00
Hiroku a29a7ebbca Merge branch 'feature/resolve-some-todos' into 'main'
now that pokedex is present, resolve some todos that required on it, also use OTs for exp calc

Closes #966

See merge request cable-mc/cobblemon!1132
2024-09-08 00:18:10 +00:00
PolymetaandHiroku f4a7cc92ee now that pokedex is present, resolve some todos that required on it, also use OTs for exp calc 2024-09-08 00:18:10 +00:00
Sterrezo 9768711c5e Evolution sound is working now! 2024-09-08 01:56:54 +02:00
Sterrezo 4b3a864b28 Nothing is working
I fixed some missing sounds that were lost in merges. Added a placeholder animation to Hisui bias Petilil. Removed an empty sound event.
2024-09-08 01:19:06 +02:00
Slippy 60f057d28e Fossil Template Pools updated to contain "apply_waterlogging" field 2024-09-07 18:11:54 -05:00
Slippy 3c4219b069 update village template_pools to include 1.21 "override_liquid_settings" field 2024-09-07 17:12:11 -05:00
JazzMcNade 0db6da951e Updated tooltip strings for dusk ball. 2024-09-07 16:44:54 -05:00
JazzMcNade 3963537e40 Buffed Dusk Ball and Repeat Ball catch rate 2024-09-07 16:38:57 -05:00
Licious dd5e578d3a Merge branch 'ImplementRepeatBall' into 'main'
Implemented Repeat Ball's catch modifier

See merge request cable-mc/cobblemon!1130
2024-09-07 20:13:37 +00:00
JazzMcNade 4dab4b7ef6 Implemented Repeat Ball's catch modifier 2024-09-07 14:44:20 -05:00
dhi_holo 93389ae98b Merge branch 'main' into holographic-cleanups 2024-09-07 18:14:41 +10:00
dhi_holo cb1dba64c0 missed a bit
one day I'll do this properly from stage 0

This is funnier though
2024-09-07 18:14:05 +10:00
Slippy 2d01e7a482 Village Pokecenters 2024-09-07 02:12:20 -05:00
dhi_holo 88f285199c added a thing for the rod recipe advancements
just makes it less spammy
2024-09-07 17:10:40 +10:00
dhi_holo e17bd10660 Automaton Fixes
Fixed Automaton Armour Trim being called that (instead of Armor because it's en_us), and missing the item name entirely
2024-09-07 17:00:06 +10:00
dhi_holo 84feb449d9 Rodvancements
+Added Recipe Advancements for all of the Poké Rod Smithing Template stuff
+Unhid the Candy advancement, as there's now enough ways to get them that it doesn't need to be hidden
-Removed the Poké Rod Smithing Template from the Archaeology tab, as it's unfitting and is located in another tab where it does fit as well.
2024-09-07 16:44:06 +10:00
dhi_holo 682abbdd43 Green Dye Switch
For the sake of sanity
2024-09-07 16:11:23 +10:00
dhi_holo f8e68d2217 Updated Protector Recipe
Now cleaner and cooler
2024-09-07 14:51:31 +10:00
dhi_holo e8ddabe3ac Merge branch 'main' into holographic-cleanups 2024-09-07 14:33:35 +10:00
dhi_holo 32dd49b669 Update display_case.json
Fixes z-fighting, resolving #964
2024-09-07 14:30:54 +10:00
dhi_holo d10ba586c8 To Undo Later
Just making it so the game doesn't have errors from the bug with the species not loading before the advancements, will undo this change once we can
(also corrected an error I put in the lang)
2024-09-07 13:09:25 +10:00
dhi_holo 6409b75aef Merge branch 'main' into holographic-cleanups 2024-09-07 12:34:15 +10:00
Myslippy 564b5ae413 Update StructurePoolGeneratorMixin.java 2024-09-06 20:51:40 +00:00
Myslippy a8c93146da Update StructurePoolGeneratorMixin 2024-09-06 20:08:37 +00:00
Erusel 4ab4f27690 fixed trade gui not closing 2024-09-06 14:39:50 +02:00
Erusel 5aaf3aaa8a fixed doomed key pressed method not being called and screens not closing 2024-09-06 14:38:18 +02:00
BuildTools 73ba27a148 fixed imports 2024-09-06 13:55:44 +02:00
BuildTools 7493ac80c4 changing thing for making it closing at the inventory key and not "e" 2024-09-06 13:55:05 +02:00
BuildTools 5d4c4a2d9d added minecraft like closing on PC 2024-09-06 13:24:03 +02:00
BuildTools dad87454cf added minecraft like closing on Pokenav 2024-09-06 13:23:18 +02:00
BuildTools fbcbdd0d1f added minecraft like closing on Starter Selection 2024-09-06 13:21:16 +02:00
BuildTools 37ac4837b2 added minecraft like closing on Trade GUI 2024-09-06 13:20:21 +02:00
BuildTools 8cae97b297 added minecraft like closing on Summary 2024-09-06 13:17:49 +02:00
dhi_holo 25ad6e96f4 Not Yet Fixed Because Grr
The component data for these is pretty much solved, except for one major issue:
It totally stops the advancements loading in game for some reason. Doesn't like the species ID effectively. If anyone wants to take a crack at it between now and whenever it's solved, plz
2024-09-03 16:26:37 +10:00
dhi_holo 8efd658a93 Fix Batch 1
Added translation strings for the two new gamerules and 6 new tags (even if one of them isn't actually in this branch)
Fixed two advancements having incorrect icons (due to switch from nbt to item components)
2024-09-02 18:22:07 +10:00
Sylvonir b42b561e49 Oops random extra variable 2024-08-09 21:38:44 -07:00
Sylvonir 68639390f5 Merge remote-tracking branch 'origin/party-control-tweaks' into party-control-tweaks 2024-08-09 21:38:17 -07:00
Sylvonir 226fefc08a Hopefully match party scroll behaviour with vanilla logic 2024-08-09 21:38:07 -07:00
Sylvonir c8fb04e0ea Hopefully match party scroll behaviour with vanilla logic 2024-08-09 21:35:38 -07:00
Sylvonir f4aa67390a Merge remote-tracking branch 'origin/party-control-tweaks' into party-control-tweaks 2024-08-09 21:30:38 -07:00
Sylvonir 1530f4d09a Update CHANGELOG.md 2024-08-09 15:22:26 -07:00
Sylvonir 2a64c405f9 Update CHANGELOG.md 2024-08-09 15:12:51 -07:00
Sylvonir d5a22165c6 Fix battle log GUI flashing when BattleGUI is opened 2024-08-09 15:00:40 -07:00
Sylvonir a16ae6665a Fix party scroll not overriding vanilla scroll with high scroll speeds 2024-08-09 13:55:01 -07:00
Sylvonir 12595dde53 Let Pokémon be invulnerable during sendout and intangible during recall 2024-08-09 13:33:15 -07:00
Sylvonir 68299d2b57 Tweak recall timing and allow it to cancel sendout 2024-08-09 13:02:13 -07:00
986 changed files with 111504 additions and 101145 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ publish:
- neoforge/build/libs/*.jar
- fabric/build/libs/*.jar
rules:
- if: '$CI_COMMIT_BRANCH == "sega_bass_pro_fishing_2"'
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
+6
View File
@@ -0,0 +1,6 @@
### Additions
- Added `moves` option to PokemonProperties, allowing you to set the moves of a Pokémon in commands and spawn files.
- Added /spawnnpc and /spawnnpcat commands.
### Fixes
- Fixed sync issues with recently learned moves where their PP would not go down until you logout and in.
+168 -150
View File
@@ -1,75 +1,36 @@
# Changelog
## [1.6.0 - The Bass Pro Fishing Update (Month Xth, 2024)](#1-6-0)
#### "Now that there's a fishing mechanic, the mod is actually good!"
### Fixes
- Fixed trading Pokémon setting their friendsip to 0 instead of the base value.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
- Fixed PokemonProperty argument suggestions for ``tera`` and ``tera_type`` not suggesting ``stellar``.
- Fixed being able to stack Relic Coin Pouches on top of each other.
- Bag Items used during a turn will now be refunded if the battle ends before the next turn begins.
### Additions
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
- Added Repeat Ball functionality, increasing the catch rate of Pokémon that are already registered as caught in a player's Pokédex.
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
- Added ``aspect`` and ``unaspect`` PokemonProperty arguments (which also includes commands such as /pokemonedit, /spawnpokemon, and /givepokemon) to allow forcing or un-forcing an aspect on a Pokémon.
- Added ``type`` alternatively ``elemental_type`` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example ``type=fire`` would be true for Charmander but false for Squirtle.
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
- Added support for Double Battles, Triple Battles, and Multi-battles.
- Adjusted Pokémon sendout positions for single battles.
- Wild Pokémon interaction range increased to 12 blocks (from 10 blocks)
- Player trade range increased to 12 blocks (from 10 blocks)
- PvP battle range increased to 32 blocks (from 10 blocks)
- Spectate range increased to 64 blocks (from 10 blocks)
### Developer
- SpawnCause is now an implementation of SpawningInfluence.
- Added "forcedAspects" to Pokemon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the Pokemon.aspects setter private. This could technically break sidemods but if you are affected by this then you were using it wrong! Use Pokemon.forcedAspects to fix it.
- Many types related to ``Pokemon`` including itself now have dedicated ``Codec``, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- ``TeraTypes`` now implements ``Iterable``.
## [1.5.3](#1-5-3)
### Fixes
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
- Fixed Cubone's cry not having a sound.
- Fixed the Seafloor spawning context not being a usable context.
- Fixed Pokemon spawning in non-full blocks like slabs.
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
- Hidden Power no longer plays the water type action effect. (It now plays the normal type action effect.)
- Fixed Chimchar and Monferno T posing whenever they sleep.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
- Fixed Bellossom clipping into the player head when shoulder mounted.
- Fixed Shroomish's look range to prevent it from looking higher than it should.
- Fixed Crumbling Arch not blending in with the world.
- Fixed Energy Root applying being usable on a fainted Pokemon.
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon
- Fixed species comparison not using namespace for both sides in PokemonProperties.isSubsetOf.
- Fixed PokemonProperties#asString prefixing nicknames with a '$'
- Fixed PC not saving when Pokemon was changed outside.
- Fixed shearble Pokemon not dropping correct wool colors.
- Fixed pasture spawning Pokemon inside solid blocks when closest spawning position is blocked off.
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server
- Fixed empty JsonPlayerData files resulting in players being unable to join server/world
### Additions
- Added extra visuals to early berry growth stages
- CriticalCaptures and Pokédex progress capture multiplier now work with the Pokédex.
- Added extra visuals to early berry growth stages.
- Hidden Power now displays its effective typing.
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone Storage Blocks.
- Added Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone storage blocks.
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
- Moves learned via Sketch now persist after battle.
- Added ston_henge_ruins, luna_henge_ruins, and sol_henge_ruins structures.
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle
- Added battle log messages for switching out Pokémon
- Added Evolution particles and sounds for Pokemon that are sent out when evolution is started.
- Added `stonjourner_henge_ruins`, `luna_henge_ruins`, and `sol_henge_ruins` structures.
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle.
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle.
- Added battle log messages for switching out Pokémon.
- Added Evolution particles and sounds for Pokémon that are sent out when evolution is started.
- Added a Nurse profession that can be unlocked by having villagers claim a healing machine block.
- Added pokecenters to all 5 village types.
- Added a `natural` block state property for the healing machine block; when property is set to true, the block will have a different texture and drop an iron ingot instead of itself.
- New config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label
- `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
- Added shiny Pokémon particles with sound effects.
### Pokémon Added
#### Gen 2
@@ -83,6 +44,9 @@
#### Gen 3
- Wynaut
#### Gen 8
- Cramorant
#### Gen 9
- Farigiraf
@@ -91,71 +55,125 @@
- Golett, Golurk
### Changes
- Buffed Dusk Ball catch multipliers: Increased multiplier to 3.5 (from 3.0) in light level 0. Increased multiplier to 3.0 (from 1.5) in light levels 1-7.
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
- Adjusted some berry balance values like yield and growth times
- Adjusted some berry balance values like yield and growth times.
- Adjusted volumes of sounds made by Display Cases, Berry Bushes, Energy Root, Medicinal Leek, Vivichoke, Mints, Revival Herbs and Gilded Chests.
- Changes to mulch buffs/durations
- Dreepy is now shoulder mountable
- Link Cable recipe has been redesigned
- Updated potion sprites
- Weedle, Caterpie, Spearow, Smoochum, Mew, Murkrow, Larvitar, Taillow, Plusle, Minun, Beldum, Starly, Buneary, Combee, Pachirisu, Pidove, Petilil, Hisui Bias Petilil, Zorua, Hisuian Zorua, Elgyem, Fletchling, Skrelp, Klefki, Fomantis, Morelull, Dreepy, Shroodle, Tatsugiri, Glimmet, and Roaming Gimmighoul are now shoulder mountable
- Chikorita's ability to look around removed due to the awkwardness of it.
- Link Cable recipe has been redesigned
- Updated potion sprites
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs
- Updated UI sounds for clicking and evolving Pokémon
- Changes to mulch buffs/durations.
- Dreepy is now shoulder mountable.
- Updated potion sprites.
- Weedle, Caterpie, Spearow, Smoochum, Mew, Murkrow, Larvitar, Taillow, Plusle, Minun, Beldum, Starly, Buneary, Combee, Pachirisu, Pidove, Petilil, Hisui Bias Petilil, Zorua, Hisuian Zorua, Elgyem, Fletchling, Skrelp, Klefki, Fomantis, Morelull, Dreepy, Shroodle, Tatsugiri, Glimmet, and Roaming Gimmighoul are now shoulder mountable.
- Link Cable recipe has been redesigned.
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs.
- Updated UI sounds for clicking and evolving Pokémon.
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
- Kakuna, Beedrill, Snorlax, Munchlax, and Lurantis received model updates
- Kakuna, Beedrill, Snorlax, Munchlax, Krabby, Kingler, and Lurantis received model updates.
- Vivillon wings will stop being clear when you remove any Vivillon related resourcepacks. The default pattern will be the meadow wings.
- Adjusted Pokémon sendout positions for single battles.
- Wild Pokémon interaction range increased to 12 blocks (from 10 blocks).
- Player trade range increased to 12 blocks (from 10 blocks).
- PvP battle range increased to 32 blocks (from 10 blocks).
- Spectate range increased to 64 blocks (from 10 blocks).
- The inventory key now closes our GUIs if they are open, mirroring how Minecraft screens usually work.
- The noise from the pasture and PC blocks being interacted with now activates Sculk sensors.
- Updated dialogue GUI assets with proper assets.
- Updated party switching interface when in battle.
- Relic coin pouches can now be waterlogged.
- Pokemon Name (and level) labels are now only visible when crouching by default, set the `displayEntityLabelsWhenCrouchingOnly` to `false` to always them even when not crouching
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4.
### Fixes
- Fixed trading Pokémon setting their friendsip to 0 instead of the base value.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
- Fixed PokemonProperty argument suggestions for ``tera`` and ``tera_type`` not suggesting ``stellar``.
- Fixed being able to stack Relic Coin Pouches on top of each other.
- Fixed Model loader generating misleading crash-reports when client is crashing
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
- Big Roots and Energy Roots now share the same sounds as intented.
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating.
- Fixed Cubone's cry not having a sound.
- Fixed sounds made by blocks playing at a lower pitch than intended.
- Fixed the Seafloor spawning context not being a usable context.
- Fixed Pokemon spawning in non-full blocks like slabs.
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
- Hidden Power no longer plays the water type action effect. (It now plays the normal type action effect.)
- Hidden Power no longer plays the water type action effect (It now plays the normal type action effect).
- Fixed Chimchar and Monferno T-posing whenever they sleep.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
- Fixed Bellossom clipping into the player head when shoulder mounted.
- Fixed Shroomish's look range to prevent it from looking higher than it should.
- Fixed Crumbling Arch not blending in with the world.
- Fixed Energy Root applying being usable on a fainted Pokémon.
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon.
- Fixed species comparison not using namespace for both sides in `PokemonProperties.isSubsetOf`.
- Fixed `PokemonProperties#asString` prefixing nicknames with a '$'.
- Fixed PC not saving when Pokémon was changed outside.
- Fixed shearable Pokémon not dropping correct wool colors.
- Fixed pasture spawning Pokémon inside solid blocks when closest spawning position is blocked off.
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
- Improve error handling when loading spawn sets to ensure invalid configurations don't crash the server.
- Fixed empty `JsonPlayerData` files resulting in players being unable to join server/world.
- Evolution notification and UI sound is now correctly playing (again).
- Fix Starter prompt not showing up.
- Fix evolution now granting Pokédex entries.
- Fix Pokédex crash when a form is looked up without ever seeing the "normal" form.
- Fixed owned poké balls floating upwards if pausing mid-transition.
- Fixed all mouse buttons working for Battle UI navigation (now it's only primary/left click).
- Fixed trading Pokémon setting their friendship to 0 instead of the base value.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
- Fixed PokemonProperty argument suggestions for `tera` and `tera_type` not suggesting `stellar`.
- Fixed being able to stack Relic Coin Pouches on top of each other.
- Fixed Model loader generating misleading crash-reports when client is crashing.
- Big Roots and Energy Roots now share the same sounds as intended.
- Fixed sounds made by blocks playing at a lower pitch than intended.
- Fixed Chimchar and Monferno T posing whenever they sleep.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
- Fixed Bellossom clipping into the player head when shoulder mounted.
- Fixed Shroomish's look range to prevent it from looking higher than it should.
- Fixed Crumbling Arch not blending in with the world.
- Fixed Energy Root applying being usable on a fainted Pokemon.
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon
- Fixed species comparison not using namespace for both sides in PokemonProperties.isSubsetOf.
- Fixed PokemonProperties#asString prefixing nicknames with a '$'
- Fixed reviving items throwing an exception when used in battle
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive
- Fixed PC not saving when Pokemon was changed outside.
- Fixed shearble Pokemon not dropping correct wool colors.
- Fixed pasture spawning Pokemon inside solid blocks when closest spawning position is blocked off.
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server
- Fixed empty JsonPlayerData files resulting in players being unable to join server/world
- Fixed crumbling_arch_ruins from generating a giant cube of air and removed the chest
- Fixed Maushold's faint animations not playing. It's sadder than Tandemaus!
- Fixed Slowking's battle idle.
- Fixed Grafaiai's walk speed to prevent model sliding.
- Fixed reviving items throwing an exception when used in battle.
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
- Fixed `crumbling_arch_ruins` from generating a giant cube of air and removed the chest.
- Fixed possible crash on large population servers due to concurrent access of data.
- Quickly sending out and recalling Pokémon now looks smoother and can no longer be spammed to cause desync issues.
- Pokémon are now invulnerable during sendout animation and intangible during recall animation.
- Pokémon are now invincible during sendout animation and intangible during recall animation.
- Scrolling with party keybinding now behaves properly when using high scroll speed or scroll sensitivity.
- Fixed battle log GUI flashing when Battle GUI is opened.
- Bag Items used during a turn will now be refunded if the battle ends before the next turn begins.
- Pokémon whose current owner is not its Original Trainer now gains extra experience.
- Fixed Fossil Resurrection advancement not being granted.
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village!
- Fixed the Ice Face ability activation being displayed incorrectly in a battle
### Developer
- `SpawnCause` is now an implementation of `SpawningInfluence`.
- Many types related to `Pokemon` including itself now have dedicated `Codec`, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- `TeraTypes` now implements `Iterable`.
- `PokemonBattle` now starts on turn 0.
- SpawnCause is now an implementation of SpawningInfluence.
- Added "forcedAspects" to Pokemon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the Pokemon.aspects setter private. This could technically break sidemods but if you are affected by this then you were using it wrong! Use Pokemon.forcedAspects to fix it.
- Renamed Pokemon.hp to Pokemon.maxHealth to make it clearer. Backwards compatibility is provided but it is an active deprecation, please migrate away from it.
- Many types related to ``Pokemon`` including itself now have dedicated ``Codec``, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- Serialization of PokemonStores and Pokemon themselves now require registry access, which will break some sidemods. This sucked for us more than it will suck for you! If you have a world or player instance, you can get it from there.
- ``TeraTypes`` now implements ``Iterable``.
- PokemonBattle now starts on turn 0.
- All sounds related to evolving Pokémon have been moved to the "sounds/evolution" folder.
- Added `forcedAspects` to Pokémon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the `Pokemon.aspects` setter private. This could technically break side-mods but if you are affected by this then you were using it wrong! Use `Pokemon.forcedAspects` to fix it.
- Cobblemon's main logger is properly static now.
- PokemonEntity.enablePoseTypeRecalculation can be used to disable automatic pose type recalculation.
### Data Pack & Resource Pack Creators
- Added support for "shedders" similar to Shedinja's evolution logic.
- Fixed the placeholder WingFlapIdle animation so the wings are not rotating opposite to each other.
- Fixed the placeholder `WingFlapIdle` animation so the wings are not rotating opposite to each other.
- 'player' type dialogue faces can now be explicitly stated so that NPC mods that use fake players can show in dialogue portraits.
- Added `isLeftSide` field for dialogue faces. This determines what side of dialogue box the portrait is on.
### Localization
- Updated translations for:
- Czech, German, Greek, Spanish, Mexican Spanish, French, Canadian French, Hungarian, Italian, Japanese, Korean, Dutch, Polish, Portuguese, Brazilian Portuguese, Russian, Ukrainian, Simplified Chinese, and Traditional Chinese
- Migrated translation project to Weblate at https://lang.cobblemon.com/projects/cobblemon/mod/
## [1.5.2 (May 27th, 2024)](#1-5-2)
### Fixes
@@ -254,13 +272,13 @@
- Added parametric motion and rotation support to particle effects.
- Added entity_scale as a molang var for particles (likely only applicable to Pokemon)
- Added support for primary quirk animations using the following format:
```JSON
`JSON
{
"quirks": [
"q.bedrock_primary_quirk('<pokemon>', '<animation>', <minSeconds>, <maxSeconds>, <loopTimes>, '<excludedLabels>', q.curve('<waveFunction>'))"
]
}
```
`
- Added support for custom stashes, similar to Gimmighoul's coin and scrap stashes.
### Localization
@@ -566,7 +584,7 @@ These are our compromise for having both regular and Hisuian forms of these Pok
- Fixed mod incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
- Fixed mod incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
- Fixed an issue that allowed Pokémon to spawn on rails.
- Fixed the data set on Pokémon when abilities are given via properties such as in ``/spawnpokemon``.
- Fixed the data set on Pokémon when abilities are given via properties such as in `/spawnpokemon`.
- Fixed Pokémon sometimes losing hidden abilities when evolving. This fix will only take effect for newly created Pokémon because of technical reasons that were explained to me but which I then forgot.
- Fixed capture messages not displaying in battle.
- Fixed the Illusion ability causing the wrong Pokémon to be targeted in battle.
@@ -579,17 +597,17 @@ These are our compromise for having both regular and Hisuian forms of these Pok
### Developer
- Significantly changed the way the properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed for some things).
- Rebuilt the scheduling API to more clearly force side choices and temporal frames of reference for tasks. The developers say that how it works is cool but I stopped listening.
- Added dialogue API and data registry. This is powerful, and there is a full example in the Cobblemon mod you can trigger using /opendialogue \<username\> cobblemon:example. The example JSON is inside the data folder of the mod.
- Added dialogue API and data registry. This is powerful, and there is a full example in the Cobblemon mod you can trigger using `/opendialogue <username> cobblemon:example`. The example JSON is inside the data folder of the mod.
- Opened up the ItemDropEntry class to allow for it to be extended.
- Added a new "advancement" evolution variant that takes an advancement identifier and succeeds if the player has the advancement.
- Made the spawning API capable of non-entity spawning. You can make it 'spawn' commands if you want. Go nuts.
- Made PokemonProperties.parse more interoperable with Java. Save yourself and stop using Java.
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
- ``Pokemon.ability`` no longer has a public setter. Please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 or 2 regular abilities and 1 hidden ability.
- ``Pokemon#rollAbility`` has been added which rerolls for a legal ability for the Pokémon.
- Made `PokemonProperties.parse` more interoperable with Java. Save yourself and stop using Java.
- Added the `HeldItemEvent`, this comes with 2 implementation `HeldItemEvent.Pre` and `HeldItemEvent.Post`.
- Corrections to the ability implementation have been made that make `Ability.forced` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
- `Pokemon.ability` no longer has a public setter. Please migrate to using `Pokemon#updateAbility`, this handles the ability coordinate storage for you when necessary.
- `Ability.forced`, `Ability.index` and `Ability.priority` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
- `AbilityChanger` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 or 2 regular abilities and 1 hidden ability.
- `Pokemon#rollAbility` has been added which re9rolls for a legal ability for the Pokémon.
- `docs/cobblemon-tags` tags and spawn-presets can now be auto-generated from the current tags in the Cobblemon mod with the `generateSpawnPresetList.py` and `generateTagList.py` scripts.
- `public_spawns_to_json.py` script has been added to give the powerful spawn.json generator a simplistic UI.
- Updated Showdown with Gen 9 DLC.
@@ -598,14 +616,14 @@ These are our compromise for having both regular and Hisuian forms of these Pok
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
- Custom fossils can be defined using a list of items and the resulting Pokémon. An example of `aerodactyl.json`:
```JSON
`JSON
{
"result": "aerodactyl",
"fossils": [
"cobblemon:old_amber_fossil"
]
}
```
`
- Fetus models can be defined in `bedrock/fossils`.
- Fetus textures can be defined in `textures/fossils`.
- Fuel for the Restoration Machine is registered inside the `natural_materials` folder. To register more fuels, create a JSON file containing an array of objects. Each object supports the following fields:
@@ -614,10 +632,10 @@ These are our compromise for having both regular and Hisuian forms of these Pok
- `returnItem`: Identifier of an item to return to the player after consumption.
- Added 3 new item tags: `ancient_poke_balls`, `fossils`, and `tumblestones`.
- Added a spawn rules system to modify general spawning behaviour, see the [wiki](https://wiki.cobblemon.com/index.php/Spawn_Rules) for more information.
- Added dialogue datapack folder and /opendialogue command.
- Added the item tag ``cobblemon:held/leaves_leftovers`` this can be used to flag apple-like items that can create leftovers when eaten.
- Added dialogue datapack folder and `/opendialogue` command.
- Added the item tag `cobblemon:held/leaves_leftovers` this can be used to flag apple-like items that can create leftovers when eaten.
- You can now add support for dynamic lighting implementations, Cobblemon ships with a default implementation for [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) and the [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), add the following to any species or form:
```JSON
`JSON
{
"lightingData": {
"lightLevel": 14,
@@ -626,10 +644,10 @@ These are our compromise for having both regular and Hisuian forms of these Pok
"_liquidGlowModeCommentRemoveMe": "Above supports LAND, UNDERWATER or BOTH"
}
}
```
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
- Added the item tag ``cobblemon:ability_changers``, contains the ``cobblemon:ability_capsule`` & ``cobblemon:ability_patch`` by default.
- Added the item tag ``cobblemon:held/is_friendship_booster``, allows items to give the Soothe Bell effect of a boost of 1.5x Friendship gained, contains ``cobblemon:soothe_bell`` by default.
`
- Following up on this change `light_source` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
- Added the item tag `cobblemon:ability_changers`, contains the `cobblemon:ability_capsule` & `cobblemon:ability_patch` by default.
- Added the item tag `cobblemon:held/is_friendship_booster`, allows items to give the Soothe Bell effect of a boost of 1.5x Friendship gained, contains `cobblemon:soothe_bell` by default.
## [1.4.1 (December 23rd, 2023)](#1-4-1)
@@ -645,7 +663,7 @@ These are our compromise for having both regular and Hisuian forms of these Pok
- Mooshtank can now be milked with a bowl for Mushroom Stew.
- Updated Showdown version to use generation 9 battle data.
- Added cries to Beldum, Metang and Metagross.
- Added a /bedrockparticle command to run Snowstorm-format particle effects.
- Added a `/bedrockparticle` command to run Snowstorm-format particle effects.
- Added data for Dipplin, Fezandipiti, Munkidori, Ogerpon, Okidogi, Poltchageist and Sinistcha.
- Added additional nickname trigger "Grumm" for Inkay's evolution.
@@ -1138,7 +1156,7 @@ Thank you so much to all of our community translators that bring the mod to the
- Fixed apricorn tree leaves looking gross on the Fast graphics mode.
- Fixed hoes not breaking apricorn tree leaves any faster.
- Fixed Shiftry's PC model position.
- Fixed the /pc command not playing the opening sound.
- Fixed the `/pc` command not playing the opening sound.
- Fixed different forms of Pokémon not being able to appear as different sizes.
- Fixed the Healing Machine soft locking you from using others when removed by non-players.
- Fixed animations being sped up when using the Replay Mod.
@@ -1155,11 +1173,11 @@ Thank you so much to all of our community translators that bring the mod to the
- Fixed battles soft-locking when consecutive Pokémon faint on switch-in.
- Fixed timing and color of battle window messages.
- Fixed players being able to trade, battle and let out their Pokémon while in spectator mode.
- Fixed Galarian Yamask not being able to evolve and by proxy the ``damage_taken`` evolution requirement.
- Fixed Bisharp not being able to evolve and by proxy the ``defeat`` evolution requirement.
- Fixed White-Striped Basculin not being able to evolve because of a broken ``recoil`` evolution requirement.
- Fixed Primeape, Qwilfish and Stantler not being able to evolve because of a broken ``use_move`` evolution requirement.
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve because of a broken ``blocks_traveled`` evolution requirement.
- Fixed Galarian Yamask not being able to evolve and by proxy the `damage_taken` evolution requirement.
- Fixed Bisharp not being able to evolve and by proxy the `defeat` evolution requirement.
- Fixed White-Striped Basculin not being able to evolve because of a broken `recoil` evolution requirement.
- Fixed Primeape, Qwilfish and Stantler not being able to evolve because of a broken `use_move` evolution requirement.
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve because of a broken `blocks_traveled` evolution requirement.
- Fixed displayName property in spawn files not doing what it's meant to do.
- Fixed Pokémon not sleeping in the wild like we wanted them to.
@@ -1174,11 +1192,11 @@ Thank you so much to all of our community translators that bring the mod to the
- Added MongoDB support for storing Pokémon and Player data. Must be enabled in config, requires MongoDB core and sync drivers (4.10.0+).
- CobblemonShowdown updated to version 10.
- Generation of a battle can be set in BattleFormat.
- Pokémon now have ``teraType``, ``dmaxLevel``, and ``gmaxFactor`` properties. Gimmicks can be used during battle by adding the respective identifiers to ``keyItems`` in PlayerData: ``key_stone``, ``z_ring``, ``dynamax_band``, and ``tera_orb``. Dynamax is only supported in Gen 8 battles. Mega Evolution and Z-Power require custom held items to be added (e.g. an item with the path ``gengarite`` will allow Gengar to Mega Evolve). Currently custom Z-Crystals and Mega Stones are not supported.
- Pokémon now have `teraType`, `dmaxLevel`, and `gmaxFactor` properties. Gimmicks can be used during battle by adding the respective identifiers to `keyItems` in PlayerData: `key_stone`, `z_ring`, `dynamax_band`, and `tera_orb`. Dynamax is only supported in Gen 8 battles. Mega Evolution and Z-Power require custom held items to be added (e.g. an item with the path `gengarite` will allow Gengar to Mega Evolve). Currently custom Z-Crystals and Mega Stones are not supported.
### Datapack & Resourcepack Creators
- All potion related shoulder effects have had their IDs changed. They now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data). For example, here is the converted Pidgey asset:
- ```json
- `json
{
"type": "potion_effect",
"effect": "minecraft:slow_falling",
@@ -1187,18 +1205,18 @@ Thank you so much to all of our community translators that bring the mod to the
"showParticles": false,
"showIcon": false
}
```
- Renamed the ``walked_steps`` evolution requirement to ``blocks_traveled``.
`
- Renamed the `walked_steps` evolution requirement to `blocks_traveled`.
- Added support for scale in animations.
- Added support for jump keyframes (i.e. pre and post keyframes)
- Added structure spawning conditions
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
- Added support for battle music. Sounds can be added to the ``battle.pvp.default`` and ``battle.pvw.default`` sound events.
- Added support for battle music. Sounds can be added to the `battle.pvp.default` and `battle.pvw.default` sound events.
- Added 'enabled' optional property on model layers, allowing later variations to disable previously-defined layers. See [this issue](https://gitlab.com/cable-mc/cobblemon/-/issues/335) for how this looks.
- Cobblemon items can now all have their own tooltips via resourcepacks. To add a tooltip, add a lang entry like "item.cobblemon.{item_id}.tooltip". If you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key ``item`` for what used to be the existing condition support and a ``nbt`` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key `item` for what used to be the existing condition support and a `nbt` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
- Fixed faint animations not working properly in add-ons.
- Fixed non-existent species in spawn pool files causing random species to spawn.
@@ -1216,7 +1234,7 @@ Thank you so much to all of our community translators that bring the mod to the
- Added animations to Staryu line and Porygon line.
- Added faint animations to Klink line.
- Add lava surface spawn preset.
- Added an ``any`` evolution requirement allowing you to define ``possibilities`` of other evolution requirements, for example, this allows you to create an evolution that requires the Pokémon to be shiny or a female.
- Added an `any` evolution requirement allowing you to define `possibilities` of other evolution requirements, for example, this allows you to create an evolution that requires the Pokémon to be shiny or a female.
- Added the `/spawnpokemonfrompool [amount]` or `/forcespawn [amount]` command to spawn Pokémon(s) in the surrounding area using the natural spawn rates/pool of that area, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.spawnpokemon` if a permission mod is present. On a successful execution of the command, the amount of Pokémon spawned will be the output.
- Added the `/pokebox` and `/pokeboxall` commands to move Pokémon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
- Added the `/pc` command which opens up the PC UI the same way interacting with the block would, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pc` if a permission mod is present.
@@ -1341,7 +1359,7 @@ Thank you so much to all of our community translators that bring the mod to the
- Added support for custom Pokémon to implement 'quirks' such as blinks.
- Added sound effect for harvesting Apricorns.
- Added icon to summary and PC interfaces to indicated if a Pokémon is shiny.
- Added the ``/spawnpokemonat <pos> <properties>`` command, the ``pos`` argument uses the same syntax as the Minecraft [summon](https://minecraft.fandom.com/wiki/Commands/summon) command.
- Added the `/spawnpokemonat <pos> <properties>` command, the `pos` argument uses the same syntax as the Minecraft [summon](https://minecraft.fandom.com/wiki/Commands/summon) command.
- Added the `/giveallpokemon` command which is definitely safe and not insane.
- Added compatibility with Carry On by preventing the mod being able to interact with Cobblemon entities, the mod caused too many gameplay detrimental features to stay enabled.
- Added healing to your party when you sleep in a bed.
@@ -1350,18 +1368,18 @@ Thank you so much to all of our community translators that bring the mod to the
- Added a config option for disallowing players from damaging Pokémon by hand.
- Apricorn seeds can now be used with the [Composter](https://minecraft.fandom.com/wiki/Composter), these have the layer increase chance of 65% like Apricorns and various Minecraft crops.
- Added support for Pokémon species data appending making it so datapack developers no longer need to overwrite files.
- Added an implementation of every [catch rate](https://bulbapedia.bulbagarden.net/wiki/Catch_rate) from generation 1 to 9, these can be used by changing the ``captureCalculator`` config value:
- ``generation_1`` Sets the calculator to the generation 1 implementation.
- ``generation_2`` Sets the calculator to the generation 2 implementation.
- ``generation_2_fixed`` Sets the calculator to the generation 2 implementation with the status multiplier bug fixed.
- ``generation_3_4`` Sets the calculator to the generation 3 and 4 implementation.
- ``generation_5`` Sets the calculator to the generation 5 implementation.
- ``generation_6`` Sets the calculator to the generation 6 implementation.
- ``generation_7`` Sets the calculator to the generation 7 implementation.
- ``generation_8`` Sets the calculator to the generation 8 implementation.
- ``generation_9`` Sets the calculator to the generation 9 implementation.
- ``cobblemon`` Sets the calculator to the custom Cobblemon implementation. This is the default value.
- ``debug`` Sets the calculator to the debug/cheat implementation, every attempt will be a successful critical capture.
- Added an implementation of every [catch rate](https://bulbapedia.bulbagarden.net/wiki/Catch_rate) from generation 1 to 9, these can be used by changing the `captureCalculator` config value:
- `generation_1` Sets the calculator to the generation 1 implementation.
- `generation_2` Sets the calculator to the generation 2 implementation.
- `generation_2_fixed` Sets the calculator to the generation 2 implementation with the status multiplier bug fixed.
- `generation_3_4` Sets the calculator to the generation 3 and 4 implementation.
- `generation_5` Sets the calculator to the generation 5 implementation.
- `generation_6` Sets the calculator to the generation 6 implementation.
- `generation_7` Sets the calculator to the generation 7 implementation.
- `generation_8` Sets the calculator to the generation 8 implementation.
- `generation_9` Sets the calculator to the generation 9 implementation.
- `cobblemon` Sets the calculator to the custom Cobblemon implementation. This is the default value.
- `debug` Sets the calculator to the debug/cheat implementation, every attempt will be a successful critical capture.
### Changes
- Pokémon now save to the world by default, meaning the same Pokémon will remain in the world and not disappear after you log out and log back in. They still despawn over time though.
@@ -1374,9 +1392,9 @@ Thank you so much to all of our community translators that bring the mod to the
- Made it so that particles are not shown whenever you have a shoulder Pokémon that gives potion effects.
- Changed hitbox and size definitions for Decidueye, Blastoise, and Magmortar
- Apricorns can now be harvested with Axes, the speed will scale with enchantments and tool material, only dropping the Apricorn if fully grown, these will still destroy the Apricorn so the manual harvest still is recommended unless you're just keen on destroying trees.
- Apricorns are now a part of the vanilla tag ``minecraft:blocks/mineable/axe``.
- Apricorns are now a part of the vanilla tag `minecraft:blocks/mineable/axe`.
- Apricorns are now compatible with any mod that breaks a whole tree at once.
- Apricorns no longer have a config value for the seed drop chance these are now a part of their loot table which can be found in ``cobblemon/loot_tables/blocks/<color>_apricorn.json``.
- Apricorns no longer have a config value for the seed drop chance these are now a part of their loot table which can be found in `cobblemon/loot_tables/blocks/<color>_apricorn.json`.
- Advancements were redone to be slightly more interesting, with improved names, descriptions, and sorting.
- Updated models and textures for Tentacool line, Gengar, Slowpoke line, Tyrogue line, Doduo line, Dratini, Dragonair, Quagsire, and Piplup line. There were probably others, the team lost track.
- Improved sending out Pokémon at the start of battle so that they are positioned in a less annoying way.
@@ -1414,17 +1432,17 @@ Thank you so much to all of our community translators that bring the mod to the
- Fixed positioning of Poké Balls when rendered in Healing Machines.
- Fixed a desync issue on servers where all Pokémon seemed like they were special forms when they weren't.
- Fixed an incompatibility with [Exordium](https://www.curseforge.com/minecraft/mc-mods/exordium).
- Fixed datapack Pokémon language key generation. A Pokémon under the namespace ``example`` named ``Pogemon`` will now correctly look for the lang key ``example.species.pogemon.name``.
- Fixed datapack Pokémon language key generation. A Pokémon under the namespace `example` named `Pogemon` will now correctly look for the lang key `example.species.pogemon.name`.
- Fixed client not receiving messages for the different "stages" for the move Bide.
- Fixed the Medium Slow and Medium Fast experience group IDs, they're now ```medium_slow``` and ``medium_fast``. Any custom datapack Pokémon will require an update.
- Fixed the Medium Slow and Medium Fast experience group IDs, they're now `medium_slow` and `medium_fast`. Any custom datapack Pokémon will require an update.
- Fixed Pokémon friendship being capped to the maximum level config value instead of the friendship one when loading Pokémon data.
- Fixed Poké Balls taking forever to capture Pokémon if you are underwater or up in the air where it takes a long time to hit the ground.
- Fixed Pokémon being unable to spawn on blocks such as snow layers.
- Fixed Pokémon spawning inside of trees.
- Fixed Pokémon experience not loading after a restart and instead going back to the minimal amount for the current level.
- Fixed being able to use ``/healpokemon`` in battle.
- Fixed being able to use `/healpokemon` in battle.
- Fixed being able to select fainted party members in the switch menu causing the battle to lock.
- Fixed ``/spawnpokemon`` command not supporting any command source other than players.
- Fixed `/spawnpokemon` command not supporting any command source other than players.
- Fixed issues with Charizard's sleep pose.
- Fixed players being able to use multiple healer machines at once.
- Fixed Pokémon layers not rendering when a Pokémon is on your shoulder.
@@ -1459,7 +1477,7 @@ Thank you so much to all of our community translators that bring the mod to the
- Added models for Natu and Xatu, Murkrow and Honchkrow, Wailmer and Wailord.
- Added new PC interface and it is beautiful.
- Reworked the battle system so that battles load faster, cause fewer bugs, and can run on shared server hosts. This is a very big change that also reduced the size of the mod by 50mb!
- Added full resource pack / data pack customization of models, textures, animations, spawning, and spawn file presets to make custom Pokémon species and variations very easy to create. You can find the guide for creating custom Pokémon on [our website](https://cobblemon.com/guides/custompokemon.html)!
- Added full resource pack and data pack customization of models, textures, animations, spawning, and spawn file presets to make custom Pokémon species and variations very easy to create. You can find the guide for creating custom Pokémon on [our website](https://cobblemon.com/guides/custompokemon.html)!
- Added water surface spawning for Pokémon like Lapras.
- Added emissive texture support to Pokémon render layers.
- Added compatibility for Mod Menu ([CurseForge](https://www.curseforge.com/minecraft/mc-mods/modmenu), [Modrinth](https://modrinth.com/mod/modmenu)).
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.entity.npc.NPCEntity;
import java.util.Collection;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.sensing.Sensor;
import net.minecraft.world.entity.ai.sensing.SensorType;
public class GenericsCheatClass {
// For some reason, doing this in the NPCEntity class in Kotlin gives confusing generic errors I could not resolve.
public static Brain.Provider<NPCEntity> createNPCBrain(Collection<MemoryModuleType<?>> memories, Collection<SensorType<? extends Sensor<? super NPCEntity>>> sensors) {
return Brain.provider(memories, sensors);
}
}
@@ -10,18 +10,12 @@ package com.cobblemon.mod.common.duck;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
public interface SoundSystemDuck {
public interface SoundEngineDuck {
void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void stopSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
Collection<SoundInstance> getSounds(@Nullable SoundSource category);
}
@@ -17,6 +17,4 @@ public interface SoundManagerDuck {
void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void stopSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import static com.cobblemon.mod.common.datafixer.CobblemonSchemas.VERSION_KEY;
import com.cobblemon.mod.common.datafixer.CobblemonSchemas;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.mojang.serialization.MapCodec;
import java.util.Optional;
import java.util.function.Supplier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.storage.ChunkStorage;
import net.minecraft.world.level.storage.DimensionDataStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkStorage.class)
public class ChunkStorageMixin {
//Modifies the NBT returned by the vanilla upgradeChunkTag to also incorporate cobblemon fixup stuff
@ModifyExpressionValue(
method = "upgradeChunkTag(Lnet/minecraft/resources/ResourceKey;Ljava/util/function/Supplier;Lnet/minecraft/nbt/CompoundTag;Ljava/util/Optional;)Lnet/minecraft/nbt/CompoundTag;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/util/datafix/DataFixTypes;updateToCurrentVersion(Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;"
)
)
public CompoundTag cobblemon$doChunkFix(
CompoundTag vanillaFixed
) {
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
return DataFixTypes.CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
}
@Inject(
method = "upgradeChunkTag(Lnet/minecraft/resources/ResourceKey;Ljava/util/function/Supplier;Lnet/minecraft/nbt/CompoundTag;Ljava/util/Optional;)Lnet/minecraft/nbt/CompoundTag;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/nbt/NbtUtils;addCurrentDataVersion(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;"
)
)
public void cobblemon$addChunkVersion(
ResourceKey<Level> resourceKey,
Supplier<DimensionDataStorage> supplier,
CompoundTag compoundTag,
Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> optional,
CallbackInfoReturnable<CompoundTag> cir
) {
compoundTag.put(VERSION_KEY, IntTag.valueOf(CobblemonSchemas.DATA_VERSION));
}
}
@@ -19,7 +19,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CrashReport.class)
public final class CrashReportMixin {
@Inject(method = "getDetails", at = @At(value = "INVOKE", target = "Lnet/minecraft/SystemReport;appendToCrashReportString(Ljava/lang/StringBuilder;)V"))
@Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/SystemReport;appendToCrashReportString(Ljava/lang/StringBuilder;)V"))
public void cobblemon$printCobblemonDetails(StringBuilder builder, CallbackInfo callback) {
CrashReportCategory cobblemon = new CrashReportCategory("Cobblemon");
cobblemon.setDetail("Version", CobblemonBuildDetails.VERSION);
@@ -21,11 +21,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(DecoratedPotPatterns.class)
public abstract class DecoratedPotPatternsMixin {
@Inject(method = "getPatternFromItem", at=@At("HEAD"), cancellable = true)
private static void cobblemon$getCobblemonSherdTexture(Item sherd,
CallbackInfoReturnable<ResourceKey<DecoratedPotPattern>> cir) {
private static void cobblemon$getCobblemonSherdTexture(
Item sherd,
CallbackInfoReturnable<ResourceKey<DecoratedPotPattern>> cir
) {
if (CobblemonSherds.INSTANCE.getSherdToPattern().containsKey(sherd)) {
cir.setReturnValue(CobblemonSherds.INSTANCE.getSherdToPattern().get(sherd));
return;
cir.cancel();
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import static com.cobblemon.mod.common.datafixer.CobblemonSchemas.VERSION_KEY;
import com.cobblemon.mod.common.datafixer.CobblemonSchemas;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.chunk.storage.EntityStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(EntityStorage.class)
public class EntityStorageMixin {
//Modifies the NBT returned by the vanilla upgradeChunkTag to also incorporate cobblemon fixup stuff
@ModifyExpressionValue(
method = "method_31731(Lnet/minecraft/world/level/ChunkPos;Ljava/util/Optional;)Lnet/minecraft/world/level/entity/ChunkEntities;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;upgradeChunkTag(Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;"
)
)
public CompoundTag cobblemon$doEntityFix(
CompoundTag vanillaFixed
) {
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
CompoundTag newTag = DataFixTypes.ENTITY_CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
newTag.put(VERSION_KEY, IntTag.valueOf(CobblemonSchemas.DATA_VERSION));
return newTag;
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemInHandRenderer.class)
public class ItemInHandRendererMixin {
@Inject(method = "renderArmWithItem", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$renderArmWithItem(AbstractClientPlayer abstractClientPlayer, float f, float g, InteractionHand interactionHand, float h, ItemStack itemStack, float i, PoseStack poseStack, MultiBufferSource multiBufferSource, int j, CallbackInfo ci) {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
}
}
@@ -8,21 +8,30 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.ModAPI;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/*
I think it would be nice to maybe use the existing mixin we have for custom item rendering [BuiltinModelItemRendererMixin]
@@ -30,25 +39,52 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
*/
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Unique private final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@Shadow public abstract void render(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model);
@Inject(
method = "Lnet/minecraft/client/renderer/entity/ItemRenderer;render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
method = "render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$overridePokedexModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int i, int j, BakedModel model, CallbackInfo ci) {
boolean shouldOverride = renderMode != ItemDisplayContext.GUI && renderMode != ItemDisplayContext.FIXED;
if (shouldOverride && stack.getItem() instanceof PokedexItem) {
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
if (!shouldBe2d && stack.getItem() instanceof PokedexItem) {
BakedModel replacementModel = CobblemonBakingOverrides.INSTANCE.getPokedexOverride(((PokedexItem) stack.getItem()).getType()).getModel();
if (!model.equals(replacementModel)) {
if (!cobblemon$isSameModel(model, replacementModel)) {
ci.cancel();
render(stack, renderMode, leftHanded, matrices, multiBufferSource, i, j, replacementModel);
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
}
} else if (shouldBe2d && stack.getItem() instanceof PokeBallItem pokeBallItem) {
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel2d(), "inventory"));
if (!cobblemon$isSameModel(model, replacementModel)) {
ci.cancel();
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
}
}
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void cobblemon$bakePokeballModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
if (stack.getItem() instanceof PokeBallItem pokeBallItem) {
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel3d(), MODEL_PATH));
ClientLevel clientWorld = world instanceof ClientLevel ? (ClientLevel) world : null;
BakedModel overriddenModel = model.getOverrides().resolve(model, stack, clientWorld, entity, seed);
cir.setReturnValue(overriddenModel == null ? this.itemModelShaper.getModelManager().getMissingModel() : overriddenModel);
}
}
// this method aims to cover an issue with modernfixes and it's "dynamic resource" causing the usual .equals check to always fail, resulting in a StackOverflow
// this falls back to comparing the model contents (e.g. location of the texture, width and height)
@Unique
private static boolean cobblemon$isSameModel(BakedModel original, BakedModel replacement) {
return original.equals(replacement) ||
(original.getParticleIcon().contents().name().equals(replacement.getParticleIcon().contents().name()) &&
original.getParticleIcon().contents().width() == replacement.getParticleIcon().contents().width() &&
original.getParticleIcon().contents().height() == replacement.getParticleIcon().contents().height());
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.util.DataKeys;
import com.mojang.serialization.Dynamic;
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.stream.Stream;
@Mixin(ItemStackComponentizationFix.class)
public class ItemStackComponentizationFixMixin
{
@Inject(method = "fixItemStack", at = @At("TAIL"))
private static void fixItemStack(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic<?> dynamic, CallbackInfo ci) {
cobblemon$attemptPokemonModelDataFix(itemStackData, dynamic);
}
@Unique
private static void cobblemon$attemptPokemonModelDataFix(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic<?> dynamic)
{
var species = itemStackData.removeTag(DataKeys.POKEMON_ITEM_SPECIES).asString();
var aspects = itemStackData.removeTag(DataKeys.POKEMON_ITEM_ASPECTS).asList(dynamic1 -> dynamic1);
var redTint = itemStackData.removeTag(DataKeys.POKEMON_ITEM_TINT_RED).asNumber();
var greenTint = itemStackData.removeTag(DataKeys.POKEMON_ITEM_TINT_GREEN).asNumber();
var blueTint = itemStackData.removeTag(DataKeys.POKEMON_ITEM_TINT_BLUE).asNumber();
var alphaTint = itemStackData.removeTag(DataKeys.POKEMON_ITEM_TINT_ALPHA).asNumber();
if(species.hasResultOrPartial() && aspects != null) {
var pokeComponent = dynamic.emptyMap().set("species", dynamic.createString(species.getOrThrow()));
pokeComponent = pokeComponent.set("aspects", dynamic.createList(aspects.stream()));
if(redTint.hasResultOrPartial() || blueTint.hasResultOrPartial() || greenTint.hasResultOrPartial() || alphaTint.hasResultOrPartial()) {
pokeComponent = pokeComponent.set("tint", dynamic.createList(Stream.of(redTint, greenTint, blueTint, alphaTint)
.map(x -> x.mapOrElse(Number::floatValue, error -> 0F))
.map(dynamic::createFloat)));
}
itemStackData.setComponent("cobblemon:pokemon_item", pokeComponent);
}
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.fabric.mixin;
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.block.entity.LecternBlockEntity;
import com.cobblemon.mod.common.CobblemonBlocks;
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.MusicManager;
@@ -21,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MusicManager.class)
public class MusicTrackerMixin {
public class MusicManagerMixin {
@Shadow @Final
private Minecraft minecraft;
@@ -43,16 +43,16 @@ public class PartyScrollMixin {
)
public void cobblemon$scrollParty(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int)accumulatedScrollY;
int i = (int) accumulatedScrollY;
if (i > 0) {
while (i-- > 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
accumulatedScrollY = 0;
PartySendBinding.INSTANCE.actioned();
} else if (i < 0) {
while (i++ < 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
accumulatedScrollY = 0;
PartySendBinding.INSTANCE.actioned();
}
}
@@ -119,14 +119,12 @@ public abstract class PlayerMixin extends LivingEntity {
private void recallPokemon(UUID uuid) {
// We need to do this cause the Entity doesn't store a reference to its storage
try {
final PartyStore party = Cobblemon.INSTANCE.getStorage().getParty(this.uuid);
for (Pokemon pokemon : party) {
if (pokemon.getUuid().equals(uuid)) {
pokemon.recall();
}
final PartyStore party = Cobblemon.INSTANCE.getStorage().getParty(this.getUUID(), this.registryAccess());
for (Pokemon pokemon : party) {
if (pokemon.getUuid().equals(uuid)) {
pokemon.recall();
}
} catch (NoPokemonStoreException ignored) {}
}
}
private boolean isShoulderPokemon(CompoundTag nbt) {
@@ -8,8 +8,8 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import com.cobblemon.mod.common.duck.SoundSystemDuck;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import com.google.common.collect.Multimap;
import com.mojang.blaze3d.audio.Channel;
import net.minecraft.client.resources.sounds.SoundInstance;
@@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
@Mixin(SoundEngine.class)
public abstract class SoundSystemMixin implements SoundSystemDuck {
public abstract class SoundEngineMixin implements SoundEngineDuck {
@Shadow
private boolean loaded;
@@ -91,7 +91,7 @@ public abstract class SoundSystemMixin implements SoundSystemDuck {
/** Allows stopping all SoundInstances that belong to a queried category. */
@Inject(method = "stop(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/sounds/SoundSource;)V", at = @At("HEAD"), cancellable = true)
public void stopSoustopnds(@Nullable ResourceLocation id, @Nullable SoundSource category, CallbackInfo cb) {
public void stopSounds(@Nullable ResourceLocation id, @Nullable SoundSource category, CallbackInfo cb) {
if (id == null && category != null) {
this.instanceBySource.get(category).forEach(this::stop);
cb.cancel();
@@ -8,9 +8,12 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import com.cobblemon.mod.common.client.sound.BlockEntitySoundTracker;
import com.cobblemon.mod.common.client.sound.EntitySoundTracker;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import com.cobblemon.mod.common.client.sound.SoundTracker;
import com.cobblemon.mod.common.duck.SoundManagerDuck;
import com.cobblemon.mod.common.duck.SoundSystemDuck;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.UnderwaterAmbientSoundInstances;
@@ -55,19 +58,13 @@ public abstract class SoundManagerMixin implements SoundManagerDuck {
/** Pauses the queried SoundInstance(s). If id is null, will pause all sounds belonging to the SoundCategory. */
@Override
public void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
((SoundSystemDuck)soundEngine).pauseSounds(id, category);
((SoundEngineDuck)soundEngine).pauseSounds(id, category);
}
/** Resumes the queried SoundInstance(s). If id is null, will resume all sounds belonging to the SoundCategory. */
@Override
public void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
((SoundSystemDuck)soundEngine).resumeSounds(id, category);
}
/** Stops the queried SoundInstance(s). If id is null, will stop all sounds belonging to the SoundCategory. */
@Override
public void stopSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
((SoundSystemDuck)soundEngine).stopSounds(id, category);
((SoundEngineDuck)soundEngine).resumeSounds(id, category);
}
/** Blocks filtered sounds from being played while a BattleMusicInstance is in progress. */
@@ -91,4 +88,10 @@ public abstract class SoundManagerMixin implements SoundManagerDuck {
public void playEnd(SoundInstance sound, int delay, CallbackInfo cb) {
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getLocation(), SoundSource.AMBIENT);
}
/** Clears tracked sounds on the client when disconnected. */
@Inject(method = "stop()V", at = @At("TAIL"))
public void stop(CallbackInfo cb) {
SoundTracker.Companion.clear();
}
}
@@ -9,22 +9,26 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement;
import net.minecraft.world.level.levelgen.structure.pools.LegacySinglePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.level.levelgen.structure.pools.*;
import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasLookup;
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.apache.commons.lang3.mutable.MutableObject;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.*;
@@ -39,11 +43,11 @@ public abstract class StructurePoolGeneratorMixin {
String pokecenter = "pokecenter";
String berry = "berry_farm";
structureGroups.put(CobblemonStructureIDs.PLAINS_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.DESERT_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SNOWY_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.TAIGA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.VILLAGE_PLAINS_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.VILLAGE_DESERT_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.VILLAGE_SAVANNA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.VILLAGE_SNOWY_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.VILLAGE_TAIGA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_LARGE, Set.of(berry));
@@ -117,53 +121,25 @@ public abstract class StructurePoolGeneratorMixin {
return reducedList.iterator();
}
@ModifyVariable(method = "tryPlacingChildren", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private PoolElementStructurePiece injected(PoolElementStructurePiece poolStructurePiece) {
ResourceLocation structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getElement());
@Inject(method = "tryPlacingChildren", at = @At(value = "NEW", target = "net/minecraft/world/level/levelgen/structure/PoolElementStructurePiece", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD)
private void afterTryPlacingChildren(
PoolElementStructurePiece poolElementStructurePiece,
MutableObject<VoxelShape> mutableObject,
int i,
boolean bl,
LevelHeightAccessor levelHeightAccessor,
RandomState randomState,
PoolAliasLookup poolAliasLookup,
LiquidSettings liquidSettings,
CallbackInfo ci,
@Local(ordinal = 1) StructurePoolElement structurePoolElement
) {
ResourceLocation structureLocationKey = getCobblemonOnlyLocation(structurePoolElement);
if (structureLocationKey != null) {
incrementStructureCount(structureLocationKey);
}
return poolStructurePiece;
}
// This doesn't SEEM necessary? It appears to work without.
// @Inject(method = "generatePiece", at = @At("HEAD"))
// private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
//// Identifier structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
////
//// if (structureLocationKey != null) {
//// Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
//// if (currentlyGenerated == null) currentlyGenerated = 0;
//// generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
//// }
////
//// List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
////
//// for (Identifier maxStructureLocationKey : structureMaxes.keySet()) {
//// Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
////
//// Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
//// if (currentlyGenerated == null) currentlyGenerated = 0;
//// if (currentlyGenerated < maxAllowed) {
//// continue;
//// }
////
//// //Already have max so need to remove
//// reducedStructurePiecesList = reducedStructurePiecesList.stream()
//// .filter(shapedStructurePiece -> {
//// Identifier locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
//// if (locationKey == null) {
//// return true;
//// }
////
//// return !locationKey.equals(maxStructureLocationKey);
//// })
//// .collect(Collectors.toList());
//// }
////
//// structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
// }
private static ResourceLocation getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
ResourceLocation location = getLocationIfAvailable(structurePoolElement);
if (location == null) return null;
@@ -186,26 +162,4 @@ public abstract class StructurePoolGeneratorMixin {
return null;
}
}
// private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, Identifier locationKey) {
// List<StructurePoolElement> reducedList = new ArrayList<>();
// int instancesFound = 0;
//
// for (StructurePoolElement structurePoolElement: structureList) {
// Identifier structureKey = getCobblemonOnlyLocation(structurePoolElement);
// if (structureKey == null || !structureKey.equals(locationKey)) {
// reducedList.add(structurePoolElement);
// continue;
// }
//
// if (instancesFound >= allowedNumberOfInstances) {
// continue;
// }
//
// reducedList.add(structurePoolElement);
// instancesFound++;
// }
//
// return reducedList;
// }
}
@@ -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.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonVillagerProfessions;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import kotlin.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.VillagerProfession;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RenderLayer.class)
public abstract class VillagerProfessionLayerMixin {
@Inject(method = "renderColoredCutoutModel", at = @At(value = "HEAD"), cancellable = true)
private static <T extends LivingEntity> void cobblemon$renderVillagerProfessionLayer(EntityModel<T> entityModel, ResourceLocation resourceLocation, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, int j, CallbackInfo ci) {
if (resourceLocation.getNamespace().equals(Cobblemon.MODID)) {
VillagerProfession profession = null;
boolean isZombie = false;
if (livingEntity instanceof Villager) profession = ((Villager) livingEntity).getVillagerData().getProfession();
if (livingEntity instanceof ZombieVillager) {
profession = ((ZombieVillager) livingEntity).getVillagerData().getProfession();
isZombie = true;
}
Pair<String, String[]> override = CobblemonVillagerProfessions.INSTANCE.getNameTagOverride(profession);
if (profession != null && override != null) {
String entityName = ChatFormatting.stripFormatting(livingEntity.getName().getString());
boolean nameMatches = false;
for (String name: override.getSecond())
if (entityName.endsWith(name)) {
nameMatches = true;
break;
}
if (nameMatches && resourceLocation.equals(getProfessionResourceLocation(resourceLocation.getNamespace(), ResourceLocation.parse(profession.name()).getPath(), isZombie))) {
ResourceLocation textureLocation = getProfessionResourceLocation(resourceLocation.getNamespace(), override.getFirst(), isZombie);
VertexConsumer vertexConsumer = multiBufferSource.getBuffer(RenderType.entityCutoutNoCull(textureLocation));
entityModel.renderToBuffer(poseStack, vertexConsumer, i, LivingEntityRenderer.getOverlayCoords(livingEntity, 0.0F), j);
ci.cancel();
}
}
}
}
private static ResourceLocation getProfessionResourceLocation(String namespace, String profession, Boolean isZombie) {
return ResourceLocation.fromNamespaceAndPath(namespace, "textures/entity/" + (isZombie ? "zombie_villager" : "villager") + "/profession/" + profession + ".png");
}
}
@@ -0,0 +1,19 @@
/*
* 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.level.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Biome.class)
public interface BiomeAccessor {
@Accessor("climateSettings")
Biome.ClimateSettings getClimateSettings();
}
@@ -32,7 +32,7 @@ public class GuiMixin {
// sus af. You have to get the method name by looking at the bytecode of the class. Can do in intellij By clicking View -> Show Bytecode (top left)
@Inject(method = "method_55797", at = @At("HEAD"), cancellable = true)
private static void cobblemon$dontRenderUiInPokedex(Minecraft minecraft, CallbackInfoReturnable<Boolean> cir) {
if (CobblemonClient.INSTANCE.getPokedexUsageContext().getScanningGuiOpen()) {
if (CobblemonClient.INSTANCE.getPokedexUsageContext().getScanningGuiOpen() && minecraft.options.getCameraType().isFirstPerson()) {
cir.setReturnValue(false);
}
}
@@ -1,69 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.pokeball;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.ModAPI;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Unique private final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@Shadow public abstract void render(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model);
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void cobblemon$bakePokeballModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
if (stack.getItem() instanceof PokeBallItem pokeBallItem) {
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel3d(), MODEL_PATH));
ClientLevel clientWorld = world instanceof ClientLevel ? (ClientLevel) world : null;
BakedModel overriddenModel = model.getOverrides().resolve(model, stack, clientWorld, entity, seed);
cir.setReturnValue(overriddenModel == null ? this.itemModelShaper.getModelManager().getMissingModel() : overriddenModel);
}
}
@Inject(
method = "render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$determinePokeballModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) {
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
if (shouldBe2d && stack.getItem() instanceof PokeBallItem pokeBallItem) {
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(pokeBallItem.getPokeBall().getModel2d(), "inventory"));
if (!model.equals(replacementModel)) {
ci.cancel();
render(stack, renderMode, leftHanded, matrices, vertexConsumers, light, overlay, replacementModel);
}
}
}
}
@@ -9,9 +9,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.CobblemonBuildDetails.smallCommitHash
//import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.SeasonResolver
import com.cobblemon.mod.common.api.data.DataProvider
@@ -19,15 +16,7 @@ import com.cobblemon.mod.common.api.drop.CommandDropEntry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_STARTED_POST
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_SCANNED
import com.cobblemon.mod.common.api.events.CobblemonEvents.STARTER_CHOSEN
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.permission.PermissionValidator
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculators
@@ -54,7 +43,6 @@ import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
import com.cobblemon.mod.common.api.storage.adapter.database.MongoDBStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
@@ -63,7 +51,7 @@ import com.cobblemon.mod.common.api.storage.molang.NbtMoLangDataStoreFactory
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
@@ -76,16 +64,16 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.command.argument.DexArgumentType
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.FormArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.NPCClassArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
@@ -104,7 +92,9 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
@@ -116,7 +106,6 @@ 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
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
@@ -140,6 +129,7 @@ object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
const val VERSION = CobblemonBuildDetails.VERSION
const val CONFIG_PATH = "config/$MODID/main.json"
@JvmField
val LOGGER: Logger = LogManager.getLogger()
lateinit var implementation: CobblemonImplementation
@@ -172,7 +162,7 @@ object Cobblemon {
this.implementation = implementation
this.LOGGER.info("Launching Cobblemon ${CobblemonBuildDetails.VERSION}${if(CobblemonBuildDetails.SNAPSHOT) "-SNAPSHOT" else ""} ")
if(CobblemonBuildDetails.SNAPSHOT) {
if (CobblemonBuildDetails.SNAPSHOT) {
this.LOGGER.info(" - Git Commit: ${smallCommitHash()} (https://gitlab.com/cable-mc/cobblemon/-/commit/${CobblemonBuildDetails.GIT_COMMIT})")
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
}
@@ -185,6 +175,7 @@ object Cobblemon {
implementation.registerEntityTypes()
implementation.registerEntityAttributes()
implementation.registerBlockEntityTypes()
implementation.registerVillagers()
implementation.registerWorldGenFeatures()
implementation.registerParticles()
implementation.registerEntityDataSerializers()
@@ -211,7 +202,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel).sendToPlayer(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel).sendToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -286,6 +277,8 @@ object Cobblemon {
CustomPokemonProperty.register(HiddenAbilityPropertyType)
CustomPokemonProperty.register(AspectPropertyType)
CustomPokemonProperty.register(UnaspectPropertyType)
CustomPokemonProperty.register(FreezeFrameProperty)
CustomPokemonProperty.register(NoAIProperty)
ifDedicatedServer {
isDedicatedServer = true
@@ -313,8 +306,8 @@ object Cobblemon {
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
pokedexNbtFactory.setup(server)
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreType.GENERAL)
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreType.POKEDEX)
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
@@ -365,7 +358,7 @@ object Cobblemon {
}
PlatformEvents.SERVER_STOPPED.subscribe {
storage.unregisterAll()
storage.unregisterAll(it.server.registryAccess())
playerDataManager.saveAllStores()
}
PlatformEvents.SERVER_STARTED.subscribe {
@@ -496,6 +489,7 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("dialogue"), DialogueArgumentType::class, SingletonArgumentInfo.contextFree(DialogueArgumentType::dialogue))
this.implementation.registerCommandArgument(cobblemonResource("form"), FormArgumentType::class, SingletonArgumentInfo.contextFree(FormArgumentType::form))
this.implementation.registerCommandArgument(cobblemonResource("dex"), DexArgumentType::class, SingletonArgumentInfo.contextFree (DexArgumentType::dex))
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
}
}
@@ -14,6 +14,8 @@ object CobblemonActivities {
val activities = mutableListOf<Activity>()
val BATTLING_ACTIVITY = Activity("pokemon_battling")
val NPC_BATTLING = Activity("npc_battling")
val NPC_ACTION_EFFECT = Activity("npc_action_effect")
val NPC_CHATTING = Activity("npc_chatting")
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -661,7 +661,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
BlockBehaviour.Properties.of()
.sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS)
.pushReaction(PushReaction.DESTROY)
.strength(0.4f)
.noOcclusion(), true
)
)
@@ -17,6 +17,7 @@ import net.minecraft.commands.Commands
object CobblemonCommands {
fun register(dispatcher: CommandDispatcher<CommandSourceStack>, registry: CommandBuildContext, selection: Commands.CommandSelection) {
SpawnPokemon.register(dispatcher)
SpawnNPCCommand.register(dispatcher)
GivePokemon.register(dispatcher)
TakePokemon.register(dispatcher)
ChangeScaleAndSize.register(dispatcher)
@@ -51,6 +52,8 @@ object CobblemonCommands {
OpenDialogueCommand.register(dispatcher)
NPCEditCommand.register(dispatcher)
AbandonMultiTeam.register(dispatcher)
FreezePokemonCommand.register(dispatcher)
ApplyPlayerTextureCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -97,6 +97,12 @@ interface CobblemonImplementation {
*/
fun registerBlockEntityTypes()
/**
* TODO
*
*/
fun registerVillagers()
/**
* TODO
*
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -20,6 +22,8 @@ object CobblemonMemories {
val BATTLING_POKEMON = register("battling_pokemon", ListCodec(UUIDUtil.CODEC, 0, 31))
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
val DIALOGUES = register<List<ActiveDialogue>>("npc_dialogues")
val ACTIVE_ACTION_EFFECT = register<ActionEffectContext>("active_action_effect")
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
@@ -32,7 +32,7 @@ import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedScanHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
@@ -87,6 +87,7 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.Rem
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnNPCPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
@@ -184,7 +185,7 @@ import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedScanPacket
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
@@ -322,6 +323,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PokeRodRegistrySyncPacket.ID, PokeRodRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(ScriptRegistrySyncPacket.ID, ScriptRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
@@ -333,8 +335,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
list.add(PacketRegisterInfo(SpawnPokemonPacket.ID, SpawnPokemonPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnPokeballPacket.ID, SpawnPokeballPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(ToastPacket.ID, ToastPacket::decode, ToastPacketHandler))
list.add(PacketRegisterInfo(SpawnGenericBedrockPacket.ID, SpawnGenericBedrockPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(SpawnNPCPacket.ID, SpawnNPCPacket::decode, SpawnExtraDataEntityHandler()))
list.add(PacketRegisterInfo(ToastPacket.ID, ToastPacket::decode, ToastPacketHandler))
// Trade packets
list.add(PacketRegisterInfo(TradeAcceptanceChangedPacket.ID, TradeAcceptanceChangedPacket::decode, TradeAcceptanceChangedHandler))
@@ -372,7 +375,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler))
//Pokedex scanning
list.add(PacketRegisterInfo(ServerConfirmedScanPacket.ID, ServerConfirmedScanPacket::decode, ServerConfirmedScanHandler))
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
return list
}
@@ -39,6 +39,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val PC_CLICK = this.create("pc.click")
@JvmField
val VILLAGER_WORK_NURSE = this.create("entity.villager.work_nurse")
@JvmField
val HEALING_MACHINE_ACTIVE = this.create("healing_machine.active")
@@ -51,10 +53,14 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val POKE_BALL_OPEN = this.create("poke_ball.open")
@JvmField
val POKE_BALL_SHINY_OPEN = this.create("poke_ball.shiny_open")
@JvmField
val POKE_BALL_HIT = this.create("poke_ball.hit")
@JvmField
val POKE_BALL_SEND_OUT = this.create("poke_ball.send_out")
@JvmField
val POKE_BALL_SHINY_SEND_OUT = this.create("poke_ball.shiny_send_out")
@JvmField
val POKE_BALL_RECALL = this.create("poke_ball.recall")
@JvmField
val POKE_BALL_THROW = this.create("poke_ball.throw")
@@ -66,29 +72,23 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val POKEDEX_CLOSE = this.create("item.pokedex.close")
@JvmField
val POKEDEX_CLICK = this.create("item.pokedex.click")
@JvmField
val POKEDEX_CLICK_SHORT = this.create("item.pokedex.click_short")
@JvmField
val POKEDEX_SCAN_OPEN = this.create("item.pokedex.scan_open")
@JvmField
val POKEDEX_SCAN_CLOSE = this.create("item.pokedex.scan_close")
@JvmField
val POKEDEX_SCAN = this.create("item.pokedex.scan")
@JvmField
val POKEDEX_SCAN_LOOP = this.create("item.pokedex.scan_loop")
@JvmField
val POKEDEX_SCAN_DETAIL = this.create("item.pokedex.scan_detail")
@JvmField
val POKEDEX_CLICK = this.create("item.pokedex.click")
val POKEDEX_SCAN_REGISTER_POKEMON = this.create("item.pokedex.scan_register_pokemon")
@JvmField
val POKEDEX_CLICK_SHORT = this.create("item.pokedex.click_short")
val POKEDEX_SCAN_REGISTER_ASPECT = this.create("item.pokedex.scan_register_aspect")
@JvmField
val POKEDEX_SNAP_PICTURE = this.create("item.pokedex.snap")
@JvmField
val POKEDEX_ZOOM_IN = this.create("item.pokedex.zoom_in")
@JvmField
val POKEDEX_ZOOM_OUT = this.create("item.pokedex.zoom_out")
@JvmField
val POKEDEX_ZOOM_INCREMENT = this.create("item.pokedex.zoom_increment")
val POKEDEX_SCAN_ZOOM_INCREMENT = this.create("item.pokedex.scan_zoom_increment")
@JvmField
val ITEM_USE = this.create("item.use")
@@ -97,7 +97,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val EVOLUTION_UI = this.create("evolution.ui")
@JvmField
val EVOLUTION = this.create("evolution")
val EVOLUTION = this.create("evolution.full")
@JvmField
val PVN_BATTLE = this.create("battle.pvn.default")
@@ -9,9 +9,16 @@
package com.cobblemon.mod.common
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.util.RandomSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.npc.VillagerData
import net.minecraft.world.entity.npc.VillagerProfession
import net.minecraft.world.entity.npc.VillagerTrades
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.trading.ItemCost
import net.minecraft.world.item.trading.MerchantOffer
import java.util.Optional
/**
* A generator for various trade offers in the mod.
@@ -32,8 +39,41 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
VillagerProfession.FARMER -> listOf(
VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(VillagerTrades.ItemsForEmeralds(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
CobblemonVillagerProfessions.NURSE -> listOf(
// requiredLevel = the level of the villager for the trade to appear for
// Novice = 1, Apprentice = 2, Journeyman = 3, Expert = 4, Master = 5
VillagerTradeOffer(CobblemonVillagerProfessions.NURSE, 1, listOf(
// i = emeraldCost, j = numberOfItems, k = maxUses, l = villagerXp, f = priceMultiplier
VillagerTrades.ItemsForEmeralds(CobblemonItems.ORAN_BERRY, 4, 1, 16, 1),
VillagerTrades.ItemsForEmeralds(CobblemonItems.PERSIM_BERRY, 4, 1, 16, 1),
VillagerTrades.ItemsForEmeralds(CobblemonItems.ENERGY_ROOT, 4, 1, 12, 1),
TradeOffer(ItemCost(CobblemonItems.MEDICINAL_LEEK, 24), ItemStack(Items.EMERALD, 1), 12, 2)
)),
VillagerTradeOffer(CobblemonVillagerProfessions.NURSE, 2, listOf(
TradeOffer(ItemCost(Items.EMERALD, 8), ItemStack(CobblemonItems.MEDICINAL_BREW), 8, 5, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
VillagerTrades.ItemsForEmeralds(CobblemonItems.BLUE_MINT_LEAF, 2, 1, 8, 5),
VillagerTrades.ItemsForEmeralds(CobblemonItems.CYAN_MINT_LEAF, 2, 1, 8, 5),
VillagerTrades.ItemsForEmeralds(CobblemonItems.GREEN_MINT_LEAF, 2, 1, 8, 5),
VillagerTrades.ItemsForEmeralds(CobblemonItems.PINK_MINT_LEAF, 2, 1, 8, 5),
VillagerTrades.ItemsForEmeralds(CobblemonItems.RED_MINT_LEAF, 2, 1, 8, 5),
VillagerTrades.ItemsForEmeralds(CobblemonItems.WHITE_MINT_LEAF, 2, 1, 8, 5)
)),
VillagerTradeOffer(CobblemonVillagerProfessions.NURSE, 3, listOf(
TradeOffer(ItemCost(CobblemonItems.MULCH_BASE, 4), ItemStack(Items.EMERALD, 2), 16, 20),
VillagerTrades.ItemsForEmeralds(Items.GLASS_BOTTLE, 1, 1, 16, 10)
)),
VillagerTradeOffer(CobblemonVillagerProfessions.NURSE, 4, listOf(
VillagerTrades.ItemsForEmeralds(CobblemonItems.REVIVAL_HERB, 8, 1, 16, 15),
VillagerTrades.ItemsForEmeralds(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 4, 15)
)),
VillagerTradeOffer(CobblemonVillagerProfessions.NURSE, 5, listOf(
TradeOffer(ItemCost(Items.EMERALD, 10), ItemStack(CobblemonItems.ANTIDOTE, 1), 12, 30, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
TradeOffer(ItemCost(Items.EMERALD, 10), ItemStack(CobblemonItems.AWAKENING, 1), 12, 30, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
TradeOffer(ItemCost(Items.EMERALD, 10), ItemStack(CobblemonItems.BURN_HEAL, 1), 12, 30, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
TradeOffer(ItemCost(Items.EMERALD, 10), ItemStack(CobblemonItems.ICE_HEAL, 1), 12, 30, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
TradeOffer(ItemCost(Items.EMERALD, 10), ItemStack(CobblemonItems.PARALYZE_HEAL, 1), 12, 30, Optional.of<ItemCost>(ItemCost(Items.GLASS_BOTTLE))),
TradeOffer(ItemCost(Items.EMERALD, 22), ItemStack(CobblemonItems.VIVICHOKE_DIP, 1), 4, 30, Optional.of<ItemCost>(ItemCost(Items.BOWL)))
))
)
VillagerProfession.FISHERMAN -> listOf(
VillagerTradeOffer(VillagerProfession.FISHERMAN, 5, listOf(
@@ -53,7 +93,7 @@ object CobblemonTradeOffers {
)
/**
* Represents a trade offer that can be attached to a villager or a wandering trader.
* Represents trade offers that can be attached to a villager or a wandering trader.
*/
interface TradeOfferHolder {
/**
@@ -63,7 +103,7 @@ object CobblemonTradeOffers {
}
/**
* Represents a trade offer from a villager.
* Represents trade offers from a villager.
*
* @property profession The target profession for this trade.
* @property requiredLevel The level required must be a possible level.
@@ -75,7 +115,7 @@ object CobblemonTradeOffers {
val profession: VillagerProfession,
val requiredLevel: Int,
override val tradeOffers: List<VillagerTrades.ItemListing>
) : TradeOfferHolder {
): TradeOfferHolder {
init {
if (this.requiredLevel < VillagerData.MIN_VILLAGER_LEVEL || this.requiredLevel > VillagerData.MAX_VILLAGER_LEVEL) {
@@ -86,7 +126,7 @@ object CobblemonTradeOffers {
}
/**
* Represents a trade offer from a wandering trader.
* Represents trade offers from a wandering trader.
*
* @property isRareTrade If this is rare or common trade.
* @property tradeOffers The list of the possible [TradeOffers.Factory].
@@ -94,6 +134,26 @@ object CobblemonTradeOffers {
data class WandererTradeOffer(
val isRareTrade: Boolean,
override val tradeOffers: List<VillagerTrades.ItemListing>
) : TradeOfferHolder
): TradeOfferHolder
/**
* Represents a trade offer from a villager.
*
* @property offeredItem The item cost offered to the villager.
* @property receivedItem The item received from the villager.
* @property maxUses How many times the trade can be used before the villager resupplies.
* @property villagerXp The experience the villager gains from the trade.
* @property optionalOfferedItem The optional, secondary item cost offered to the villager.
* @property priceMultiplier The amount multiplier of how much the item cost can rise and fall by depending on discounts and penalties
*/
class TradeOffer(
private val offeredItem: ItemCost,
private val receivedItem: ItemStack,
private val maxUses: Int,
private val villagerXp: Int = 1,
private val optionalOfferedItem: Optional<ItemCost> = Optional.empty(),
private val priceMultiplier: Float = 0.05F
): VillagerTrades.ItemListing {
override fun getOffer(entity: Entity?, randomSource: RandomSource?): MerchantOffer = MerchantOffer(offeredItem, optionalOfferedItem, receivedItem, maxUses, villagerXp, priceMultiplier)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.ImmutableSet
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.entity.ai.village.poi.PoiType
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
object CobblemonVillagerPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Registry<PoiType>>, PoiType>() {
override val registry: Registry<PoiType> = BuiltInRegistries.POINT_OF_INTEREST_TYPE
override val resourceKey: ResourceKey<Registry<PoiType>> = Registries.POINT_OF_INTEREST_TYPE
@JvmField
val NURSE_KEY: ResourceKey<PoiType> = createKey("nurse")
@JvmField
val NURSE = create(NURSE_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.HEALING_MACHINE), 1, 1))
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
private fun getBlockStates(block: Block): Set<BlockState> = ImmutableSet.copyOf(block.stateDefinition.possibleStates)
}
@@ -0,0 +1,47 @@
/*
* 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.platform.PlatformRegistry
import com.google.common.collect.ImmutableSet
import net.minecraft.core.Holder
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.sounds.SoundEvent
import net.minecraft.world.entity.ai.village.poi.PoiType
import net.minecraft.world.entity.npc.VillagerProfession
object CobblemonVillagerProfessions: PlatformRegistry<Registry<VillagerProfession>, ResourceKey<Registry<VillagerProfession>>, VillagerProfession>() {
override val registry: Registry<VillagerProfession> = BuiltInRegistries.VILLAGER_PROFESSION
override val resourceKey: ResourceKey<Registry<VillagerProfession>> = Registries.VILLAGER_PROFESSION
@JvmField
val NURSE = profession(CobblemonVillagerPoiTypes.NURSE_KEY, CobblemonSounds.VILLAGER_WORK_NURSE)
/**
* Register a villager profession texture override for Cobblemon villagers of the given [VillagerProfession] for specified names.
*
* @param profession The Cobblemon [VillagerProfession] being overridden.
* @return The profession override texture file name and a list of names that will trigger the override, as a pair.
*/
fun getNameTagOverride(profession: VillagerProfession?): Pair<String, Array<String>>? = when (profession) {
NURSE -> Pair("nurse_joy", arrayOf("ジョーイ", "간호순", "祖兒", "喬伊", "乔伊", "האחות ג'וי", "الممرضة جوي", "Joy", "Joelle", "Joëlle", "Джой", "จอย"))
else -> null
}
private fun profession(resourceKey: ResourceKey<PoiType>, soundEvent: SoundEvent?): VillagerProfession =
create(resourceKey.location().path, VillagerProfession(
resourceKey.location().toString(),
{ holder: Holder<PoiType> -> holder.`is`(resourceKey) },
{ holder: Holder<PoiType> -> holder.`is`(resourceKey) },
ImmutableSet.of(), ImmutableSet.of(), soundEvent
))
}
@@ -21,20 +21,18 @@ class PokemonInteractContext(val type: ResourceLocation, val item: ResourceLocat
class PokemonInteractCriterion(
playerCtx: Optional<ContextAwarePredicate>,
val type: Optional<String>,
val item: Optional<String>
val type: String,
val item: String
): SimpleCriterionCondition<PokemonInteractContext>(playerCtx) {
companion object {
val CODEC: Codec<PokemonInteractCriterion> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(PokemonInteractCriterion::playerCtx),
Codec.STRING.optionalFieldOf("type").forGetter(PokemonInteractCriterion::type),
Codec.STRING.optionalFieldOf("item").forGetter(PokemonInteractCriterion::item)
Codec.STRING.optionalFieldOf("type", "any").forGetter(PokemonInteractCriterion::type),
Codec.STRING.optionalFieldOf("item", "any").forGetter(PokemonInteractCriterion::item)
).apply(it, ::PokemonInteractCriterion) }
}
override fun matches(player: ServerPlayer, context: PokemonInteractContext): Boolean {
val otherType = this.type.orElse("any")
val otherItem = this.item.orElse("any")
return (context.type == otherType.asIdentifierDefaultingNamespace() || otherType == "any") && (context.type == otherItem.asIdentifierDefaultingNamespace() || otherItem == "any")
return (context.type == this.type.asIdentifierDefaultingNamespace() || this.type == "any") && (context.item == this.item.asIdentifierDefaultingNamespace() || this.item == "any")
}
}
@@ -33,7 +33,6 @@ import java.io.PrintWriter
* @since April 1st, 2022.
*/
object JsonManifestWalker {
// im walkin ere
/**
* Builds a manifest at [manifestPath] using the files inside the same folder.
*
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.battles.model
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonNetwork
@@ -41,6 +42,8 @@ import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
@@ -90,6 +93,7 @@ open class PokemonBattle(
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
.forEach { it.reset() }
}
actor.setupStruct()
}
}
@@ -181,6 +185,11 @@ open class PokemonBattle(
*/
fun getActor(player: ServerPlayer) = actors.firstOrNull { it.isForPlayer(player) }
/**
* Gets the first battle actor whom the given NPC controls, or null if there is no such actor.
*/
fun getActor(npc: NPCEntity) = actors.firstOrNull { it is NPCBattleActor && it.npc == npc }
/**
* Gets a [BattleActor] and an [ActiveBattlePokemon] from a pnx key, e.g. p2a
*
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.battles.model.actor
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
@@ -34,6 +36,8 @@ abstract class BattleActor(
pokemonList.forEach { it.actor = this }
}
val struct = QueryStruct(hashMapOf())
lateinit var showdownId: String
lateinit var battle: PokemonBattle
@@ -134,6 +138,10 @@ abstract class BattleActor(
battle.writeShowdownAction(">$showdownId ${showdownMessages.joinToString()}")
}
open fun setupStruct() {
struct.addFunction("uuid") { StringValue(uuid.toString()) }
}
abstract fun getName(): MutableComponent
/**
@@ -139,7 +139,7 @@ open class PartySelectPokemonDTO(
aspects = pokemon.aspects,
heldItem = pokemon.heldItemNoCopy(),
currentHealth = pokemon.currentHealth,
maxHealth = pokemon.hp,
maxHealth = pokemon.maxHealth,
enabled = enabled
)
@@ -22,8 +22,10 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import java.util.UUID
import java.util.concurrent.CompletableFuture
import net.minecraft.server.level.ServerPlayer
/**
@@ -34,19 +36,31 @@ import net.minecraft.server.level.ServerPlayer
* @since December 27th, 2023
*/
class ActiveDialogue(var playerEntity: ServerPlayer, var dialogueReference: Dialogue) {
var initialized = false
val dialogueId = UUID.randomUUID()
val runtime = MoLangRuntime().setup()
var npc: NPCEntity? = null
var currentPage = dialogueReference.pages[0]
val playerStruct = playerEntity.asMoLangValue()
var activeInput = ActiveInput(this, currentPage.input)
init {
val completion = CompletableFuture<Unit>()
fun initialize() {
runtime.environment.query.addFunctions(
mapOf(
"dialogue" to java.util.function.Function { _ -> toMoLangStruct() },
"player" to java.util.function.Function { _ -> playerStruct }
)
)
dialogueReference.initializationAction.invoke(this)
if (!completion.isDone) {
val packet = DialogueOpenedPacket(this, true)
playerEntity.sendPacket(packet)
}
initialized = true
}
val currentPageIndex
@@ -85,7 +99,9 @@ class ActiveDialogue(var playerEntity: ServerPlayer, var dialogueReference: Dial
}
}
}
playerEntity.sendPacket(DialogueOpenedPacket(this, false))
if (initialized) {
playerEntity.sendPacket(DialogueOpenedPacket(this, false))
}
}
fun setPage(index: Int) {
@@ -114,7 +130,10 @@ class ActiveDialogue(var playerEntity: ServerPlayer, var dialogueReference: Dial
}
fun close() {
DialogueManager.stopDialogue(playerEntity)
if (initialized) {
DialogueManager.stopDialogue(playerEntity)
}
completion.complete(Unit)
}
fun escape() {
@@ -9,6 +9,8 @@
package com.cobblemon.mod.common.api.dialogue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
/**
* A dialogue that could be sent to players.
@@ -18,29 +20,39 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
*/
class Dialogue(
val pages: List<DialoguePage> = mutableListOf(),
val background: ResourceLocation = DEFAULT_BACKGROUND,
val escapeAction: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.close() },
val speakers: Map<String, DialogueSpeaker> = emptyMap()
val speakers: Map<String, DialogueSpeaker> = emptyMap(),
val initializationAction: DialogueAction = FunctionDialogueAction { _, _ -> }
) {
companion object {
val DEFAULT_BACKGROUND = cobblemonResource("textures/gui/dialogue/dialogue_box.png")
@JvmOverloads
fun of(
pages: Iterable<DialoguePage>,
background: ResourceLocation = DEFAULT_BACKGROUND,
escapeAction: ExpressionLike,
speakers: Map<String, DialogueSpeaker>
): Dialogue {
return Dialogue(
pages = pages.toList(),
escapeAction = ExpressionLikeDialogueAction(escapeAction),
background = background,
speakers = speakers
)
}
@JvmOverloads
fun of(
pages: Iterable<DialoguePage>,
background: ResourceLocation = DEFAULT_BACKGROUND,
escapeAction: (ActiveDialogue) -> Unit,
speakers: Map<String, DialogueSpeaker>
): Dialogue {
val dialogue = Dialogue(
pages = pages.toList(),
background = background,
escapeAction = FunctionDialogueAction { activeDialogue, _ -> escapeAction(activeDialogue) },
speakers = speakers
)
@@ -8,8 +8,10 @@
package com.cobblemon.mod.common.api.dialogue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.util.resolve
/**
@@ -39,6 +41,9 @@ class ExpressionLikeDialogueAction(val expression: ExpressionLike) : DialogueAct
if (input != null) {
dialogue.runtime.environment.setSimpleVariable("selected_option", StringValue(input))
}
dialogue.runtime.resolve(expression)
dialogue.runtime.resolve(
expression,
mapOf("player" to dialogue.playerStruct, "npc" to (dialogue.npc?.struct ?: QueryStruct(hashMapOf())))
)
}
}
@@ -20,6 +20,8 @@ import net.minecraft.resources.ResourceLocation
* @since January 1st, 2024
*/
sealed interface DialogueFaceProvider {
val isLeftSide: Boolean
companion object {
val types = mutableMapOf(
"player" to PlayerDialogueFaceProvider::class.java,
@@ -32,15 +34,24 @@ sealed interface DialogueFaceProvider {
class ArtificialDialogueFaceProvider(
val modelType: String = "",
val identifier: ResourceLocation = cobblemonResource("bulbasaur"),
val aspects: Set<String> = setOf()
val aspects: Set<String> = setOf(),
override val isLeftSide: Boolean = true
) : DialogueFaceProvider
class ReferenceDialogueFaceProvider(
val entityId: Int
val entityId: Int,
override val isLeftSide: Boolean = true
): DialogueFaceProvider
class PlayerDialogueFaceProvider(val playerId: UUID) : DialogueFaceProvider
/**
* A face provider that uses the player's skin as the face, as long as there is a player with this UUID online.
*
* What's interesting is that this works for fake players, which is what Taterzens' NPCs use.
*/
class PlayerDialogueFaceProvider(val playerId: UUID = UUID.randomUUID(), override val isLeftSide: Boolean = true) : DialogueFaceProvider
class ExpressionLikeDialogueFaceProvider(
val providerExpression: ExpressionLike
): DialogueFaceProvider
): DialogueFaceProvider {
override val isLeftSide: Boolean = false // Doesn't get used
}
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.util.activeDialogue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.util.withNPCValue
import java.util.UUID
import net.minecraft.server.level.ServerPlayer
@@ -28,20 +28,25 @@ import net.minecraft.server.level.ServerPlayer
object DialogueManager {
val activeDialogues = mutableMapOf<UUID, ActiveDialogue>()
fun startDialogue(playerEntity: ServerPlayer, dialogue: Dialogue) {
startDialogue(ActiveDialogue(playerEntity, dialogue))
fun startDialogue(playerEntity: ServerPlayer, dialogue: Dialogue): ActiveDialogue {
val activeDialogue = ActiveDialogue(playerEntity, dialogue)
startDialogue(activeDialogue)
return activeDialogue
}
fun startDialogue(playerEntity: ServerPlayer, npcEntity: NPCEntity, dialogue: Dialogue) {
fun startDialogue(playerEntity: ServerPlayer, npcEntity: NPCEntity, dialogue: Dialogue): ActiveDialogue {
val activeDialogue = ActiveDialogue(playerEntity, dialogue)
activeDialogue.runtime.withQueryValue("npc") { npcEntity.struct }
activeDialogue.runtime.withNPCValue("npc", npcEntity)
activeDialogue.npc = npcEntity
startDialogue(activeDialogue)
return activeDialogue
}
fun startDialogue(activeDialogue: ActiveDialogue) {
activeDialogues[activeDialogue.playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, true)
activeDialogue.playerEntity.sendPacket(packet)
activeDialogue.initialize()
if (!activeDialogue.completion.isDone) {
activeDialogues[activeDialogue.playerEntity.uuid] = activeDialogue
}
}
fun stopDialogue(playerEntity: ServerPlayer) {
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.dialogue.input.DialogueInput
import com.cobblemon.mod.common.api.dialogue.input.DialogueNoInput
import com.google.gson.JsonArray
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
/**
* A page of a dialogue. This has lines of text and also some kind of input expected from the player.
@@ -29,6 +30,7 @@ class DialoguePage(
var speaker: String? = null,
var lines: MutableList<DialogueText> = mutableListOf(),
var input: DialogueInput = DialogueNoInput(),
var background: ResourceLocation? = null,
var clientActions: MutableList<Expression> = mutableListOf(),
var escapeAction: DialogueAction? = null,
) {
@@ -40,6 +42,7 @@ class DialoguePage(
speaker: String? = null,
lines: Iterable<MutableComponent>,
input: DialogueInput = DialogueNoInput(),
background: ResourceLocation? = null,
clientActions: Iterable<Expression> = emptyList(),
/** The thing to do when the player presses ESC while on this page. If null, falls back to the same property on [Dialogue]. */
escapeAction: ((ActiveDialogue) -> Unit)? = null,
@@ -49,6 +52,7 @@ class DialoguePage(
speaker = speaker,
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
input = input,
background = background,
clientActions = clientActions.toMutableList(),
escapeAction = escapeAction?.let { func -> FunctionDialogueAction { activeDialogue, _ -> func(activeDialogue) } }
)
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.entity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.concurrent.CompletableFuture
/**
@@ -20,6 +21,6 @@ import java.util.concurrent.CompletableFuture
*
*/
interface PokemonSender {
fun sendingOut(): CompletableFuture<Unit>
fun sendingOut(pokemon: Pokemon): CompletableFuture<Unit>
fun recalling(pokemonEntity: PokemonEntity): CompletableFuture<Unit>
}
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.api.entity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.player.Player
interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
fun changePokemon(pokemon: Pokemon)
fun drop(source: DamageSource?) {}
fun updatePostDeath() {}
fun handleStatus(status: Byte) {}
fun spawnShinyParticle(player: Player) {}
}
@@ -204,7 +204,6 @@ fun drawString(
@JvmOverloads
fun drawPosablePortrait(
identifier: ResourceLocation,
aspects: Set<String>,
matrixStack: PoseStack,
scale: Float = 13F,
contextScale: Float = 1F,
@@ -228,20 +227,19 @@ fun drawPosablePortrait(
matrixStack.scale(scale, scale, -scale)
matrixStack.translate(0.0, -PORTRAIT_DIAMETER / 18.0, 0.0)
val sprite = repository.getSprite(identifier, aspects, SpriteType.PORTRAIT);
val sprite = repository.getSprite(identifier, state, SpriteType.PORTRAIT);
if (sprite == null) {
val model = repository.getPoser(identifier, aspects)
state.currentAspects = aspects
val model = repository.getPoser(identifier, state)
state.currentModel = model
val texture = repository.getTexture(identifier, aspects, state.animationSeconds)
val texture = repository.getTexture(identifier, state)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(identifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
repository.getTextureNoSubstitute(identifier, state).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, contextScale)
context.put(RenderContext.SPECIES, identifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.ASPECTS, state.currentAspects)
context.put(RenderContext.POSABLE_STATE, state)
val renderType = RenderType.entityCutout(texture)
@@ -274,7 +272,7 @@ fun drawPosablePortrait(
val packedLight = LightTexture.pack(11, 7)
val colour = toHex(r, g, b, a)
model.withLayerContext(immediate, state, repository.getLayers(identifier, aspects)) {
model.withLayerContext(immediate, state, repository.getLayers(identifier, state)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, colour)
immediate.endBatch()
}
@@ -292,7 +290,6 @@ fun drawPosablePortrait(
fun drawProfile(
repository: VaryingModelRepository<*>,
resourceIdentifier: ResourceLocation,
aspects: Set<String>,
matrixStack: PoseStack,
state: PosableState,
partialTicks: Float,
@@ -301,22 +298,20 @@ fun drawProfile(
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
val sprite = repository.getSprite(resourceIdentifier, aspects, SpriteType.PROFILE);
val sprite = repository.getSprite(resourceIdentifier, state, SpriteType.PROFILE)
if(sprite == null) {
if (sprite == null) {
val model = repository.getPoser(resourceIdentifier, aspects)
val texture = repository.getTexture(resourceIdentifier, aspects, state.animationSeconds)
val model = repository.getPoser(resourceIdentifier, state)
val texture = repository.getTexture(resourceIdentifier, state)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(resourceIdentifier, aspects, 0f)
.let { context.put(RenderContext.TEXTURE, it) }
repository.getTextureNoSubstitute(resourceIdentifier, state).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, 1F)
context.put(RenderContext.SPECIES, resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.ASPECTS, state.currentAspects)
context.put(RenderContext.POSABLE_STATE, state)
state.currentAspects = aspects
state.currentModel = model
val renderType = RenderType.entityCutout(texture)//model.getLayer(texture)
@@ -342,7 +337,7 @@ fun drawProfile(
RenderSystem.setShaderLights(light1, light2)
val packedLight = LightTexture.pack(11, 7)
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, aspects)) {
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, state)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
bufferSource.endBatch()
}
@@ -23,7 +23,7 @@ import com.mojang.serialization.Codec
interface ExpressionLike {
override fun toString(): String
/** Produces a [MoValue] for a [MoLangRuntime] to supply an environment. */
fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue> = emptyMap()): MoValue
fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue> = runtime.environment.context?.map ?: hashMapOf()): MoValue
fun getString(): String = toString()
fun resolveDouble(runtime: MoLangRuntime) = resolve(runtime).asDouble()
@@ -18,23 +18,25 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonActivities
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.worldRegistry
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import net.minecraft.client.gui.components.tabs.Tab
import java.util.UUID
import net.minecraft.commands.arguments.EntityAnchorArgument
import net.minecraft.core.Holder
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
@@ -44,10 +46,9 @@ import net.minecraft.nbt.ListTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceKey
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.commands.TagCommand
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.tags.TagKey
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
@@ -57,6 +58,8 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.dimension.DimensionType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -101,6 +104,12 @@ object MoLangFunctions {
runtime.environment.context = params.environment.context
val script = params.getString(0).asIdentifierDefaultingNamespace()
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
},
"run_command" to java.util.function.Function { params ->
val command = params.getString(0)
server()?.let {
return@Function DoubleValue(it.commands.dispatcher.execute(command, it.createCommandSourceStack()))
} ?: return@Function DoubleValue.ZERO
}
)
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -116,10 +125,11 @@ object MoLangFunctions {
map.put("save_data") { _ -> if (player is ServerPlayer) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) }
map.put("main_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.mainHandItem.item).asMoLangValue(Registries.ITEM) }
map.put("off_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.offhandItem.item).asMoLangValue(Registries.ITEM) }
map.put("face") { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
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) }
map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) }
// map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, Permission)) }
map.put("tell") { params ->
val message = params.getString(0).text()
val overlay = params.getBooleanOrNull(1) ?: false
@@ -141,12 +151,35 @@ object MoLangFunctions {
environment.query = player.asMoLangValue()
environment
}
if (player is ServerPlayer) {
map.put("is_party_at_full_health") { _ ->
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
map.put("can_heal_at_healer") { params ->
val pos = params.get<ArrayStruct>(0).asBlockPos()
val healer = player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
val party = player.party()
return@put DoubleValue(healer.canHeal(party))
}
map.put("put_pokemon_in_healer") { params ->
val healer = player.level()
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
.orElse(null) ?: return@put DoubleValue.ZERO
val party = player.party()
if (healer.canHeal(party)) {
healer.activate(player.uuid, party)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
}
map
}
)
val entityFunctions = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
map.put("damage") { params ->
val amount = params.getDouble(0)
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
@@ -178,15 +211,34 @@ object MoLangFunctions {
map.put("is_on_ground") { _ -> DoubleValue(entity.onGround()) }
map.put("world") { _ -> entity.level().worldRegistry.wrapAsHolder(entity.level()).asWorldMoLangValue() }
map.put("biome") { _ -> entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
map.put("is_healer_in_use") { params ->
val pos = params.get<ArrayStruct>(0).asBlockPos()
val healer = entity.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ONE
return@put DoubleValue(healer.isInUse)
}
map.put("find_nearby_block") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
val isTag = type.path.startsWith("#")
val range = params.getDoubleOrNull(1) ?: 10
val blockPos = entity.level().getBlockStatesWithPos(AABB.ofSize(entity.position(), range.toDouble(), range.toDouble(), range.toDouble()))
.filter { it.first.blockHolder.let { if (isTag) it.`is`(TagKey.create(Registries.BLOCK, type)) else it.`is`(type) } }
.minByOrNull { it.second.distSqr(entity.blockPosition()) }
?.second
if (blockPos != null) {
return@put ArrayStruct(mapOf("0" to DoubleValue(blockPos.x), "1" to DoubleValue(blockPos.y), "2" to DoubleValue(blockPos.z)))
} else {
return@put DoubleValue.ZERO
}
}
map
}
)
val npcFunctions = mutableListOf<(NPCEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ npc ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("class") { StringValue(npc.npc.resourceIdentifier.toString()) }
map.put("class") { StringValue(npc.npc.id.toString()) }
map.put("name") { StringValue(npc.name.string) }
map.put("face") { ObjectValue(ReferenceDialogueFaceProvider(npc.id)) }
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) }
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
map.put("run_script_on_client") { params ->
val world = npc.level()
@@ -199,13 +251,50 @@ object MoLangFunctions {
}
map.put("run_script") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val environment = params.environment
val runtime = MoLangRuntime()
runtime.environment.query = environment.query
runtime.environment.variable = environment.variable
runtime.environment.context = environment.context
runtime.environment.cloneFrom(params.environment)
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
map.put("run_action_effect") { params ->
val runtime = MoLangRuntime().setup()
runtime.environment.cloneFrom(params.environment)
runtime.withNPCValue(value = npc)
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
if (actionEffect != null) {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(NPCProvider(npc)),
runtime = runtime
)
npc.actionEffect = context
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
npc.brain.setActiveActivityIfPossible(CobblemonActivities.NPC_ACTION_EFFECT)
actionEffect.run(context).thenRun {
val npcActionEffect = npc.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
if (npcActionEffect == context && npc.brain.isActive(CobblemonActivities.NPC_ACTION_EFFECT)) {
npc.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
npc.actionEffect = null
}
}
return@put DoubleValue(1)
}
return@put DoubleValue(0)
}
map.put("put_pokemon_in_healer") { params ->
val healer = npc.level().getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
val party = npc.staticParty ?: return@put DoubleValue.ZERO
if (healer.canHeal(party)) {
healer.activate(npc.uuid, party)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("look_at_position") { params ->
val pos = Vec3(params.getDouble(0), params.getDouble(1), params.getDouble(2))
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
}
map.put("environment") { _ -> npc.runtime.environment }
map
}
@@ -220,6 +309,11 @@ object MoLangFunctions {
map.put("is_pvw") { DoubleValue(battle.isPvW) }
map.put("battle_type") { StringValue(battle.format.toString()) }
map.put("environment") { battle.runtime.environment }
map.put("get_actor") { params ->
val uuid = UUID.fromString(params.getString(0))
val actor = battle.actors.find { it.uuid == uuid } ?: return@put DoubleValue.ZERO
return@put actor.struct
}
map
}
)
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
/**
@@ -20,7 +21,7 @@ import com.bedrockk.molang.runtime.value.MoValue
class ObjectValue<T>(
var obj: T,
val stringify: (T) -> String = { it.toString() },
val doublify: (T) -> Double = { throw NotImplementedError("Doublify not implemented for this object value, are you doing weird molang") }
val doublify: (T) -> Double = { DoubleValue.ONE.value }
) : QueryStruct(hashMapOf()) {
override fun value() = this
override fun asDouble() = doublify(obj)
@@ -118,6 +118,8 @@ open class Move(
return json
}
fun copy() = loadFromJSON(saveToJSON(JsonObject()))
fun saveToBuffer(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(name)
buffer.writeSizedInt(IntSize.U_BYTE, currentPp)
@@ -54,7 +54,7 @@ class MoveSet : Iterable<Move> {
fun copyFrom(other: MoveSet) {
doWithoutEmitting {
clear()
other.getMoves().forEach { add(it) }
other.getMoves().forEach { add(it.copy()) }
}
update()
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.world.entity.Entity
@@ -38,7 +39,7 @@ class ActionEffectTimeline(
CompletableFuture.completedFuture(Unit)
} else {
val finalFuture = CompletableFuture<Unit>()
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
// .toList copy because I'm paranoid about iterators trying to share between identical effects playing
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
finalFuture
}.exceptionallyCompose {
@@ -71,8 +72,6 @@ class ActionEffectContext(
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
) {
inline fun <reified T> findOneProvider() = providers.filterIsInstance<T>().firstOrNull()
}
class UsersProvider(users: List<Entity>): EntityProvider {
@@ -85,6 +84,10 @@ class TargetsProvider(targets: List<Entity>): EntityProvider {
constructor(vararg targets: Entity): this(targets.toList())
}
class NPCProvider(npc: NPCEntity): EntityProvider {
override val entities = listOf(npc)
}
interface EntityProvider {
val entities: List<Entity>
}
@@ -31,7 +31,7 @@ class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityC
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val expressions = variables.map { it.originalString }.toSet()
val expressions = variables.map { it.originalString }
// Treat them as expressions if possible but otherwise yeah just send them as strings
val animation = animation.map {
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.entity.PosableEntity
import net.minecraft.world.entity.Entity
/**
@@ -24,12 +25,16 @@ import net.minecraft.world.entity.Entity
interface EntityConditionalActionEffectKeyframe {
val entityCondition: ExpressionLike
fun test(context: ActionEffectContext, entity: Entity, isUser: Boolean): Boolean {
// TODO this should use the entity's own query struct
context.runtime.environment.query.addFunction("entity") {
QueryStruct(hashMapOf())
.addFunction("uuid") { StringValue(entity.stringUUID) }
.addFunction("is_user") { DoubleValue(isUser) }
}
context.runtime.environment.query
.addFunction("entity") {
if (entity is PosableEntity) {
entity.struct
} else {
QueryStruct(hashMapOf())
}.addFunction("is_user") { DoubleValue(isUser) } // Deprecated tbh
}
.addFunction("is_user") { DoubleValue(isUser) }
return entityCondition.resolveBoolean(context.runtime)
}
}
@@ -45,7 +45,7 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
}?.asIdentifierDefaultingNamespace() ?: return skip()
entities.filter { it is PosableEntity }.forEach { entity ->
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locator)
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, listOf(locator))
val players = (entity.level() as ServerLevel).getPlayers { it.distanceTo(entity) <= visibilityRange }
packet.sendToPlayers(players)
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.scheduling.delayedFuture
@@ -56,16 +56,12 @@ abstract class MultiblockBlock(properties: Properties) : BaseEntityBlock(propert
return super.useWithoutItem(state, world, pos, player, hit)
}
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val result = super.playerWillDestroy(world, pos, state, player)
if (!world.isClientSide) {
val entity = world.getBlockEntity(pos)
if (entity is MultiblockEntity && entity.multiblockStructure != null) {
entity.multiblockStructure!!.playerWillDestroy(world, pos, state, player)
}
entity?.setRemoved()
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
val entity = level.getBlockEntity(pos)
if (entity is MultiblockEntity && entity.multiblockStructure != null && state.block != newState.block) {
entity.multiblockStructure!!.playerWillDestroy(level, pos, state, null)
}
return result
super.onRemove(state, level, pos, newState, movedByPiston)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
@@ -59,6 +59,8 @@ abstract class MultiblockEntity(
}
}
override fun isValidBlockState(blockState: BlockState) = blockState.block is MultiblockBlock
abstract override fun loadAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider)
}
@@ -0,0 +1,39 @@
/*
* 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.net.serializers
import com.cobblemon.mod.common.entity.npc.NPCPlayerModelType
import com.cobblemon.mod.common.entity.npc.NPCPlayerTexture
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readEnumConstant
import com.cobblemon.mod.common.util.writeEnumConstant
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.network.syncher.EntityDataSerializer
object NPCPlayerTextureSerializer : EntityDataSerializer<NPCPlayerTexture> {
val ID = cobblemonResource("npcplayertexture")
fun write(buffer: RegistryFriendlyByteBuf, texture: NPCPlayerTexture) {
buffer.writeEnumConstant(texture.model)
if (texture.model == NPCPlayerModelType.NONE) {
return
}
buffer.writeByteArray(texture.texture)
}
fun read(buffer: RegistryFriendlyByteBuf): NPCPlayerTexture {
val model = buffer.readEnumConstant(NPCPlayerModelType::class.java)
val texture = if (model == NPCPlayerModelType.NONE) ByteArray(1) else buffer.readByteArray()
return NPCPlayerTexture(texture, model)
}
override fun codec() = StreamCodec.of(::write, ::read)
override fun copy(texture: NPCPlayerTexture) = NPCPlayerTexture(texture.texture, texture.model)
}
@@ -11,13 +11,15 @@ package com.cobblemon.mod.common.api.npc
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.NPCBattleConfiguration
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable
import com.cobblemon.mod.common.api.npc.configuration.NPCInteractConfiguration
import com.cobblemon.mod.common.api.npc.variation.NPCVariationProvider
import com.cobblemon.mod.common.api.npc.variation.RandomNPCVariationProvider
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EntityDimensions
@@ -30,24 +32,49 @@ import net.minecraft.world.entity.EntityDimensions
*/
class NPCClass {
@Transient
lateinit var resourceIdentifier: ResourceLocation
lateinit var id: ResourceLocation
var names = mutableListOf(lang("npc.name.default"))
var resourceIdentifier: ResourceLocation = cobblemonResource("dummy")
var names: MutableList<Component> = mutableListOf()
var aspects: MutableSet<String> = mutableSetOf() // These only make sense when applied via presets
var hitbox = EntityDimensions.scalable(0.6F, 1.8F)
var battleConfiguration = NPCBattleConfiguration()
var aiScripts: MutableList<ResourceLocation> = mutableListOf()
var interaction: NPCInteractConfiguration? = null
var variables = mutableMapOf<String, MoValue>()
var variations: MutableMap<String, NPCVariationProvider> = mutableMapOf()
var config: MutableList<NPCConfigVariable> = mutableListOf()
var variables = mutableMapOf<String, MoValue>() // Questionable whether this should be here.
var party: NPCPartyProvider? = null
var skill: Int = 0
// If you're adding stuff here, add it to NPCPreset and NPCClassAdapter too
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(resourceIdentifier.toString())
buffer.writeCollection(names) { _, v -> buffer.writeText(v) }
buffer.writeFloat(this.hitbox.width)
buffer.writeFloat(this.hitbox.height)
buffer.writeBoolean(this.hitbox.fixed)
battleConfiguration.encode(buffer)
buffer.writeCollection(aiScripts) { _, v -> buffer.writeString(v.toString()) }
buffer.writeNullable(interaction) { _, value ->
buffer.writeString(value.type)
value.encode(buffer)
}
buffer.writeSizedInt(IntSize.U_BYTE, variations.size)
for ((key, value) in variations) {
buffer.writeString(key)
val aspects = value.aspects
buffer.writeCollection(aspects) { _, v -> buffer.writeString(v) }
}
buffer.writeCollection(config) { _, v ->
buffer.writeString(v.variableName)
buffer.writeText(v.displayName)
buffer.writeText(v.description)
buffer.writeEnumConstant(v.type)
buffer.writeString(v.defaultValue)
}
buffer.writeInt(skill)
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
buffer.writeString(key)
buffer.writeString(value.asString())
@@ -55,6 +82,7 @@ class NPCClass {
}
fun decode(buffer: RegistryFriendlyByteBuf) {
resourceIdentifier = ResourceLocation.parse(buffer.readString().toString())
names = buffer.readList { buffer.readText().copy() }.toMutableList()
val length = buffer.readFloat()
val width = buffer.readFloat()
@@ -62,6 +90,7 @@ class NPCClass {
hitbox = if (fixed) EntityDimensions.fixed(length, width) else EntityDimensions.scalable(length, width)
battleConfiguration = NPCBattleConfiguration()
battleConfiguration.decode(buffer)
aiScripts = buffer.readList { ResourceLocation.parse(buffer.readString()) }.toMutableList()
interaction = buffer.readNullable {
val type = buffer.readString()
val configType = NPCInteractConfiguration.types[type] ?: return@readNullable null
@@ -69,6 +98,22 @@ class NPCClass {
instance.decode(buffer)
instance
}
val variationSize = buffer.readSizedInt(IntSize.U_BYTE)
for (i in 0 until variationSize) {
val key = buffer.readString()
val aspects = buffer.readList { buffer.readString() }.toSet()
val provider = RandomNPCVariationProvider(aspects)
variations[key] = provider
}
config = buffer.readList {
val variableName = buffer.readString()
val displayName = buffer.readText()
val description = buffer.readText()
val type = buffer.readEnumConstant(NPCConfigVariable.NPCVariableType::class.java)
val defaultValue = buffer.readString()
NPCConfigVariable(variableName, displayName, description, type, defaultValue)
}.toMutableList()
skill = buffer.readInt()
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
val key = buffer.readString()
val value = buffer.readString()
@@ -18,6 +18,8 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.NPCInteractConfiguration
import com.cobblemon.mod.common.api.npc.variation.NPCVariationProvider
import com.cobblemon.mod.common.api.npc.variation.WeightedAspect
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
@@ -30,6 +32,7 @@ import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -57,7 +60,12 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(NPCVariationProvider::class.java, NPCVariationProviderAdapter)
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(NPCClass::class.java, NPCClassAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
.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)
@@ -95,7 +103,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
/**
* Finds an [NPCClass] by its unique [ResourceLocation].
*
* @param identifier The unique [NPCClass.resourceIdentifier] of the [NPCClass].
* @param identifier The unique [NPCClass.id] of the [NPCClass].
* @return The [NPCClass] if existing.
*/
fun getByIdentifier(identifier: ResourceLocation) = this.npcClassesByIdentifier[identifier]
@@ -116,10 +124,20 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
*/
fun random(): NPCClass = this.npcClassesByIdentifier.values.random()
fun dummy(): NPCClass {
val dummy = NPCClass()
dummy.id = cobblemonResource("dummy")
return dummy
}
override fun reload(data: Map<ResourceLocation, NPCClass>) {
this.npcClassesByIdentifier.clear()
data.forEach { (identifier, species) ->
species.resourceIdentifier = identifier
species.id = identifier
// shortcut so they don't have to state the resource identifier if they don't wanna
if (species.resourceIdentifier.path == "dummy") {
species.resourceIdentifier = identifier
}
this.npcClassesByIdentifier[identifier] = species
}
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.npc
import com.cobblemon.mod.common.api.npc.partyproviders.DynamicPartyProvider
import com.cobblemon.mod.common.api.npc.partyproviders.NPCParty
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
@@ -26,16 +28,12 @@ import net.minecraft.server.level.ServerPlayer
interface NPCPartyProvider {
companion object {
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
SimplePartyProvider.TYPE to { SimplePartyProvider() }
SimplePartyProvider.TYPE to { SimplePartyProvider() },
DynamicPartyProvider.TYPE to { DynamicPartyProvider() }
)
}
val type: String
fun provide(npc: NPCEntity, challengers: List<ServerPlayer>): PartyStore
fun encode(buffer: RegistryFriendlyByteBuf)
fun decode(buffer: RegistryFriendlyByteBuf)
fun saveToNBT(nbt: CompoundTag)
fun loadFromNBT(nbt: CompoundTag)
fun provide(npc: NPCEntity, level: Int): NPCParty
fun loadFromJSON(json: JsonElement)
}
@@ -8,11 +8,40 @@
package com.cobblemon.mod.common.api.npc
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable
import com.cobblemon.mod.common.api.npc.configuration.NPCInteractConfiguration
import com.cobblemon.mod.common.api.npc.variation.NPCVariationProvider
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EntityDimensions
class NPCPreset {
lateinit var id: ResourceLocation
var resourceIdentifier: ResourceLocation? = null
var aspects: Set<String>? = null
var variations: Map<String, NPCVariationProvider>? = null
var config: List<NPCConfigVariable>? = null
var party: NPCPartyProvider? = null
// var mergeMode = MergeMode.KEEP
var aiScripts: List<ResourceLocation>? = null
var interaction: NPCInteractConfiguration? = null
var names: MutableSet<Component>? = null
var hitbox: EntityDimensions? = null
var skill: Int? = null
fun applyTo(npcClass: NPCClass) {
resourceIdentifier?.let { npcClass.resourceIdentifier = it }
aspects?.let { npcClass.aspects.addAll(it) }
config?.forEach {
val variableName = it.variableName
npcClass.config.removeIf { it.variableName == variableName }
npcClass.config.add(it)
}
variations?.entries?.forEach { (key, value) -> npcClass.variations[key] = value }
party?.let { npcClass.party = it }
aiScripts?.let { npcClass.aiScripts.addAll(it) }
interaction?.let { npcClass.interaction = it }
names?.let { npcClass.names.addAll(it) }
hitbox?.let { npcClass.hitbox = it }
skill?.let { npcClass.skill = it }
}
}
@@ -0,0 +1,91 @@
/*
* 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.npc
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.ai.SleepDepth
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.NPCInteractConfiguration
import com.cobblemon.mod.common.api.npc.variation.NPCVariationProvider
import com.cobblemon.mod.common.api.npc.variation.WeightedAspect
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.item.Item
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.phys.AABB
object NPCPresets : JsonDataRegistry<NPCPreset> {
override val id = cobblemonResource("npc_preset")
override val type = PackType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
.registerTypeAdapter(AABB::class.java, BoxAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(NPCVariationProvider::class.java, NPCVariationProviderAdapter)
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
.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(Either::class.java, ResourceLocation::class.java, ExpressionLike::class.java).type, NPCScriptAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
override val typeToken: TypeToken<NPCPreset> = TypeToken.get(NPCPreset::class.java)
override val resourcePath = "npc_presets"
override val observable = SimpleObservable<NPCPresets>()
private val npcPresetsByIdentifier = mutableMapOf<ResourceLocation, NPCPreset>()
override fun sync(player: ServerPlayer) {
// TODO probably do want to sync the presets
}
override fun reload(data: Map<ResourceLocation, NPCPreset>) {
npcPresetsByIdentifier.clear()
npcPresetsByIdentifier.putAll(data)
observable.emit(this)
}
fun getPreset(identifier: ResourceLocation): NPCPreset? {
return npcPresetsByIdentifier[identifier]
}
}
@@ -0,0 +1,273 @@
# Cobbled NPCs
NPC's are AI-driven entities that are built on the same identifier-aspects rendering platform
as Cobblemon's Pokémon entities. Every NPC in the world is based on an [NPC class](#npc-classes).
## NPC Classes
An NPC class is a datapacked JSON (inside the <code>npcs</code> folder) that defines many properties of an NPC in a way that's reusable
across any number of individual NPC entities. The possible properties are explained in [NPC Properties](#npc-properties)
## NPC Class Presets
For many properties or sets of properties of an NPC class, there may be logical grouping of settings.
As an example, many types of trainer NPC classes could share the logic of challenging nearby players
that they are able to see. This could become tedious if the same AI script and range config needed to
be defined on every NPC class. Not only is it tedious, but if there was some tweak that was made in the
underlying logic of that AI, it would need to be updated in every single NPC class JSON that uses it.
The solution to this is NPC Class Presets, a datapack folder (<code>npc_presets</code>) with JSON files that have an almost identical
format to [NPC Classes](#npc-classes), but with every property being nullable. An NPC class can reference
any number of presets and the properties of those presets will be merged into the NPC class as it loads.
## NPC Properties
{
"hitbox": "player",
"resourceIdentifier": "cobblemon:bug_catcher",
"presets": [
"cobblemon:npc-standard"
],
"variations": {
"dirt": [
"clean",
"slightly_dirty",
"dirty",
"filthy"
],
"net": {
"type": "weighted",
"options": [
{
"aspect": "green-net",
"weight": 10
},
{
"aspect": "blue-net",
"weight": 5
},
{
"aspect": "red-net",
"weight": 1
}
]
}
},
"config": [
{
"variableName": "challenge_distance",
"displayName": "cobblemon.npc.challenge_distance",
"description": "cobblemon.npc.challenge_distance.description",
"type": "number",
"default": "5"
}
],
"names": [
"Vera",
"Hiro",
"John Cobblemon"
],
"interaction": {
"type": "dialogue",
"dialogue": "cobblemon:npc-example"
},
"battleConfiguration": {
"canChallenge": true
},
"skill": 5,
"party": {
"type": "simple",
"pokemon": [
"spiritomb level=1 moves=splash,firepunch,flamethrower,swift",
"porygonz level=1 speed_ev=252",
"togekiss level=1 nature=timid",
"lucario level=1 held_item=cobblemon:focus_sash",
"milotic level=1 ability=marvelscale ",
"garchomp level=1 hp_iv=31 attack_iv=31"
]
}
}
### hitbox
The hitbox of the NPC, relevant to combat and pathing. This can be defined as an object
or as a shortcut string that is predefined in the code. The only currently hardcoded
hitbox name is "player", which is the standard Steve Minecraft hitbox and is the same as
this fully defined hitbox:
"hitbox": {
"width": 0.6,
"height": 1.8
}
### resourceIdentifier
An optional property representing the 'identifier' that will be used for rendering the NPC.
In a way, this is the same as the "species" of a Pokémon. It is this value combined with
whatever aspects have been defined for the NPC that completely handles the appearance of the
NPC. This identifier is referenced in the variation JSONs on the client.
If left blank, it will fallback to the identifier used for the NPC class itself. The reason why
this is useful is that it allows several NPC classes to appear the same despite having differences
in AI, parties, interactions, etc.
### presets
A list of strings representing the identifiers of [NPC Class Presets](#npc-class-presets) that form the foundation of
this NPC class. Any property defined in this class will override the same property in the presets.
### variations
A map of variations that generate the aspects that control the appearance of the NPC. The keys of this
map should represent the kind of variation that is being defined, and the values are some kind of [NPC Variation](#npc-variation).
Variations can be defined in presets and flow through to the NPC class, but they can also be defined
in the NPC class. If the same name is used in both a preset and an NPC class, that which is on the NPC
class will override that from the preset. If the variation names are different, they will be merged together.
#### NPC Variation
An NPC variation is some way in which aspects can be generated for the NPC. The types of variation can
be added using the API, but the type most anticipated is the weighted choice aspects.
This can be defined as a list of strings, in which case the aspect will be chosen randomly from the list
with uniform distribution.
"dirt": [
"clean",
"slightly_dirty",
"dirty",
"filthy"
]
The weighted variation can also be defined more fully, which is necessary if you want some options to be
more likely than others.
"net": {
"type": "weighted",
"options": [
{
"aspect": "green-net",
"weight": 10
},
{
"aspect": "blue-net",
"weight": 5
},
{
"aspect": "red-net",
"weight": 1
}
]
}
In this example, the "net" value will be "green-net" most of the time, 10 times out of 16. The "blue-net"
aspect will be chosen 5 times out of 16, and the "red-net" aspect will be chosen 1 time out of 16.
When using a type of NPC variation that is not "weighted", the value of "type" must specify it and the
other values (instead of "options") will be in whatever format is dictated by that type.
### config
A list of NPC config values that are predefined as being necessary for this class. An NPC config
variable's value is used in the MoLang environment of the NPC and can be accessed in any MoLang
environment that has the NPC.
This class property is a way to present clear options to the user if configuring an NPC in-game using
the NPC Edit command. This does not mean that NPC variables can only be created when they were defined
in the class, but it makes it easier for the user to know what variables should be set and will set
the initial values when an NPC is initialized.
An example situation of using this is if there is an NPC class preset that defines an AI script for
challenging a player when they come within a certain range. Inside that class preset, the AI script may
need to know at what distance the player will be engaged. This can be defined in the NPC class preset as
a config variable, and therefore any NPC class that relies on this preset will have that config variable
and present it to users when they setup an NPC of that class.
"config": [
{
"variableName": "challenge_distance",
"displayName": "cobblemon.npc.challenge_distance",
"description": "cobblemon.npc.challenge_distance.description",
"type": "number",
"default": "5"
}
]
The "type" of variable is one of: `string`, `number`, or `boolean`. The type is used to preset a more
appropriate input field for the user when they are setting up the NPC.
### names
A list of strings that represent the names that the NPC can have. The name of the NPC is chosen randomly
when initialized. These can either be simple names or translation keys so provide localization support.
### interaction
An object that defines how the NPC will handle being interacted with by players. The type of interaction
is defined by the "type" property, and different types of interaction have different properties.
#### dialogue
The "dialogue" interaction type is a simple way to have an NPC say something when interacted with. The
"dialogue" property should be the identifier of a datapacked dialogue JSON file. The dialogue that is
created will have `c.npc` as a contextual property in any script that occurs as part of that dialogue,
allowing the dialogue to work with the NPC (including the NPC config values, such as
`c.npc.config.challenge_distance`).
"interaction": {
"type": "dialogue",
"dialogue": "cobblemon:npc-example"
}
#### script
The "script" interaction type is a way to have an NPC run a predefined script when interacted with. The
script field is an identifier of a datapacked MoLang script file (in the `scripts` folder).
"interaction": {
"type": "script",
"script": "cobblemon:npc-example"
}
#### custom_script
The "custom_script" interaction type is a way to have an NPC run the MoLang script defined specifically
in this file. The script property can be a single string or an array of strings to do multiline MoLang scripts.
"interaction": {
"type": "custom_script",
"script": "c.player.tell('My name is ' + c.npc.name);"
}
### battleConfiguration
I don't really know whether this is going to stay tbh
### skill
The skill level when this NPC is used in battle. This is a number from 1 to 5, with 5 being most difficult
to beat. This does not control their Pokémon's levels or moves, but it does control how well they use them.
### party
A party provider which generates the party of Pokémon that this NPC will have when in battle. The type of
party is defined by the "type", and different types of party have different properties. An NPC party is
intended to be initialized when an NPC is spawned and uses a 'seed level'. When spawned using the world
spawning system, it will use a seed level that is based on the player's party's average level. When
spawned using the NPC command, the seed level can be defined in the command and is otherwise set to 1.
A party provider produces either 'static' or 'dynamic' parties. A static party is one that is generated
when the NPC is initialized and will not change. A dynamic party is one that may be configured using the
seed level but can be different any time the NPC is challenged.
A dynamic party prevents the NPC from doing several actions, such as using healing machines, having
persistent health of their party that changes between battles, and keeping their Pokémon out of their
balls. This is because the NPC's party is not stored in the world.
#### simple
The "simple" party type is a way to define a party of Pokémon in a simple list format. Each string in the
list should be a set of Pokémon properties, as used in commands and spawn files.
This party provider type is considered 'static'. Any level that is not mentioned in the Pokémon properties
will be set to the seed level.
"party": {
"type": "simple",
"pokemon": [
"spiritomb moves=splash,firepunch,flamethrower,swift",
"porygonz speed_ev=252",
"togekiss nature=timid",
"lucario held_item=cobblemon:focus_sash",
"milotic ability=marvelscale ",
"garchomp level=100 hp_iv=31 attack_iv=31"
]
}
#### dynamic
To be implemented
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.npc.configuration
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -25,50 +23,50 @@ class NPCBattleConfiguration {
buffer.writeBoolean(canChallenge)
buffer.writeBoolean(simultaneousBattles)
buffer.writeBoolean(healAfterwards)
buffer.writeNullable(party) { _, provider ->
buffer.writeString(provider.type)
provider.encode(buffer)
}
// buffer.writeNullable(party) { _, provider ->
// buffer.writeString(provider.type)
// provider.encode(buffer)
// }
}
fun decode(buffer: RegistryFriendlyByteBuf) {
canChallenge = buffer.readBoolean()
simultaneousBattles = buffer.readBoolean()
healAfterwards = buffer.readBoolean()
party = buffer.readNullable {
val type = buffer.readString()
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
val decodedParty = providerBuilder(type)
decodedParty.decode(buffer)
decodedParty
}
// party = buffer.readNullable {
// val type = buffer.readString()
// val providerBuilder = NPCPartyProvider.types[type]
// ?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
//
// val decodedParty = providerBuilder(type)
// decodedParty.decode(buffer)
// decodedParty
// }
}
fun saveToNBT(nbt: CompoundTag) {
nbt.putBoolean(DataKeys.NPC_CAN_CHALLENGE, canChallenge)
nbt.putBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES, simultaneousBattles)
nbt.putBoolean(DataKeys.NPC_HEAL_AFTERWARDS, healAfterwards)
val party = party
if (party != null) {
val partyNBT = CompoundTag()
partyNBT.putString(DataKeys.NPC_PARTY_TYPE, party.type)
party.saveToNBT(partyNBT)
nbt.put(DataKeys.NPC_PARTY, partyNBT)
}
// val party = party
// if (party != null) {
// val partyNBT = CompoundTag()
// partyNBT.putString(DataKeys.NPC_PARTY_TYPE, party.type)
// party.saveToNBT(partyNBT)
// nbt.put(DataKeys.NPC_PARTY, partyNBT)
// }
}
fun loadFromNBT(nbt: CompoundTag) {
canChallenge = nbt.getBoolean(DataKeys.NPC_CAN_CHALLENGE)
simultaneousBattles = nbt.getBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES)
healAfterwards = nbt.getBoolean(DataKeys.NPC_HEAL_AFTERWARDS)
val partyNBT = nbt.getCompound(DataKeys.NPC_PARTY)
if (!partyNBT.isEmpty) {
val type = partyNBT.getString(DataKeys.NPC_PARTY_TYPE)
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
party = providerBuilder(type).also { it.loadFromNBT(partyNBT) }
}
// val partyNBT = nbt.getCompound(DataKeys.NPC_PARTY)
// if (!partyNBT.isEmpty) {
// val type = partyNBT.getString(DataKeys.NPC_PARTY_TYPE)
// val providerBuilder = NPCPartyProvider.types[type]
// ?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
// party = providerBuilder(type).also { it.loadFromNBT(partyNBT) }
// }
}
}
@@ -0,0 +1,51 @@
/*
* 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.npc.configuration
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.network.chat.Component
/**
* A predefined variable that will be declared and filled in on any NPC that extends from the class or preset
* that specifies this variable. This gives the client a cleaner way to represent a variable that should exist
* on the NPC.
*
* @author Hiroku
* @since August 14th, 2023
*/
class NPCConfigVariable(
val variableName: String = "variable",
val displayName: Component = "Variable".asTranslated(),
val description: Component = "A variable that can be used in the NPC's configuration.".asTranslated(),
val type: NPCVariableType = NPCVariableType.NUMBER,
val defaultValue: String = "0",
) {
enum class NPCVariableType {
NUMBER,
STRING,
BOOLEAN
}
fun apply(npc: NPCEntity, value: MoValue) {
npc.config.setDirectly(variableName, value)
}
fun applyDefault(npc: NPCEntity) {
val value = when (type) {
NPCVariableType.STRING -> StringValue(defaultValue)
NPCVariableType.BOOLEAN -> DoubleValue(defaultValue.toBoolean())
else -> DoubleValue(defaultValue.toDouble())
}
npc.config.set(variableName.split(".").iterator(), value)
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.npc.configuration.interaction
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.dialogue.DialogueManager
import com.cobblemon.mod.common.api.dialogue.Dialogues
@@ -31,10 +32,24 @@ class DialogueNPCInteractionConfiguration : NPCInteractConfiguration {
override fun interact(npc: NPCEntity, player: ServerPlayer): Boolean {
val dialogue = Dialogues.dialogues[this.dialogue] ?: return false
DialogueManager.startDialogue(ActiveDialogue(player, dialogue))
val currentDialogues = npc.brain.getMemory(CobblemonMemories.DIALOGUES).orElse(mutableListOf())
val activeDialogue = DialogueManager.startDialogue(player, npc, dialogue)
val newDialogues = currentDialogues + activeDialogue
npc.brain.setMemory(CobblemonMemories.DIALOGUES, newDialogues)
activeDialogue.completion.thenRun { onDialogueStopped(npc, activeDialogue) }
return true
}
fun onDialogueStopped(npc: NPCEntity, activeDialogue: ActiveDialogue) {
val currentDialogues = npc.brain.getMemory(CobblemonMemories.DIALOGUES).orElse(mutableListOf())
val newDialogues = currentDialogues - activeDialogue
if (newDialogues.isEmpty()) {
npc.brain.eraseMemory(CobblemonMemories.DIALOGUES)
} else {
npc.brain.setMemory(CobblemonMemories.DIALOGUES, newDialogues)
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(dialogue)
}
@@ -0,0 +1,52 @@
/*
* 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.npc.partyproviders
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.JsonElement
import net.minecraft.resources.ResourceLocation
/**
* A provider of a party for battling the NPC. It generates a [StaticNPCParty] but using a more complex process
* that takes an input level and makes a randomized selection of Pokémon and movesets.
*
* @author Hiroku
* @since July 12th, 2024
*/
class DynamicPartyProvider : NPCPartyProvider {
companion object {
val TYPE = "dynamic"
}
class DynamicPool {
val id = ResourceLocation.parse("cobblemon:pool")
val pokemon: MutableList<DynamicPokemon> = mutableListOf()
}
class DynamicPokemon(
val pokemon: PokemonProperties,
val possibleLevels: IntRange,
val selectableTimes: IntRange,
val guaranteed: Boolean = false
)
override val type = "dynamic"
override fun loadFromJSON(json: JsonElement) {
TODO("Not yet implemented")
}
override fun provide(npc: NPCEntity, level: Int): NPCParty {
TODO("Not yet implemented")
}
}
@@ -0,0 +1,91 @@
/*
* 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.npc.partyproviders
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import net.minecraft.core.RegistryAccess
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
/**
* Represents a party of Pokemon that an NPC can have. This isn't directly a party of Pokémon so that
* a dynamically generated party can exist where a party is generated at the time of a challenge.
*
* This interface is sealed. Implementations should be either [StaticNPCParty] or [DynamicNPCParty].
* If you want a custom type then you should implement [DynamicNPCParty] and register it in the
* [DynamicNPCParty.types] map.
*
* @author Hiroku
* @since July 12th, 2024
*/
sealed interface NPCParty {
fun getParty(player: ServerPlayer, npc: NPCEntity): PartyStore?
fun encode(buffer: RegistryFriendlyByteBuf)
fun decode(buffer: RegistryFriendlyByteBuf)
fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
}
/**
* A simple wrapping around a [NPCPartyStore]. This is a straightforward party that is always the same.
*
* @author Hiroku
* @since July 12th, 2024
*/
class StaticNPCParty(val party: NPCPartyStore) : NPCParty {
override fun getParty(player: ServerPlayer, npc: NPCEntity) = party
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, party.size())
for (pokemon in party) {
Pokemon.S2C_CODEC.encode(buffer, pokemon)
}
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
party.add(Pokemon.S2C_CODEC.decode(buffer))
}
}
override fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
for ((index, pokemon) in party.withIndex()) {
nbt.put(DataKeys.NPC_PARTY_POKEMON + index, pokemon.saveToNBT(registryAccess))
}
}
override fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
var index = 0
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
this.party.add(Pokemon().loadFromNBT(registryAccess, nbt.getCompound(DataKeys.NPC_PARTY_POKEMON + index)))
index++
}
}
}
/**
* A dynamic party that is generated at the time of a challenge.
*
* @author Hiroku
* @since July 12th, 2024
*/
interface DynamicNPCParty : NPCParty {
companion object {
val types = mutableMapOf<String, Class<out NPCParty>>()
init {}
}
}
@@ -11,16 +11,16 @@ package com.cobblemon.mod.common.api.npc.partyproviders
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.toProperties
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
/**
* A basic party provider that just produces a [StaticNPCParty] based on a list of [PokemonProperties].
*
* @author Hiroku
* @since August 19th, 2023
*/
class SimplePartyProvider : NPCPartyProvider {
companion object {
const val TYPE = "simple"
@@ -31,45 +31,26 @@ class SimplePartyProvider : NPCPartyProvider {
val pokemon = mutableListOf<PokemonProperties>()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, pokemon.size)
for (pokemon in this.pokemon) {
buffer.writeString(pokemon.originalString)
}
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
pokemon.add(PokemonProperties.parse(buffer.readString()))
}
}
override fun saveToNBT(nbt: CompoundTag) {
for ((index, pokemon) in this.pokemon.withIndex()) {
nbt.putString(DataKeys.NPC_PARTY_POKEMON + index, pokemon.originalString)
}
}
override fun loadFromNBT(nbt: CompoundTag) {
var index = 0
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
this.pokemon.add(PokemonProperties.parse(nbt.getString(DataKeys.POKEMON_PROPERTIES + index)))
index++
}
}
override fun loadFromJSON(json: JsonElement) {
json as JsonObject
if (json.has(DataKeys.NPC_PARTY_POKEMON.lowercase())) {
json.get(DataKeys.NPC_PARTY_POKEMON.lowercase()).asJsonArray.forEach { pokemon.add(PokemonProperties.parse(it.asString)) }
val pokemonList = json.asJsonObject.getAsJsonArray("pokemon")
pokemonList.forEach {
val pokemon = if (it.isJsonPrimitive) {
it.asString.toProperties()
} else {
val obj = it.asJsonObject
val props = PokemonProperties()
props.loadFromJSON(obj)
props
}
this.pokemon.add(pokemon)
}
}
override fun provide(npc: NPCEntity, challengers: List<ServerPlayer>): PartyStore {
return NPCPartyStore(npc).apply {
for (properties in pokemon) {
add(properties.create())
}
}
override fun provide(npc: NPCEntity, level: Int): NPCParty {
val pokemon = pokemon.map { it.copy().also { it.level = it.level ?: level }.create() }
val party = NPCPartyStore(npc)
pokemon.forEach(party::add)
return StaticNPCParty(party)
}
}
@@ -0,0 +1,41 @@
/*
* 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.npc.variation
import com.cobblemon.mod.common.entity.npc.NPCEntity
// TODO probably need an exclusion list of aspects to prevent double application
// also some way to add them from a command?
/**
* An NPC variation provider happens at the moment of generating a particular NPC and works by appending to the
* aspects list that gets locked onto the NPC. Then ideally they can be tweaked by an editor or code after they've
* been put on the NPC. Retaining knowledge of the variation provider might be necessary for presenting a clear
* way of moving between different options but might be out of scope.
*
* @author Hiroku
* @since August 10th, 2024
*/
interface NPCVariationProvider {
/** All of the possible aspects that could be provided. */
val aspects: Set<String>
/** Provides some number of aspects for the given entity. */
fun provideAspects(npcEntity: NPCEntity): Set<String>
companion object {
val types = mutableMapOf(
"weighted" to WeightedNPCVariationProvider::class.java,
"random" to RandomNPCVariationProvider::class.java
)
fun register(type: String, clazz: Class<out NPCVariationProvider>) {
types[type] = clazz
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc.variation
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* A super basic random NPC variation provider but is mainly used to simply communicate variations to the client.
*
* @author Hiroku
* @since August 14th, 2024
*/
class RandomNPCVariationProvider(
override val aspects: Set<String> = emptySet()
) : NPCVariationProvider {
override fun provideAspects(npcEntity: NPCEntity) = setOf(aspects.random())
}
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc.variation
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.weightedSelection
/**
* An element of a [WeightedNPCVariationProvider] that provides a set of aspects with a weight.
*
* @author Hiroku
* @since August 11th, 2024
*/
class WeightedAspect(val aspects: Set<String>, val weight: Double)
/**
* A variation provider that provides a set of aspects with a weight. When providing for an entity,
* a simple weighted selection is done to determine which aspects to apply.
*
* @author Hiroku
* @since August 11th, 2024
*/
class WeightedNPCVariationProvider : NPCVariationProvider {
var options: List<WeightedAspect> = emptyList()
override val aspects: Set<String>
get() = options.flatMap(WeightedAspect::aspects).toSet()
override fun provideAspects(npcEntity: NPCEntity) = options.weightedSelection(WeightedAspect::weight)?.aspects ?: emptySet()
}
@@ -9,10 +9,11 @@
package com.cobblemon.mod.common.api.pasture
import com.cobblemon.mod.common.Cobblemon
import net.minecraft.core.BlockPos
import net.minecraft.resources.ResourceLocation
import java.util.*
import net.minecraft.core.BlockPos
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.ResourceLocation
class PastureLink(val linkId: UUID, val pcId: UUID, val dimension: ResourceLocation, val pos: BlockPos, val permissions: PasturePermissions) {
fun getPC() = Cobblemon.storage.getPC(pcId)
fun getPC(registryAccess: RegistryAccess) = Cobblemon.storage.getPC(pcId, registryAccess)
}
@@ -46,6 +46,7 @@ object CobblemonPermissions {
val GIVE_ALL_POKEMON = this.create("${COMMAND_PREFIX}giveallpokemon", PermissionLevel.ALL_COMMANDS)
val SPAWN_POKEMON = this.create("${COMMAND_PREFIX}spawnpokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val SPAWN_NPC = this.create("${COMMAND_PREFIX}spawnnpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val STOP_BATTLE = this.create("${COMMAND_PREFIX}stopbattle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -74,6 +75,8 @@ object CobblemonPermissions {
val POKEDEX = this.create("${COMMAND_PREFIX}pokedex", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val FREEZE_POKEMON = this.create("${COMMAND_PREFIX}freezepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val APPLY_PLAYER_TEXTURE = this.create("${COMMAND_PREFIX}applyplayertexture", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val ABANDON_MULTITEAM = this.create("${COMMAND_PREFIX}abandonmultiteam", PermissionLevel.NONE)
@@ -172,8 +172,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("net_ball", CatchRateModifiers.typeBoosting(3F, ElementalTypes.BUG, ElementalTypes.WATER))
createDefault("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
createDefault("nest_ball", CatchRateModifiers.NEST)
// ToDo implement effect once pokedex is implemented, we have a custom multiplier of 2.5 instead of the official pokeball
createDefault("repeat_ball")
createDefault("repeat_ball", CatchRateModifiers.REPEAT)
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F * turn * (1229F / 4096F)).coerceAtMost(4F) })
createDefault("luxury_ball", effects = listOf(FriendshipEarningBoostEffect(2F)))
createDefault("premier_ball")
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokedex.CaughtCount
import net.minecraft.server.level.ServerPlayer
import kotlin.math.roundToInt
import kotlin.random.Random
import net.minecraft.server.level.ServerPlayer
/**
* A provider for the possibility at a critical capture.
@@ -29,8 +31,7 @@ interface CriticalCaptureProvider {
* @return If the critical capture should occur
*/
fun shouldHaveCriticalCapture(player: ServerPlayer, modifiedCatchRate: Float): Boolean {
// ToDo once pokedex is implemented change number here
val caughtCount = 0
val caughtCount = Cobblemon.playerDataManager.getPokedexData(player).getGlobalCalculatedValue(CaughtCount)
val caughtMultiplier = when {
caughtCount <= 30 -> 0F
// This one is exact
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokedex.CaughtCount
import net.minecraft.server.level.ServerPlayer
/**
@@ -25,8 +27,7 @@ interface PokedexProgressCaptureMultiplierProvider {
* @return The multiplier based on the caught count.
*/
fun caughtMultiplierFor(player: ServerPlayer): Float {
// ToDo once pokedex is implemented change number here
val caughtCount = 0
val caughtCount = Cobblemon.playerDataManager.getPokedexData(player).getGlobalCalculatedValue(CaughtCount)
return when {
caughtCount < 30 -> 1229F / 4096F
// This one is exact
@@ -8,10 +8,14 @@
package com.cobblemon.mod.common.api.pokeball.catching.modifiers
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -98,8 +102,8 @@ object CatchRateModifiers {
*/
val LIGHT_LEVEL = WorldStateModifier { _, entity ->
when (entity.level().getMaxLocalRawBrightness(entity.blockPosition())) {
0 -> 3F
in 1..7 -> 1.5F
0 -> 3.5F
in 1..7 -> 3F
else -> 1F
}
}
@@ -148,6 +152,21 @@ object CatchRateModifiers {
}
}) { _, pokemon -> pokemon.form.weight >= 1000F }
/**
* Used by [PokeBalls.REPEAT_BALL].
* Checks if the entity is registered as caught in the thrower's pokedex data.
* If yes boosts the catch rate by *2.5
*/
val REPEAT: CatchRateModifier = WorldStateModifier { thrower, pokemon ->
val playerDexData = Cobblemon.playerDataManager.getPokedexData(thrower.uuid)
val speciesId = pokemon.pokemon.species.resourceIdentifier
val knowledge = playerDexData.getKnowledgeForSpecies(speciesId)
when (knowledge) {
PokedexEntryProgress.CAUGHT -> 3.5F
else -> 1F
}
}
/**
* Used by [PokeBalls.NET_BALL].
* Boosts the catch rate if the target is of the given types.
@@ -179,5 +198,4 @@ object CatchRateModifiers {
val battle = BattleRegistry.getBattleByParticipatingPlayer(player) ?: return@BattleModifier 1F
multiplierCalculator.invoke(battle.turn)
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.resources.ResourceLocation
@@ -46,6 +47,11 @@ abstract class AbstractPokedexManager {
}
}
fun getHighestKnowledgeForSpecies(pokemon: Pokemon): PokedexEntryProgress {
val speciesRecord = getSpeciesRecord(pokemon.species.resourceIdentifier)
return speciesRecord?.getKnowledge() ?: PokedexEntryProgress.NONE
}
fun getHighestKnowledgeFor(entry: PokedexEntry): PokedexEntryProgress {
val speciesRecord = getSpeciesRecord(entry.speciesId) ?: return PokedexEntryProgress.NONE
val hasAllAspects = entry.conditionAspects.all(speciesRecord::hasAspect)
@@ -80,6 +86,21 @@ abstract class AbstractPokedexManager {
}
}
fun getNewInformation(pokemon: Pokemon): PokedexLearnedInformation {
val speciesRecord = getSpeciesRecord(pokemon.species.resourceIdentifier)
if (speciesRecord == null || speciesRecord.getKnowledge() == PokedexEntryProgress.NONE) {
return PokedexLearnedInformation.SPECIES
}
val formRecord = speciesRecord.getFormRecord(pokemon.form.name)
if (formRecord == null || formRecord.knowledge == PokedexEntryProgress.NONE) {
return PokedexLearnedInformation.FORM
}
if (pokemon.aspects.none(speciesRecord::hasAspect) || pokemon.gender !in formRecord.getGenders() || !formRecord.hasSeenShinyState(pokemon.shiny)) {
return PokedexLearnedInformation.VARIATION
}
return PokedexLearnedInformation.NONE
}
fun getSeenShinyStates(entry: PokedexEntry, form: PokedexForm): Set<String> {
return getSpeciesRecord(entry.speciesId)?.getFormRecord(form.displayForm)?.getSeenShinyStates() ?: return emptySet()
}
@@ -98,6 +119,10 @@ abstract class AbstractPokedexManager {
open fun onSpeciesRecordUpdated(speciesDexRecord: SpeciesDexRecord) {
// Save stuff and packet updates
clearCalculatedValues()
}
fun clearCalculatedValues() {
dexCalculatedValues.clear()
globalCalculatedValues.clear()
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokedexDexSyncPacket
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
@@ -49,6 +47,6 @@ object Dexes : JsonDataRegistry<PokedexDef> {
}
override fun sync(player: ServerPlayer) {
PokedexDexSyncPacket(dexEntryMap.values)
PokedexDexSyncPacket(dexEntryMap.values).sendToPlayer(player)
}
}
@@ -0,0 +1,16 @@
/*
* 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.pokedex
enum class PokedexLearnedInformation {
NONE,
SPECIES,
FORM,
VARIATION
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.pokemon.FormData
@@ -50,7 +51,7 @@ class PokedexManager(
override fun onSpeciesRecordUpdated(speciesDexRecord: SpeciesDexRecord) {
uuid.getPlayer()?.sendPacket(
SetClientPlayerDataPacket(
type = PlayerInstancedDataStoreType.POKEDEX,
type = PlayerInstancedDataStoreTypes.POKEDEX,
playerData = ClientPokedexManager(mutableMapOf(speciesDexRecord.id to speciesDexRecord)),
isIncremental = true
)
@@ -19,13 +19,21 @@ interface GlobalPokedexValueCalculator<T> {
fun calculate(dexManager: AbstractPokedexManager): T
}
object CaughtCount : PokedexValueCalculator<Int> {
object CaughtCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
override fun calculate(dexManager: AbstractPokedexManager): Int {
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }
}
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Int {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }
}
}
object SeenCount : PokedexValueCalculator<Int> {
object SeenCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
override fun calculate(dexManager: AbstractPokedexManager): Int {
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }
}
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Int {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }
}
@@ -98,7 +98,7 @@ class AggregatePokedexDef(
) { id, sortOrder, entries ->
val result = AggregatePokedexDef(id)
result.sortOrder = sortOrder
//result.subDexIds.addAll(entries)
result.subDexIds.addAll(entries)
result
}
}
@@ -54,7 +54,8 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
}
override val observable = SimpleObservable<DexEntries>()
override fun sync(player: ServerPlayer) {
DexEntrySyncPacket(entries.values)
DexEntrySyncPacket(entries.values).sendToPlayer(player)
}
}
@@ -14,6 +14,7 @@ import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
@@ -23,14 +24,32 @@ import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.api.types.tera.elemental.ElementalTypeTeraType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.isUuid
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.simplify
import com.cobblemon.mod.common.util.splitMap
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mojang.brigadier.StringReader
import com.mojang.serialization.Codec
import java.util.UUID
import kotlin.math.min
import kotlin.random.Random
import net.minecraft.ResourceLocationException
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.core.HolderLookup
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.StringTag
@@ -38,9 +57,8 @@ import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import java.util.*
import kotlin.random.Random
/**
* A grouping of typical, selectable properties for a Pokémon. This is serializable
@@ -122,6 +140,8 @@ open class PokemonProperties {
props.tradeable = parseBooleanProperty(keyPairs, listOf("tradeable", "tradable"))
props.originalTrainerType = OriginalTrainerType.entries.toList().parsePropertyOfCollection(keyPairs, listOf("originaltrainertype", "ottype"), labelsOptional = true) { it.name.lowercase() }
props.originalTrainer = parsePlayerProperty(keyPairs, listOf("originaltrainer", "ot"))
props.moves = parseString(keyPairs, listOf("moves"))?.split(",")
props.heldItem = parseString(keyPairs, listOf("helditem", "held_item"))
val maybeIVs = IVs()
val maybeEVs = EVs()
@@ -151,6 +171,16 @@ open class PokemonProperties {
}
}
private fun parseString(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): String? {
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels) ?: return null
val value = matchingKeyPair.second
return if (value.isNullOrBlank()) {
null
} else {
value
}
}
private fun parseIntProperty(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): Int? {
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels) ?: return null
val value = matchingKeyPair.second
@@ -305,6 +335,8 @@ open class PokemonProperties {
var tradeable: Boolean? = null
var originalTrainerType: OriginalTrainerType? = null
var originalTrainer: String? = null // Original Trainer by Username or UUID
var moves: List<String>? = null
var heldItem: String? = null
var ivs: IVs? = null
var evs: EVs? = null
@@ -333,6 +365,10 @@ open class PokemonProperties {
this.commonApply(pokemonEntity.pokemon)
}
fun applyCustomProperties(pokemonEntity: PokemonEntity) {
this.customProperties.forEach { it.apply(pokemonEntity) }
}
private fun commonApply(pokemon: Pokemon) {
species?.let {
return@let try {
@@ -392,6 +428,31 @@ open class PokemonProperties {
}
pokemon.refreshOriginalTrainer()
}
moves?.let { moves ->
if (pokemon.moveSet.getMoves().isEmpty()) {
pokemon.initializeMoveset(preferLatest = true)
}
val moveTemplates = moves.mapNotNull(Moves::getByName).shuffled()
val replaceableIndices = (0..3).filterNot { pokemon.moveSet[it]?.template?.let(moveTemplates::contains) == true }.shuffled()
val insertableMoves = moveTemplates.filterNot { pokemon.moveSet.any { move -> move.template == it } }
if (replaceableIndices.isNotEmpty() && insertableMoves.isNotEmpty()) {
val insertingMoves = insertableMoves.subList(0, min(replaceableIndices.size, insertableMoves.size))
pokemon.moveSet.doWithoutEmitting {
insertingMoves.forEachIndexed { index, move -> pokemon.moveSet.setMove(replaceableIndices[index], move.create()); pokemon.moveSet[replaceableIndices[index]]!!.update() }
}
pokemon.moveSet.update()
}
}
heldItem?.let { itemKey ->
val server = server() ?: return@let
val parser = ItemParser(server.registryAccess())
val result = parser.parse(StringReader(itemKey))
val stack = ItemStack(result.item)
stack.applyComponents(result.components)
if (stack.isEmpty) return@let
pokemon.heldItem = stack
}
pokemon.updateAspects()
}
@@ -441,8 +502,9 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let { return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let { return false }
moves?.takeIf { it.any { move -> pokemon.moveSet.none { it.template.name == move } } }?.let { return false }
return true
}
@@ -492,6 +554,7 @@ open class PokemonProperties {
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
return true
}
@@ -519,7 +582,7 @@ open class PokemonProperties {
}
fun createEntity(world: Level): PokemonEntity {
return PokemonEntity(world, create())
return PokemonEntity(world, create()).also { applyCustomProperties(it) }
}
// TODO Codecs at some point
@@ -546,6 +609,8 @@ open class PokemonProperties {
tradeable?.let { nbt.putBoolean(DataKeys.POKEMON_TRADEABLE, it) }
originalTrainerType?.let { nbt.putInt(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, it.ordinal) }
originalTrainer?.let { nbt.putString(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
moves?.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_MOVES, it.joinToString(separator = ",")) }
heldItem?.let {nbt.putString(DataKeys.POKEMON_PROPERTIES_HELDITEM, it)}
val custom = ListTag()
customProperties.map { StringTag.valueOf(it.asString()) }.forEach { custom.add(it) }
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -575,6 +640,8 @@ open class PokemonProperties {
tradeable = if (tag.contains(DataKeys.POKEMON_TRADEABLE)) tag.getBoolean(DataKeys.POKEMON_TRADEABLE) else null
originalTrainerType = if (tag.contains(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)) OriginalTrainerType.valueOf(tag.getString(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)) else null
originalTrainer = if (tag.contains(DataKeys.POKEMON_ORIGINAL_TRAINER)) tag.getString(DataKeys.POKEMON_ORIGINAL_TRAINER) else null
moves = if (tag.contains(DataKeys.POKEMON_PROPERTIES_MOVES)) tag.getString(DataKeys.POKEMON_PROPERTIES_MOVES).split(",") else null
heldItem = if (tag.contains(DataKeys.POKEMON_PROPERTIES_HELDITEM)) tag.getString(DataKeys.POKEMON_PROPERTIES_HELDITEM) else null
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, Tag.TAG_STRING.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -606,6 +673,8 @@ open class PokemonProperties {
tradeable?.let { json.addProperty(DataKeys.POKEMON_TRADEABLE, it) }
originalTrainerType?.let { json.addProperty(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, it.name) }
originalTrainer?.let { json.addProperty(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
moves?.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_MOVES, it.joinToString(separator = ",")) }
heldItem?.let {json.addProperty(DataKeys.POKEMON_PROPERTIES_HELDITEM, it)}
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -636,6 +705,8 @@ open class PokemonProperties {
tradeable = json.get(DataKeys.POKEMON_TRADEABLE)?.asBoolean
originalTrainerType = json.get(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)?.asString?.let { OriginalTrainerType.valueOf(it) }
originalTrainer = json.get(DataKeys.POKEMON_ORIGINAL_TRAINER)?.asString
moves = json.get(DataKeys.POKEMON_PROPERTIES_MOVES)?.asString?.split(",")
heldItem = json.get(DataKeys.POKEMON_PROPERTIES_HELDITEM)?.asString
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -670,6 +741,8 @@ open class PokemonProperties {
originalTrainerType?.let { pieces.add("originaltrainertype=${it.name}") }
originalTrainer?.let { pieces.add("originaltrainer=$it") }
customProperties.forEach { pieces.add(it.asString()) }
moves?.let { pieces.add("moves=${it.joinToString(separator = ",")}") }
heldItem?.let {pieces.add("helditem=$it")}
return pieces.joinToString(separator)
}
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokemonGainedEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.moves.BenchedMove
@@ -30,6 +31,9 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.protocol.game.ClientboundSoundPacket
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.ItemStack
@@ -152,6 +156,7 @@ interface Evolution : EvolutionLike {
val pokemonEntity = pokemon.entity
if (pokemonEntity == null || !useEvolutionEffect) {
pokemon.getOwnerPlayer()?.playNotifySound(CobblemonSounds.EVOLUTION_UI, SoundSource.PLAYERS, 1F, 1F)
evolutionMethod(pokemon)
} else {
pokemonEntity.busyLocks.add("evolving")
@@ -170,12 +175,12 @@ interface Evolution : EvolutionLike {
}
private fun evolutionAnimation(pokemon: Entity) {
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("q.bedrock_stateful('evolution', 'evolution', 'endures_primary_animations');"), emptySet())
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("q.bedrock_stateful('evolution', 'evolution', 'endures_primary_animations');"), listOf())
playPoseableAnimationPacket.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 128.0, pokemon.level().dimension())
}
private fun cryAnimation(pokemon: Entity) {
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("cry"), emptySet())
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("cry"), emptyList())
playPoseableAnimationPacket.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 128.0, pokemon.level().dimension())
}
@@ -192,9 +197,9 @@ interface Evolution : EvolutionLike {
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLUTION_UI, 1F, 1F)
this.shed(pokemon)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
CobblemonEvents.POKEMON_GAINED.post(PokemonGainedEvent(pokemon.getOwnerUUID()!!, pokemon))
}
fun applyTo(pokemon: Pokemon) {
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokemon.experience
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -29,8 +30,9 @@ object StandardExperienceCalculator : ExperienceCalculator {
val victorPokemon = battlePokemon.effectedPokemon
val victorLevel = victorPokemon.level
val term3 = (((2.0 * opponentLevel) + 10) / (opponentLevel + victorLevel + 10)).pow(2.5)
// ToDo when OT is implemented 1.5 if traded with someone from the same locale, 1.7 otherwise, 1.0 if OT
val nonOtBonus = 1.0
// ToDo 1.7 if pokemon was traded with someone of different locale/language
val nonOtBonus = if (battlePokemon.effectedPokemon.originalTrainerType == OriginalTrainerType.PLAYER &&
battlePokemon.effectedPokemon.originalTrainer.equals(battlePokemon.actor.uuid.toString())) 1.0 else 1.5
val luckyEggMultiplier = if (battlePokemon.effectedPokemon.heldItemNoCopy().`is`(CobblemonItemTags.LUCKY_EGG)) Cobblemon.config.luckyEggMultiplier else 1.0
val evolutionMultiplier = if (battlePokemon.effectedPokemon.evolutionProxy.server().any { evolution ->
val requirements = evolution.requirements.asSequence()
@@ -1,36 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.CollectionSubscription
interface MutableObservableCollection<T, C : Collection<T>> :
ObservableCollection<T, C>,
MutableCollection<T> {
fun subscribe(
priority: Priority = Priority.NORMAL,
anyChangeHandler: (Pair<C, T>) -> Unit,
additionHandler: ((Pair<C, T>) -> Unit)? = null,
removalHandler: ((Pair<C, T>) -> Unit)? = null,
): CollectionSubscription<T, C>
override fun add(element: T): Boolean
fun addIf(element: T, predicate: (C) -> Boolean): Boolean
override fun remove(element: T): Boolean
fun removeIf(predicate: (T) -> Boolean): Boolean
override fun addAll(elements: Collection<T>): Boolean
override fun retainAll(elements: Collection<T>): Boolean
override fun removeAll(elements: Collection<T>): Boolean
override fun iterator(): MutableIterator<T>
override fun clear()
fun mutableCopy(): MutableObservableCollection<T, C>
}
@@ -1,8 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections
interface MutableObservableCollectionIterator<T, C : Collection<T>, I : Iterator<T>> :
ObservableCollectionIterator<T, I>,
MutableIterator<T> {
override fun remove()
}
@@ -1,32 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
interface ObservableCollection<T, C : Collection<T>> :
Collection<T>,
Observable<Pair<C, T>> {
val elements: C
override val size: Int
override fun subscribe(
priority: Priority,
handler: (Pair<C, T>) -> Unit,
): ObservableSubscription<Pair<C, T>>
override fun unsubscribe(subscription: ObservableSubscription<Pair<C, T>>)
override fun isEmpty(): Boolean
fun isNotEmpty(): Boolean
override fun contains(element: T): Boolean
override fun containsAll(elements: Collection<T>): Boolean
override operator fun iterator(): Iterator<T>
fun copy(): ObservableCollection<T, C>
}
@@ -1,7 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections
interface ObservableCollectionIterator<T, I : Iterator<T>> : Iterator<T> {
override fun hasNext(): Boolean
override fun next(): T
}
@@ -1,35 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.reactive.Observable
/**
* This attempts to cast [this] value to [Observable].
*
* @return An observable set with [this] as an [Observable] or an empty set.
*/
fun <T> T.getElementObservables(): Set<Observable<*>> {
this.tryGetObservable()?.let { return setOf(it) }
return emptySet()
}
/**
* This attempts to cast [this] value to [Observable].
*
* @return This value as an [Observable] or null.
*/
fun <T> T.tryGetObservable(): Observable<*>? = if (this is Observable<*>) this else null
fun <T> MutableIterator<T>.removeIf(predicate: (T) -> Boolean): Boolean {
var removed = false
this.forEach { element ->
if (predicate(element)) {
this.remove()
if (!removed) {
removed = true
}
}
}
return removed
}
@@ -1,242 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.IndexedListSubscription
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.ListSubscription
import com.cobblemon.mod.common.api.reactive.collections.removeIf
open class MutableObservableList<T>(
list: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) : MutableObservableCollection<T, List<T>>,
ObservableList<T>(list, elementHandler),
MutableList<T> {
private val additionObservable = SimpleObservable<Pair<List<T>, T>>()
private val removalObservable = SimpleObservable<Pair<List<T>, T>>()
private val setObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
private val swapObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
private val additionObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
private val removalObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
private val setObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Int>>()
private val swapObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Pair<Int, Int>>>()
override fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
}
fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
@Suppress("DuplicatedCode")
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
set = setHandler?.let { setObservable.subscribe(priority, it) },
swap = swapHandler?.let { swapObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler, setHandler, swapHandler)
}
fun subscribeIndexed(
priority: Priority,
additionHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
removalHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
setHandler: ((Triple<List<T>, Pair<T, T>, Int>) -> Unit)?,
swapHandler: ((Triple<List<T>, Pair<T, T>, Pair<Int, Int>>) -> Unit)?,
): IndexedListSubscription<T> {
return IndexedListSubscription(
addition = additionHandler?.let { additionObservableIndexed.subscribe(priority, it) },
removal = removalHandler?.let { removalObservableIndexed.subscribe(priority, it) },
set = setHandler?.let { setObservableIndexed.subscribe(priority, it) },
swap = swapHandler?.let { swapObservableIndexed.subscribe(priority, it) },
)
}
/**
* @return `true` after handling [register] & all subscriptions to [additionObservable],
* [additionObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitAddition(element: T, index: Int): Boolean {
register(element)
additionObservable.emit(list to element)
additionObservableIndexed.emit(Triple(list, element, index))
return emitAnyChange(element)
}
/**
* @return `true` after handling [unregister] & all subscriptions to [removalObservable],
* [removalObservableIndexed] & [ObservableList.emitAnyChange].
*/
private fun emitRemoval(element: T, index: Int): Boolean {
unregister(element)
removalObservable.emit(list to element)
removalObservableIndexed.emit(Triple(list, element, index))
return emitAnyChange(element)
}
/**
* @return `true` after handling [register], [unregister] & all subscriptions to [setObservable],
* [setObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitSet(elements: Pair<T, T>, index: Int): Boolean {
register(elements.first)
unregister(elements.second)
setObservable.emit(list to elements)
setObservableIndexed.emit(Triple(list, elements, index))
emitAnyChange(elements.first)
emitAnyChange(elements.second)
return true
}
/**
* @return `true` after handling all subscriptions to [swapObservable],
* [swapObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitSwap(elements: Pair<T, T>, indices: Pair<Int, Int>): Boolean {
swapObservable.emit(list to elements)
swapObservableIndexed.emit(Triple(list, elements, indices))
emitAnyChange(elements.first)
emitAnyChange(elements.second)
return true
}
override operator fun iterator() = MutableObservableListIterator(this)
override fun listIterator() = MutableObservableListIterator(this)
override fun listIterator(index: Int) = MutableObservableListIterator(this, index)
override fun subList(fromIndex: Int, toIndex: Int): MutableObservableSubList<T> {
return MutableObservableSubList(this, fromIndex, toIndex, elementHandler)
}
override fun addIf(element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(list)) add(element) else false
}
override fun add(element: T): Boolean {
list.add(element)
return emitAddition(element, lastIndex)
}
fun addAtIf(index: Int, element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(list)) {
add(index, element)
true
} else {
false
}
}
override fun add(index: Int, element: T) {
list.add(index, element)
emitAddition(element, index)
}
fun setIf(index: Int, element: T, predicate: (T) -> Boolean): T? {
return if (predicate(list[index])) set(index, element) else null
}
override operator fun set(index: Int, element: T): T {
return list.set(index, element).also { emitSet(element to it, index) }
}
fun swapIf(indexOne: Int, indexTwo: Int, predicate: () -> Boolean): Boolean {
return predicate().also { if (it) swap(indexOne, indexTwo) }
}
fun swap(indexOne: Int, indexTwo: Int) {
val one = list[indexOne]
val two = list.set(indexTwo, one)
list[indexOne] = two
emitSwap(one to two, indexOne to indexTwo)
}
override fun removeIf(predicate: (T) -> Boolean): Boolean = iterator().removeIf(predicate)
override fun remove(element: T): Boolean {
val indexOf = list.indexOfFirst { it == element }
if (indexOf == -1) {
return false
}
list.removeAt(indexOf)
return emitRemoval(element, indexOf)
}
override fun removeAt(index: Int): T {
val element = list.removeAt(index)
emitRemoval(element, index)
return element
}
fun removeAtIf(index: Int, predicate: (T) -> Boolean): T? {
val element = list[index]
if (predicate(element)) {
list.removeAt(index)
emitRemoval(element, index)
}
return element
}
override fun addAll(elements: Collection<T>): Boolean {
elements.forEach { add(it) }
return true
}
override fun addAll(index: Int, elements: Collection<T>): Boolean {
var currentIndex = index
elements.forEach { add(currentIndex++, it) }
return true
}
override fun removeAll(elements: Collection<T>):Boolean = removeIf { elements.contains(it) }
override fun retainAll(elements: Collection<T>):Boolean = removeIf { !elements.contains(it) }
override fun clear() {
while (isNotEmpty()) {
removeAt(0)
}
}
}
@@ -1,60 +0,0 @@
package com.cobblemon.mod.common.api.reactive.collections.list
// the name is not quite long enough yet...
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
open class MutableObservableListIterator<T>(
observableList: MutableObservableList<T>,
index: Int = 0,
) : MutableObservableCollectionIterator<T, Set<T>, ListIterator<T>>,
ObservableListIterator<T>(observableList, index),
MutableListIterator<T> {
private val mutableList: MutableObservableList<T> = observableList
private var illegalSetOrRemoveState: Boolean? = null
set (value) { if (field != value) field = value }
override fun next(): T {
illegalSetOrRemoveState = false
return super.next()
}
override fun previous(): T {
illegalSetOrRemoveState = false
return super.previous()
}
override fun remove() {
throwIfIllegalState("remove")
mutableList.removeAt(--index)
illegalSetOrRemoveState = true
}
override fun set(element: T) {
throwIfIllegalState("set")
mutableList[index -1] = element
}
override fun add(element: T) {
when {
mutableList.isEmpty() -> mutableList.add(element).run { index = 0 }
index < mutableList.size -> mutableList.add(index++, element)
else -> mutableList.add(element).run { index++ }
}
illegalSetOrRemoveState = true
}
private fun throwIfIllegalState(function: String) {
if (illegalSetOrRemoveState == null) {
throw IllegalStateException(
"Observable List iterator '$function' was called before 'next' or 'previous'."
)
} else if (illegalSetOrRemoveState == true) {
throw IllegalStateException(
"Observable List iterator '$function' was called, " +
"but 'remove' or 'add' was already called this iteration."
)
}
}
}

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