Compare commits

..
Author SHA1 Message Date
Polymeta b79e73928a add multibattle NPC test command for debugging
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-21 15:35:59 +01:00
Polymeta c89ea759a6 fix invalidActionExceptions if the gimmickMove's targetType is different from it's normal move
good example is earthquake (allAdjacent) vs maxquake (adjacentFoe)

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-08 17:00:58 +01:00
Polymeta c6479525f9 double oop docs
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 16:07:55 +01:00
Polymeta 2a7dababab oop licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 15:58:21 +01:00
Polymeta 7ec6e61d4a first approach with resetting forms pre-battle
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 15:16:06 +01:00
Polymeta 7735af5c11 fixed itempredicates not working when used in species definition
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 11:39:51 +01:00
Polymeta 18d25dcf04 small reshuffle for requirements and introduce keyitem requirement (also add aerodactyl as example)
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 11:39:18 +01:00
Polymeta e259dc18a2 fix doubles not properly working with gimmicks
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-07 11:26:30 +01:00
Polymeta 7439436e93 fix trailing comma in schema, and check pokemon entity for registryAccess before doing the rest
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-06 10:49:37 +01:00
Polymeta 1c51d5ca98 prevent shiftbutton from being triggered or clicked when its not actually visible
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-05-06 10:42:59 +01:00
Segfault Guy d94952347f Portrait updates and form effects. 2025-04-30 00:30:06 -04:00
Segfault Guy 7fe6141640 form20 blaze it 2025-04-20 23:41:12 -04:00
Segfault Guy 4b1653ca6c Merge remote-tracking branch 'origin/main' into form_changes 2025-04-20 20:53:54 -04:00
Polymeta 068b55be7b Merge branch 'josh/dynamic-light-integration' into 'main'
Josh/dynamic light integration

See merge request cable-mc/cobblemon!1621
2025-04-20 13:52:34 +00:00
JoshandPolymeta 3f6bd52d43 Josh/dynamic light integration 2025-04-20 13:52:34 +00:00
Polymeta 4ca3f25ab2 Merge branch 'translucent-culling' into 'main'
Translucent Culling Parameter

See merge request cable-mc/cobblemon!1606
2025-04-20 09:47:02 +00:00
GertimoshkaandPolymeta 70e81f1cbe Translucent Culling Parameter 2025-04-20 09:47:02 +00:00
Polymeta df8abf4ddb Merge branch 'josh/left-handed-item-fix' into 'main'
Item position adjustment to perfectly line up hand-held items

See merge request cable-mc/cobblemon!1610
2025-04-20 09:46:28 +00:00
JoshandPolymeta 26dd0b9d42 Item position adjustment to perfectly line up hand-held items 2025-04-20 09:46:28 +00:00
Paul 2f16ba5470 Don't play quirks in battle GUI 2025-04-20 08:53:39 +10:00
Landon cbdfbc9745 Merge branch 'pc-box-name-limit' into 'main'
Adds name character length restriction to PC box name.

See merge request cable-mc/cobblemon!1620
2025-04-19 11:28:15 +00:00
Hiroku ac210e027b Merge branch 'windows-12' into 'main'
more pc stat displays

See merge request cable-mc/cobblemon!1617
2025-04-19 11:04:57 +00:00
TestandHiroku 1fc91a794f more pc stat displays 2025-04-19 11:04:57 +00:00
Landon f78b289171 Merge branch 'riding-dismount' into 'main'
Created the Pokemon Dismount Logic

See merge request cable-mc/cobblemon!1613
2025-04-19 10:47:16 +00:00
AramandLandon c46461c843 Created the Pokemon Dismount Logic 2025-04-19 10:47:16 +00:00
Deltric 386a897295 Adds name character length restriction to PC box name. 2025-04-19 05:46:22 -05:00
Hiroku 8328f741d8 Merge branch 'spawn-pool-observable' into 'main'
Observable in SpawnPool was not emitted on reload.

See merge request cable-mc/cobblemon!1618
2025-04-19 09:07:56 +00:00
Deltric 2b19943416 Observable in SpawnPool was not emitted on reload. 2025-04-19 03:30:50 -05:00
Paul 550b99be36 Merge remote-tracking branch 'origin/main' into lang-updates 2025-04-18 22:01:33 +10:00
Polymeta bfbee7b1d7 Merge branch 'loot-fix' into 'main'
Improved parity with vanilla mobs' drop behavior

See merge request cable-mc/cobblemon!1576
2025-04-18 07:02:45 +00:00
Test f5d71390dd Merge branch 'dex-zoom' into 'main'
A few dex scanner fixes/improvements, closes #1328

Closes #1328

See merge request cable-mc/cobblemon!1608
2025-04-18 05:06:31 +00:00
CraterandTest 6ff5d3df06 A few dex scanner fixes/improvements, closes #1328 2025-04-18 05:06:30 +00:00
gruxor 988766894a Review resolutions 2025-04-18 00:08:43 -04:00
Polymeta 34805ea496 oop typo - allow dragon breath to be used in a restoration tank
Closes #1539

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-18 00:25:39 +01:00
Polymeta b670d44e21 reorder delegate molang functions to be added first so they are overridden by more specific functions
Also allow PoseableState sound molang to play without an entity present (in UIs)

Closes #1529

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-18 00:08:59 +01:00
Polymeta 159905eb3b Merge branch 'josh/left-handed-item-fix' into 'main'
Corrected item rotation for left hand display context

See merge request cable-mc/cobblemon!1609
2025-04-17 20:32:11 +00:00
Josh c10213480b Flip rotations when display context is left hand 2025-04-17 15:22:11 -04:00
gruxor 3d5a365a6e Added a configuration option for drops' behavior upon Pokémon death 2025-04-17 14:06:33 -04:00
gruxor 4b64b4307a Changelog update 2025-04-17 14:04:16 -04:00
gruxor 382f741db8 Improved parity with vanilla mobs' drop behavior 2025-04-17 14:04:16 -04:00
TeggyandWeblate 6d62d911d6 Translated using Weblate (Spanish)
Currently translated at 96.6% (6787 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-17 11:38:28 +00:00
Alexis SalgueiroandWeblate a1d40084c5 Translated using Weblate (French)
Currently translated at 92.2% (6479 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-04-17 04:04:45 +00:00
TeggyandWeblate 8b1b63e110 Translated using Weblate (Spanish)
Currently translated at 96.4% (6775 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-17 04:04:44 +00:00
TeggyandWeblate 9e2e95e287 Translated using Weblate (Spanish)
Currently translated at 96.0% (6744 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-16 09:42:42 +00:00
TeggyandWeblate 6694680c50 Translated using Weblate (Spanish)
Currently translated at 95.9% (6735 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-15 05:11:49 +00:00
TeggyandWeblate 8cafce0a4a Translated using Weblate (Spanish)
Currently translated at 95.7% (6724 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-14 10:33:04 +00:00
Watuneni iteiteandWeblate de271f81b1 Translated using Weblate (Japanese)
Currently translated at 76.4% (5365 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-04-14 06:50:20 +00:00
TeggyandWeblate feb78c5c58 Translated using Weblate (Spanish)
Currently translated at 95.3% (6698 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-14 06:50:19 +00:00
MiaandWeblate e5d052a6bf Translated using Weblate (German)
Currently translated at 86.7% (6090 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/de/
2025-04-12 10:41:09 +00:00
Segfault Guy 7265ab57b9 Apply species schema changes to evolutions. 2025-04-11 01:47:41 -04:00
Segfault Guy 27e3a95b06 cleanup test changes 2025-04-11 01:17:29 -04:00
Segfault Guy 38de3c6267 In-battle form changes and PokemonProperties form completion. 2025-04-09 00:18:10 -04:00
TeggyandWeblate 0ea4641536 Translated using Weblate (Spanish)
Currently translated at 95.3% (6696 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-08 10:41:46 +00:00
TeggyandWeblate bc4699e354 Translated using Weblate (Spanish)
Currently translated at 95.1% (6683 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-04-02 11:09:46 +00:00
TeggyandWeblate 9270119675 Translated using Weblate (Spanish)
Currently translated at 95.1% (6682 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-31 15:23:52 +00:00
TeggyandWeblate 0058906022 Translated using Weblate (Spanish)
Currently translated at 95.1% (6678 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-31 00:16:23 +00:00
KyoandWeblate 14d2a76211 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/vi/
2025-03-27 12:41:50 +00:00
Watuneni iteiteandWeblate 7669000962 Translated using Weblate (Japanese)
Currently translated at 76.3% (5358 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-03-27 12:41:46 +00:00
TeggyandWeblate 8912911800 Translated using Weblate (Spanish)
Currently translated at 94.3% (6621 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-22 01:16:22 +00:00
阿鱼andWeblate 62ecfaf54a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-20 23:47:02 +00:00
TeggyandWeblate 6684177236 Translated using Weblate (Spanish)
Currently translated at 94.2% (6615 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-20 23:47:00 +00:00
Watuneni iteiteandWeblate deca5cf55b Translated using Weblate (Japanese)
Currently translated at 76.1% (5350 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-03-20 23:46:59 +00:00
Segfault Guy 376e5ac906 Merge branch 'main' into form_changes 2025-03-18 22:26:00 -04:00
阿鱼andWeblate 0f75797759 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-17 21:03:49 +00:00
Alexis SalgueiroandWeblate c194aefce5 Translated using Weblate (French)
Currently translated at 91.5% (6425 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-03-15 14:17:58 +00:00
Aiser(CCHusky)andWeblate f4798c8783 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-15 14:17:57 +00:00
chronicenandWeblate 2178a5f10e Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-15 14:17:57 +00:00
TeggyandWeblate bc145a56d5 Translated using Weblate (Spanish)
Currently translated at 93.6% (6575 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-14 17:00:49 +00:00
Segfault Guy b225283dcc Toggleable, deferrable, and wild forms. 2025-03-12 23:00:29 -04:00
Nico MerenqandWeblate 476ef3bb2f Translated using Weblate (Russian)
Currently translated at 73.9% (5189 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-03-12 13:19:09 +00:00
Alexis SalgueiroandWeblate 460050289b Translated using Weblate (French)
Currently translated at 90.7% (6373 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-03-10 15:04:25 +00:00
Nico MerenqandWeblate 01fd4dcc68 Translated using Weblate (Russian)
Currently translated at 73.8% (5182 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-03-09 18:06:58 +00:00
Nico MerenqandWeblate 0392eb3b7e Translated using Weblate (Russian)
Currently translated at 73.6% (5172 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-03-09 15:29:58 +00:00
阿鱼andWeblate 493f423a2f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-09 10:54:04 +00:00
阿鱼andWeblate cbcfa1bbfb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (7021 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-09 10:45:17 +00:00
Nico MerenqandWeblate 17a3ff15db Translated using Weblate (Russian)
Currently translated at 73.6% (5171 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-03-09 10:30:06 +00:00
阿鱼andWeblate 9c14cccc98 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.9% (7017 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-09 10:30:05 +00:00
Weblate 850cfbe755 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-03-08 22:51:12 +00:00
TeggyandWeblate 28afa6a9e8 Translated using Weblate (Spanish)
Currently translated at 93.1% (6538 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-08 22:51:08 +00:00
阿鱼andWeblate 7a76d95ce7 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.7% (6936 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-08 22:51:07 +00:00
TeggyandWeblate 54707d0618 Translated using Weblate (Spanish)
Currently translated at 92.1% (6470 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-08 15:08:19 +00:00
Aiser(CCHusky)andWeblate 307c400e28 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.6% (6929 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-08 15:08:18 +00:00
阿鱼andWeblate 5c9b3ab25d Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.6% (6929 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-08 15:08:18 +00:00
阿鱼andWeblate 532e991fb2 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.1% (6890 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-08 11:31:35 +00:00
Nico MerenqandWeblate b1bf817329 Translated using Weblate (Russian)
Currently translated at 73.6% (5171 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ru/
2025-03-08 11:31:34 +00:00
TeggyandWeblate c091f90487 Translated using Weblate (Spanish)
Currently translated at 91.7% (6445 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-08 08:23:13 +00:00
阿鱼andWeblate a49d4715ff Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.0% (6816 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-08 08:23:12 +00:00
阿鱼andWeblate 652b15532e Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.9% (6807 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-07 04:18:30 +00:00
TeggyandWeblate e4064897b0 Translated using Weblate (Spanish)
Currently translated at 91.5% (6430 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-03-07 04:18:29 +00:00
阿鱼andWeblate f9ca8222da Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.7% (6793 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-06 12:30:15 +00:00
阿鱼andWeblate 67480a945f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 95.5% (6708 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-06 06:42:04 +00:00
Alexis SalgueiroandWeblate e6e811e6e6 Translated using Weblate (French)
Currently translated at 90.0% (6320 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-03-06 06:41:58 +00:00
Watuneni iteiteandWeblate 4635c531b9 Translated using Weblate (Japanese)
Currently translated at 73.7% (5178 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-03-06 06:41:56 +00:00
Watuneni iteiteandWeblate 3bdfac9a46 Translated using Weblate (Japanese)
Currently translated at 71.7% (5039 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-03-05 13:53:19 +00:00
chronicenandWeblate 2b7a71cb1c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 91.4% (6424 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-05 13:53:19 +00:00
阿鱼andWeblate d47a500a0e Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 91.4% (6424 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-05 13:53:19 +00:00
阿鱼andWeblate 33e6dbb4c1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 87.4% (6143 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2025-03-04 15:50:25 +00:00
Alexis SalgueiroandWeblate 482c57dd2e Translated using Weblate (French)
Currently translated at 89.1% (6259 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-03-03 19:14:47 +00:00
Alexis SalgueiroandWeblate 503c62d78d Translated using Weblate (French)
Currently translated at 87.8% (6169 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-03-03 09:30:43 +00:00
Seonho LeeandWeblate 708d0ab6d3 Translated using Weblate (Korean)
Currently translated at 70.0% (4915 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ko/
2025-03-03 09:30:42 +00:00
TeggyandWeblate 19727542bd Translated using Weblate (Spanish)
Currently translated at 91.4% (6424 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-28 22:02:07 +00:00
Alexis SalgueiroandWeblate c4e6714a95 Translated using Weblate (French)
Currently translated at 87.6% (6153 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/fr/
2025-02-28 02:41:41 +00:00
TeggyandWeblate c116e612c6 Translated using Weblate (Spanish)
Currently translated at 90.4% (6354 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-28 02:41:41 +00:00
Fiestorra ISVandWeblate 215a055ce6 Translated using Weblate (Spanish)
Currently translated at 90.4% (6354 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-28 02:41:40 +00:00
Fiestorra ISVandWeblate 9c0b6b8d26 Translated using Weblate (Spanish)
Currently translated at 87.3% (6134 of 7021 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-25 22:27:22 +00:00
Fiestorra ISVandWeblate 5c4462ee94 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-25 18:48:01 +00:00
TeggyandWeblate 04edd6f9e2 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-25 18:48:01 +00:00
Polymeta 52821cc051 Merge branch 'main' into lang-updates 2025-02-25 18:38:33 +00:00
Segfault Guy b90f852a71 Clear invalid forms on species change. 2025-02-24 20:12:23 -05:00
Segfault Guy fdf7a9703a coalesce temp and perm forms under same property and instead deserialize by custom adapter instead of having unique properties. 2025-02-23 23:21:43 -05:00
Watuneni iteiteandWeblate 0029deb029 Translated using Weblate (Japanese)
Currently translated at 81.6% (5218 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-02-23 23:03:25 +00:00
TeggyandWeblate c6360e2a36 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-23 23:03:24 +00:00
Fiestorra ISVandWeblate 2e77c647b6 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-23 23:03:22 +00:00
Watuneni iteiteandWeblate c3d3b19788 Translated using Weblate (Japanese)
Currently translated at 81.3% (5200 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-02-21 08:11:08 +00:00
Fiestorra ISVandWeblate 219cd61beb Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-21 08:11:07 +00:00
TeggyandWeblate 207e5a9142 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-19 13:03:51 +00:00
Segfault Guy bb41f28342 Merge remote-tracking branch 'origin/main' into form_changes 2025-02-18 22:33:23 -05:00
Watuneni iteiteandWeblate c721fb534d Translated using Weblate (Japanese)
Currently translated at 81.2% (5191 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/ja/
2025-02-18 15:00:29 +00:00
iván SeguraandWeblate dba2622fd7 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-18 15:00:28 +00:00
iván SeguraandWeblate da68675133 Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-16 12:57:27 +00:00
iván SeguraandWeblate 9aea5a7daf Translated using Weblate (Spanish)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/es/
2025-02-15 13:26:10 +00:00
Gustavo PuerariandWeblate 10402575fb Translated using Weblate (Portuguese (Brazil))
Currently translated at 68.3% (4369 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-13 23:43:36 +00:00
João Pedro Silva AfonsoandWeblate 0f82ade57c Translated using Weblate (Portuguese (Brazil))
Currently translated at 68.3% (4369 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-13 23:43:35 +00:00
Segfault Guy c5ee7ebe7a Fix ability registration for forms. 2025-02-11 21:40:35 -05:00
Gustavo PuerariandWeblate 450b041118 Translated using Weblate (Portuguese (Brazil))
Currently translated at 65.1% (4166 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-11 21:12:32 +00:00
Weblate 770c349ac4 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-02-10 00:41:50 +00:00
Gustavo PuerariandWeblate f76648b6f2 Translated using Weblate (Portuguese (Brazil))
Currently translated at 59.8% (3826 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-10 00:41:46 +00:00
Gustavo PuerariandWeblate 56e60667db Translated using Weblate (Portuguese (Brazil))
Currently translated at 51.5% (3297 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-07 00:29:47 +00:00
Gustavo PuerariandWeblate c4a60951c5 Translated using Weblate (Portuguese (Brazil))
Currently translated at 49.7% (3178 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-05 07:22:58 +00:00
Segfault Guy f6ae10e1d9 It's about f(orm)cking time. 2025-02-03 23:38:53 -05:00
KyoandWeblate 814eee8125 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (6391 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/vi/
2025-02-03 09:44:44 +00:00
Gustavo PuerariandWeblate 2a9ff3bfbd Translated using Weblate (Portuguese (Brazil))
Currently translated at 47.9% (3064 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-02-03 09:44:43 +00:00
Weblate 83350cb0db Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-01-31 22:57:44 +00:00
Patch Notes GuyandWeblate 778761dbee Translated using Weblate (Vietnamese)
Currently translated at 99.1% (6335 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/vi/
2025-01-31 22:57:39 +00:00
Gustavo PuerariandWeblate e9881b748a Translated using Weblate (Portuguese (Brazil))
Currently translated at 46.4% (2971 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-01-31 22:57:25 +00:00
Weblate dabaeefa23 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/
2025-01-31 09:41:51 +00:00
Patch Notes GuyandWeblate 0e0bec2712 Added translation using Weblate (Vietnamese) 2025-01-31 09:41:47 +00:00
Gustavo PuerariandWeblate 48fe160334 Translated using Weblate (Portuguese (Brazil))
Currently translated at 45.1% (2887 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-01-31 03:10:48 +00:00
Gustavo PuerariandWeblate 7d599e12d0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 42.7% (2731 of 6391 strings)

Translation: Cobblemon/Mod
Translate-URL: http://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2025-01-27 23:30:11 +00:00
Segfault GuyandSegfault Guy b2070b53fb Changing form... changes form? Wow! 2024-12-03 23:41:17 -05:00
Segfault GuyandSegfault Guy 7fb873dc2c Showdown Form and Species registration rework. 2024-12-03 23:10:25 -05:00
Segfault GuyandSegfault Guy 641f529de3 Better Form and Evolution triggering. 2024-12-03 19:12:41 -05:00
Segfault GuyandSegfault Guy db9fb4fefa Refactor Evolution and add Transformation to expand application of Evolution events and requirements. 2024-12-02 23:13:57 -05:00
763 changed files with 21605 additions and 18336 deletions
+1
View File
@@ -6,6 +6,7 @@
- Added filter functionality in PC UI, which supports `PokemonProperties` (e.g. `shiny=yes` shows all shiny Pokémon).
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
- Added IVs and EVs stat displays in PC.
### Developer
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
+8
View File
@@ -19,6 +19,8 @@
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
- Added lang keys for all moves and abilities up to Generation 9.
- Fixed Moon Ball moon phase logic to actually work correctly
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
### Pokémon Added
@@ -127,6 +129,10 @@
- Shulker shell Forretress is now a cosmetic rather than a special evo and thus all shulker Forretress will revert back to normal until a shulker shell is put in their cosmetic slot.
- Updated `doPokemonSpawning` gamerule to support per-dimension configurations.
- The Pokedex now displays a form name of a "normal" Pokémon for when the base form is still a named form.
- Improved the zoom functionality of the Pokédex Scanner by giving the levels logarithmic scaling.
- Added a subtle rotation effect to the Pokédex Scanner's wheel when zooming.
- Improved parity with vanilla mobs' drop behavior; loot and XP drop on death instead of after the entire death sequence finishes.
- Quirk animations no longer play in the battle GUI since they were pretty distracting.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
@@ -156,6 +162,8 @@
- Fixed an issue with newer versions of Fabric API where underground Pokémon were spawning in The End.
- Fixed spawning not working well when you're at high points surrounded by lower altitude spawning areas, such as flying.
- Fixed certain Pokémon with forms not having appropriate stock Pokédex entries.
- Fixed issue with Pokédex Scanner that caused the open/close overlay to have the wrong opacity values
- Fixed dragon's breath not being usable on the restoration tank when it should be
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -13,10 +13,12 @@ import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.LambDynLights;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -53,15 +55,19 @@ public class LambDynamicLightsCompat {
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
return extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int resolvedShoulderLightLevel(Player player) {
final var shoulderDataPair = PokemonOnShoulderRenderer.shoulderDataOf(player);
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final Optional<Integer> leftLightLevel = extractShoulderLightLevel(shoulderDataPair.getFirst(), underwater);
final Optional<Integer> rightLightLevel = extractShoulderLightLevel(shoulderDataPair.getSecond(), underwater);
return Math.max(leftLightLevel.orElse(0), rightLightLevel.orElse(0));
final int leftLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
return Math.max(leftLightLevel, rightLightLevel);
}
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
@@ -71,11 +77,16 @@ public class LambDynamicLightsCompat {
return Optional.of(form.getLightingData().getLightLevel());
}
private static Optional<Integer> extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return Optional.empty();
return 0;
}
return extractFormLightLevel(shoulderData.getForm(), underwater);
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
@@ -8,24 +8,11 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import com.cobblemon.mod.common.stadium.StadiumClientManager;
import com.cobblemon.mod.common.util.CameraExtensionsKt;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -33,17 +20,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Shadow @Final Minecraft minecraft;
@Shadow float renderDistance;
@Shadow @Final private LightTexture lightTexture;
@Shadow private boolean renderHand;
@Shadow private double getFov(Camera activeRenderInfo, float partialTicks, boolean useFOVSetting) { return 0.0; }
@Shadow public void resetProjectionMatrix(Matrix4f matrix) {}
@Shadow public Matrix4f getProjectionMatrix(double fov) { return new Matrix4f(); }
@Shadow private void renderItemInHand(Camera camera, float partialTick, Matrix4f projectionMatrix) {}
@Shadow public void pick(float partialTicks) {}
@Unique
long lastTime = -1;
@@ -66,54 +42,4 @@ public class GameRendererMixin {
lastTime = newTime;
}
@Inject(method = "renderLevel", at = @At(value = "HEAD"), cancellable = true)
public void renderLevel(DeltaTracker deltaTracker, CallbackInfo ci) {
if (StadiumClientManager.INSTANCE.shouldUseStadiumCamera()) {
float f = deltaTracker.getGameTimeDeltaPartialTick(true);
this.lightTexture.updateLightTexture(f);
if (this.minecraft.getCameraEntity() == null) {
this.minecraft.setCameraEntity(this.minecraft.player);
}
this.minecraft.getProfiler().push("camera");
Camera stadiumCamera = new Camera();
Entity entity = (Entity) this.minecraft.player;
stadiumCamera.setup(this.minecraft.level, entity, false, false, 1.0F);
//adjust camera
StadiumClientManager.INSTANCE.updateCameraData(f);
var position = StadiumClientManager.INSTANCE.getCameraPosition();
stadiumCamera.setPosition(position);
Quaternionf rotation = StadiumClientManager.INSTANCE.getCameraRotation();
var forwardVector = rotation.transform(Camera.FORWARDS, new Vector3f());
var upVector = rotation.transform(Camera.UP, new Vector3f());
var leftVector = rotation.transform(Camera.LEFT, new Vector3f());
stadiumCamera.rotation.set(rotation);
stadiumCamera.xRot = (float)Mth.wrapDegrees(-Math.toDegrees(Math.asin(forwardVector.y)));
stadiumCamera.yRot = Mth.wrapDegrees((float)-Math.toDegrees(Camera.FORWARDS.angleSigned(forwardVector, Camera.UP)) + 180);
stadiumCamera.forwards.set(forwardVector);
stadiumCamera.up.set(upVector);
stadiumCamera.left.set(leftVector);
this.renderDistance = (float)(this.minecraft.options.getEffectiveRenderDistance() * 16);
double d = this.getFov(stadiumCamera, f, true);
Matrix4f matrix4f = this.getProjectionMatrix(d);
this.resetProjectionMatrix(matrix4f);
Quaternionf quaternionf = stadiumCamera.rotation().conjugate(new Quaternionf());
Matrix4f matrix4f2 = (new Matrix4f()).rotation(quaternionf);
this.minecraft.levelRenderer.prepareCullFrustum(stadiumCamera.getPosition(), matrix4f2, this.getProjectionMatrix(Math.max(d, (double)(Integer)this.minecraft.options.fov().get())));
this.minecraft.levelRenderer.renderLevel(deltaTracker, false, stadiumCamera, (GameRenderer) (Object) this, this.lightTexture, matrix4f2, matrix4f);
this.minecraft.getProfiler().pop();
ci.cancel();
}
}
@Inject(method = "tick", at = @At(value = "TAIL"))
public void tick(CallbackInfo ci) {
// StadiumClientManager.INSTANCE.tickCameraManager();
}
}
@@ -86,9 +86,9 @@ import com.cobblemon.mod.common.permission.LaxPermissionValidator
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.FORM_ASPECTS
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
@@ -100,6 +100,7 @@ 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
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.pokemon.transformation.triggers.BlockClickTrigger
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
@@ -254,13 +255,8 @@ object Cobblemon {
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { event ->
val player = event.player
val block = player.level().getBlockState(event.pos).block
player.party().forEach { pokemon ->
pokemon.lockedEvolutions
.filterIsInstance<BlockClickEvolution>()
.forEach { evolution ->
evolution.attemptEvolution(pokemon, BlockClickEvolution.BlockInteractionContext(block, player.level()))
}
}
val context = BlockClickTrigger.Context(block, player.level())
player.party().forEach { pokemon -> pokemon.triggerTransformations(BlockClickTrigger::class.java, context) }
}
PlatformEvents.CHANGE_DIMENSION.subscribe {
@@ -301,6 +297,7 @@ object Cobblemon {
SHINY_ASPECT.register()
GENDER_ASPECT.register()
FORM_ASPECTS.register()
COSMETIC_SLOT_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
@@ -36,9 +36,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val STADIUM: BlockEntityType<StadiumBlockEntity> = this.create("stadium_terminal", BlockEntityType.Builder.of(::StadiumBlockEntity, CobblemonBlocks.STADIUM).build(null))
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@@ -354,9 +354,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
@JvmField
val STADIUM = create("stadium_terminal", StadiumBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F)))
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@@ -272,8 +272,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val STADIUM = blockItem("stadium_terminal", CobblemonBlocks.STADIUM)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -170,9 +170,6 @@ import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptanceP
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
@@ -211,6 +208,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.AddFormPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.ClearFormsPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.RemoveFormPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.AcceptFormPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.RevertFormPacket
import com.cobblemon.mod.common.net.serverhandling.*
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
@@ -218,10 +221,6 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.stadium.StadiumCamerasSyncPacket
import com.cobblemon.mod.common.stadium.StadiumJoinHandler
import com.cobblemon.mod.common.stadium.StadiumJoinPacket
import com.cobblemon.mod.common.stadium.StadiumCameraManagersSyncPacket
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -378,8 +377,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(StadiumCamerasSyncPacket.ID, StadiumCamerasSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(StadiumCameraManagersSyncPacket.ID, StadiumCameraManagersSyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -436,15 +433,17 @@ object CobblemonNetwork {
// Pokédex scanning
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
// Form packets
list.add(PacketRegisterInfo(AddFormPacket.ID, AddFormPacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(ClearFormsPacket.ID, ClearFormsPacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RemoveFormPacket.ID, RemoveFormPacket::decode, PokemonUpdatePacketHandler()))
// Debug / cheats
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
// Riding
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
// Stadium packets
list.add(PacketRegisterInfo(StadiumJoinPacket.ID, StadiumJoinPacket::decode, StadiumJoinHandler))
return list
}
@@ -542,6 +541,10 @@ object CobblemonNetwork {
// NPC packets
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
// Form Packets
list.add(PacketRegisterInfo(AcceptFormPacket.ID, AcceptFormPacket::decode, AcceptFormHandler))
list.add(PacketRegisterInfo(RevertFormPacket.ID, RevertFormPacket::decode, RevertFormHandler))
// Riding packet(s)
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.abilities
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
@@ -25,7 +26,7 @@ class AbilityTemplate(
var builder: (AbilityTemplate, forced: Boolean, priority: Priority) -> Ability = { template, forced, priority -> Ability(template, forced, priority) },
val displayName: String = "cobblemon.ability.$name",
val description: String = "cobblemon.ability.$name.desc"
) {
) : ShowdownIdentifiable {
/**
* Returns the Ability or if applicable the extension connected to this template
*/
@@ -45,6 +46,8 @@ class AbilityTemplate(
*/
fun create(json: JsonObject) = create().loadFromJSON(json)
override fun showdownId(): String = ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "")
companion object {
@JvmStatic
@@ -48,9 +48,9 @@ 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
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.pokemon.transformation.progress.DefeatTransformationProgress
import com.cobblemon.mod.common.pokemon.transformation.progress.LastBattleCriticalHitsTransformationProgress
import com.cobblemon.mod.common.pokemon.transformation.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.giveOrDropItemStack
@@ -77,7 +77,7 @@ open class PokemonBattle(
val side2: BattleSide
) {
/** Whether logging will be silenced for this battle. */
var mute = true
var mute = false
val struct = this.asMoLangValue()
val runtime = MoLangRuntime().also { it.environment.query = struct }
@@ -92,7 +92,7 @@ open class PokemonBattle(
actor.battle = this
actor.pokemonList.forEach { battlePokemon ->
battlePokemon.effectedPokemon.evolutionProxy.current().progress()
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
.filterIsInstance<LastBattleCriticalHitsTransformationProgress>()
.forEach { it.reset() }
}
}
@@ -252,11 +252,12 @@ open class PokemonBattle(
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted) {
// TODO refactor and apply to forms
pokemon.lockedEvolutions.forEach { evolution ->
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatTransformationProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatTransformationProgress() }
progress.updateProgress(DefeatTransformationProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
}
}
}
@@ -8,11 +8,11 @@
package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
import com.cobblemon.mod.common.pokemon.Pokemon
class EvolutionItemDropEntry : ItemDropEntry() {
val requirements: MutableSet<EvolutionRequirement> = mutableSetOf()
val requirements: MutableSet<TransformationRequirement> = mutableSetOf()
override fun canDrop(pokemon: Pokemon?): Boolean = pokemon != null && requirements.all { it.check(pokemon) }
}
@@ -12,8 +12,8 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
/**
* Fired when an evolution is accepted.
@@ -10,8 +10,8 @@ package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
/**
* Fired after an evolution finishes.
@@ -8,9 +8,9 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
/**
* Fired before an evolution is sent to clients.
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon.evolution
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -12,8 +12,8 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
import com.cobblemon.mod.common.util.getBoolean
/**
@@ -14,7 +14,7 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.FossilRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.transformation.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
@@ -215,6 +215,7 @@ fun drawPosablePortrait(
ageInTicks: Float = 0F,
headYaw: Float = 0F,
headPitch: Float = 0F,
doQuirks: Boolean = true,
r: Float = 1F,
g: Float = 1F,
b: Float = 1F,
@@ -240,6 +241,7 @@ fun drawPosablePortrait(
context.put(RenderContext.SPECIES, identifier)
context.put(RenderContext.ASPECTS, state.currentAspects)
context.put(RenderContext.POSABLE_STATE, state)
context.put(RenderContext.DO_QUIRKS, doQuirks)
val renderType = RenderType.entityCutout(texture)
@@ -74,7 +74,7 @@ abstract class RequestManager<T : ServerPlayerActionRequest> {
?: this.getInboundRequest(receiver.uuid, requestID)
/** Determines whether [player] can receive a [T]. */
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading() || player.party().find { it.entity?.isBusy == true } != null
open fun isBusy(player: ServerPlayer): Boolean = player.isBusy() || player.isPartyBusy()
/** Determines if the [target] is a valid interaction request target. */
abstract fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
import com.cobblemon.mod.common.api.pokedex.*
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -57,9 +56,10 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticl
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
import com.cobblemon.mod.common.pokemon.transformation.triggers.ItemInteractionTrigger
import com.cobblemon.mod.common.pokemon.transformation.triggers.LevelUpTrigger
import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import net.minecraft.commands.arguments.EntityAnchorArgument
@@ -1180,16 +1180,15 @@ object MoLangFunctions {
map.put("result") { evolution.result.asMoLangValue() }
if (evolution is LevelUpEvolution) {
if (evolution.trigger is LevelUpTrigger) {
map.put("is_level_up") { DoubleValue.ONE }
} else if (evolution is TradeEvolution) {
} else if (evolution.trigger is TradeTrigger) {
map.put("is_trade") { DoubleValue.ONE }
} else if (evolution is ItemInteractionEvolution) {
} else if (evolution.trigger is ItemInteractionTrigger) {
map.put("is_item") { DoubleValue.ONE }
}
map.put("is_optional") { DoubleValue(evolution.optional) }
map.put("consumes_held_item") { DoubleValue(evolution.consumeHeldItem) }
map
}
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
@@ -56,7 +57,7 @@ open class MoveTemplate(
val critRatio: Double,
val effectChances: Array<Double>,
val actionEffect: ActionEffectTimeline?
) {
) : ShowdownIdentifiable {
val struct: MoStruct by lazy {
QueryStruct(hashMapOf())
.addFunction("name") { StringValue(name) }
@@ -192,4 +193,6 @@ open class MoveTemplate(
}
return this.elementalType
}
override fun showdownId() = ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "")
}
@@ -466,29 +466,18 @@ open class PokemonProperties {
}
fun matches(properties: PokemonProperties): Boolean {
return properties.asString() == this.asString()
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
return this.commonMatches(properties) && this.customProperties.containsAll(properties.customProperties)
}
// Undefined properties will match whatever they're compared with
private fun commonMatches(pokemon: Pokemon): Boolean {
level?.takeIf { it != pokemon.level }?.let { return false }
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
gender?.takeIf { it != pokemon.gender }?.let { return false }
species?.run {
try {
val species = if (this == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
}
if (pokemon.species != species) {
return false
}
} catch (e: ResourceLocationException) {
return false
}
}
species?.takeIf { it.asSpecies() != pokemon.species }?.let { return false }
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.formOnlyShowdownId(), true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
@@ -527,6 +516,36 @@ open class PokemonProperties {
return true
}
// Undefined properties will match whatever they're compared with
private fun commonMatches(properties: PokemonProperties): Boolean {
level?.takeIf { it != properties.level }?.let { return false }
shiny?.takeIf { it != properties.shiny }?.let { return false }
gender?.takeIf { it != properties.gender }?.let { return false }
species?.takeIf { it.asSpecies() != properties.species?.asSpecies() }?.let { return false }
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
form?.takeIf { it != properties.form }?.let { return false }
friendship?.takeIf { it != properties.friendship }?.let { return false }
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
nature?.takeIf { it != properties.nature }?.let { return false }
ability?.takeIf { it != properties.ability }?.let { return false }
status?.takeIf { it != properties.status }?.let { return false }
ivs?.forEach{ stat ->
if (stat.value != properties.ivs?.get(stat.key)) { return false }
}
evs?.forEach{ stat ->
if (stat.value != properties.evs?.get(stat.key)) { return false }
}
type?.takeIf { it != properties.type }?.let { return false }
teraType?.takeIf { it != properties.teraType }?.let { return false }
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
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
}
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
fun isSubSetOf(properties: PokemonProperties): Boolean {
level?.takeIf { it != properties.level }?.let { return false }
@@ -798,4 +817,15 @@ open class PokemonProperties {
return potentialAbility.template.create(false, potentialAbility.priority)
}
private fun String.asSpecies() =
try {
if (this == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace())
}
}
catch (e: ResourceLocationException) {
null
}
}
@@ -12,6 +12,8 @@ import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
import com.cobblemon.mod.common.api.abilities.CommonAbility
import com.cobblemon.mod.common.api.abilities.PotentialAbility
import com.cobblemon.mod.common.api.ai.SleepDepth
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
@@ -24,14 +26,15 @@ import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.transformation.evolution.PreEvolution
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroupAdapter
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
@@ -42,11 +45,16 @@ import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPack
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
import com.cobblemon.mod.common.pokemon.abilities.SpecialAbility
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.transformation.adapters.*
import com.cobblemon.mod.common.pokemon.transformation.form.GigantamaxForm
import com.cobblemon.mod.common.pokemon.transformation.form.PermanentForm
import com.cobblemon.mod.common.pokemon.transformation.form.StandardForm
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
import com.cobblemon.mod.common.pokemon.transformation.requirements.HeldItemRequirement
import com.cobblemon.mod.common.pokemon.transformation.requirements.MoveSetRequirement
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -69,6 +77,7 @@ import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.levelgen.structure.Structure
import net.minecraft.world.phys.AABB
import java.util.NoSuchElementException
object PokemonSpecies : JsonDataRegistry<Species> {
@@ -84,10 +93,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(ExperienceGroup::class.java, ExperienceGroupAdapter)
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
.registerTypeAdapter(Learnset::class.java, LearnsetAdapter)
.registerTypeAdapter(Evolution::class.java, CobblemonEvolutionAdapter)
.registerTypeAdapter(AABB::class.java, BoxAdapter)
.registerTypeAdapter(AbilityPool::class.java, AbilityPoolAdapter)
.registerTypeAdapter(EvolutionRequirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(PreEvolution::class.java, CobblemonPreEvolutionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Set::class.java, Evolution::class.java).type, LazySetAdapter(Evolution::class))
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
@@ -106,7 +113,10 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(TransformationTrigger::class.java, CobblemonTriggerAdapter)
.registerTypeAdapter(TransformationRequirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(FormData::class.java, FormAdapter)
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
@@ -126,6 +136,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
get() = this.speciesByIdentifier.values
val implemented = mutableListOf<Species>()
val forms: Collection<FormData>
get() = this.species.flatMap { it.allForms }
init {
SpeciesAdditions.observable.subscribe {
this.species.forEach(Species::initialize)
@@ -225,34 +238,18 @@ object PokemonSpecies : JsonDataRegistry<Species> {
}
/**
* The representation of a [Species] and/or [FormData] in the context of showdown.
* The representation of [FormData] in the context of Showdown.
* This is intended as a sort of DTO that can be easily converted between JSON and Java/JS objects.
*
* @param species The [Species] being converted or the base species if the form is not null.
* @param form The [FormData] being converted int o species (Showdown considers them species) will be null when dealing with the base form.
* @param form The [FormData] being converted into a species (Showdown considers them species).
*/
@Suppress("unused")
internal class ShowdownSpecies(species: Species, form: FormData?) {
val num = species.nationalPokedexNumber
val name = if (form != null) "${createShowdownName(species)}-${form.name}" else createShowdownName(species)
val baseSpecies = if (form != null) createShowdownName(species) else this.name
val forme = form?.name
// ToDo baseForme
val otherFormes = if (form == null && species.forms.isNotEmpty()) species.forms.map { "${this.name}-${it.name}" } else emptyList()
val formeOrder = if (form == null && this.otherFormes.isNotEmpty()) arrayListOf(this.name, *this.otherFormes.toTypedArray()) else emptyList()
val abilities: Map<String, String> = mapOf(
"0" to "No Ability",
"1" to "No Ability",
"H" to "No Ability",
"S" to "No Ability"
)
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
val nfe = this.evos.isNotEmpty()
val eggGroups = (form?.eggGroups ?: species.eggGroups).map { it.showdownID }
val gender: String? = when (form?.maleRatio ?: species.maleRatio) {
internal open class ShowdownSpecies(form: FormData) {
val num = form.species.nationalPokedexNumber
val name = form.showdownName()
val types = form.types.map { it.name.replaceFirstChar(Char::uppercase) }
val eggGroups = form.eggGroups.map { it.showdownID }
val gender: String? = when (form.maleRatio) {
0F -> "F"
1F -> "M"
-1F, 1.125F -> "N"
@@ -260,35 +257,132 @@ object PokemonSpecies : JsonDataRegistry<Species> {
}
val genderRatio = if (this.gender == null)
mapOf(
"maleRatio" to (form?.maleRatio ?: species.maleRatio),
"femaleRation" to (1F - (form?.maleRatio ?: species.maleRatio))
"M" to (form.maleRatio),
"F" to (1F - (form.maleRatio))
) else null
val baseStats = mapOf(
"hp" to (form?.baseStats?.get(Stats.HP) ?: species.baseStats[Stats.HP] ?: 1),
"atk" to (form?.baseStats?.get(Stats.ATTACK) ?: species.baseStats[Stats.ATTACK] ?: 1),
"def" to (form?.baseStats?.get(Stats.DEFENCE) ?: species.baseStats[Stats.DEFENCE] ?: 1),
"spa" to (form?.baseStats?.get(Stats.SPECIAL_ATTACK) ?: species.baseStats[Stats.SPECIAL_ATTACK] ?: 1),
"spd" to (form?.baseStats?.get(Stats.SPECIAL_DEFENCE) ?: species.baseStats[Stats.SPECIAL_DEFENCE] ?: 1),
"spe" to (form?.baseStats?.get(Stats.SPEED) ?: species.baseStats[Stats.SPEED] ?: 1)
)
val heightm = (form?.height ?: species.height) / 10
val weightkg = (form?.weight ?: species.weight) / 10
val baseStats = Stats.PERMANENT.associateBy({ it.showdownId }, { form.baseStats[it] ?: 1 })
val heightm = form.height / 10
val weightkg = form.weight / 10
// This is ugly, but we already have it hardcoded in the mod anyway
val maxHP = if (species.showdownId() == "shedinja") 1 else null
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
// ToDo battleOnly
// ToDo changesFrom
val requiredMove = form?.requiredMove
val requiredItem = form?.requiredItem
val requiredItems = form?.requiredItems
val maxHP = if (this.num == 292) 1 else null
val cannotDynamax = form.dynamaxBlocked
val abilities = form.showdownAbilityMapping()
// Common fields exempt from standard forms
val baseSpecies = if (form !is StandardForm) form.species.standardForm.showdownName() else null
val forme = if (form !is StandardForm) form.name else null
/**
* The following fields are optional and currently omitted
*
* cosmeticFormes (not needed, we have species features)
* addedType (tertiary type [by name or showdownid] added by moves or abilities)
* tier (smogon tryhard bs)
* doublesTier (smogon tryhard bs)
* natDexTier (smogon tryhard bs)
* evoType (the evolution method used to evolve from 'prevo'; trade', 'useItem', 'levelMove', 'levelExtra', 'levelFriendship', 'levelHold', 'other')
* evoMove (the move [by name or showdownid] used to evolve from 'prevo')
* evoLevel (the minimum level required to evolve from 'prevo')
* canHatch (flag for baby pokemon)
* color (color, value is arbitrary)
* tags (flags for filtering pokemon; 'Mega', 'Mythical', 'Sub-Legendary', 'Restricted Legendary', 'Paradox')
* unreleasedHidden (whether this pokemon has an unreleased hidden ability)
* maleOnlyHidden (whether it is only possible to get the hidden ability on a male pokemon)
* gmaxUnreleased (whether this pokemon has an unreleased gmax form)
* pokemonGoData (no one cares)
*/
}
private fun createShowdownName(species: Species): String {
if (species.resourceIdentifier.namespace == Cobblemon.MODID) {
return species.name
/**
* The representation of a [PermanentForm] in the context of Showdown.
*
* @param form The [PermanentForm] being converted into a species.
*/
internal open class ShowdownPermanentForm(form: PermanentForm) : ShowdownSpecies(form) {
// species this form evolves from
val prevo = form.preEvolution?.form?.showdownName()
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
val evos = form.evolutions.map { "" }
// not fully evolved
val nfe = this.evos.isNotEmpty()
// whether there is a gmax form to change to
val canGigantamax: String? = form.changeableForms.filterIsInstance<GigantamaxForm>().firstOrNull()?.name // TODO id better
}
/**
* The representation of a [StandardForm] in the context of Showdown.
*
* @param form The [StandardForm] being converted into a species.
*/
internal class ShowdownStandardForm(form: StandardForm) : ShowdownPermanentForm(form) {
// list of all forms descending from this base species
val otherFormes = form.flattenForms().map { it.showdownName() }
// list of forms [by name or showdownid] in the order they appear in game [union of baseSpecies, otherFormes, and cosmeticFormes] with the exception of ending with 'Gmax' forms
val formeOrder = if (this.otherFormes.isNotEmpty()) arrayListOf(this.name, *this.otherFormes.toTypedArray()) else null
// TODO we don't enforce any form order with our Species registration - right now this looks like only a problem with Mega Rayquaza, but investigate changes needed on Showdown's end to enable these lists to be more dynamic
}
/**
* The representation of a [TemporaryForm] in the context of Showdown.
*
* @param form The [TemporaryForm] being converted into a species.
*/
internal class ShowdownTemporaryForm(form: TemporaryForm) : ShowdownSpecies(form) {
// form change can occur during a battle
val battleOnly = if (form.battleOnly) form.parentForm.showdownName() else null
// form change can occur outside of battle (mutually exclusive with battleOnly)
val changesFrom = if (!form.battleOnly) form.parentForm.showdownName() else null
// pokemon must know this move to be in this form
val requiredMove = form.requirements.filterIsInstance<MoveSetRequirement>().firstOrNull()?.move?.showdownId() // TODO update showdown to support 'requiredMoves'
// pokemon must hold one of these item(s) to be in this form
val requiredItems = form.requirements.filterIsInstance<HeldItemRequirement>().firstOrNull()?.let { requirement ->
try {
requirement.itemCondition.items.get().map { BuiltInRegistries.ITEM.getKey(it.value()).path } // TODO held items on showdown are ignorant of namespace
}
catch (e: NoSuchElementException)
{
Cobblemon.LOGGER.error("Failed to resolve required held item for ${form.showdownId()}}") // TODO this needs to be done after tags are loaded...
null
}
}
return "${species.resourceIdentifier.namespace}:${species.name}"
}
/** Accommodate for Showdown bullshit. */
/*
private fun TemporaryForm.showdownParentForm(): Any {
if (this.parentOverrides.isNotEmpty())
return this.species.standardForm.flattenForms().filter {
parentOverrides.contains(it.showdownId())
}.map {
it.showdownName()
}
else
return parentForm.showdownName()
}
*/
/**
* Constructs a mapping of the [PotentialAbility]s belonging to a [FormData] in Showdown identifiable format.
*
* @return A map of type (numeric for [CommonAbility]s, H for [HiddenAbility], S for [SpecialAbility]) to showdownid.
*/
private fun FormData.showdownAbilityMapping(): Map<String, String>? {
// potential abilities
val commonAbilities = this.abilities.filterIsInstance<CommonAbility>()
val hiddenAbility = this.abilities.find { it is HiddenAbility }
val specialAbility = this.abilities.find { it is SpecialAbility }
// can have multiple common abilities
val abilities = mutableMapOf<String, String>()
val mapping: (String, PotentialAbility) -> Unit = { key, ability ->
abilities.put(key, ability.template.showdownId())
}
// can only have one hidden and one special ability
commonAbilities.forEachIndexed { index, ability -> mapping(index.toString(), ability) }
hiddenAbility?.let { ability -> mapping("H", ability) }
specialAbility?.let { ability -> mapping("S", ability) }
return if (abilities.isEmpty()) null else abilities
}
}
@@ -0,0 +1,24 @@
package com.cobblemon.mod.common.api.pokemon.aspect
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* A provider of aspects for a [SpeciesFeature].
*
* @author Segfault Guy
* @since Feb 12th, 2024
*/
interface FeatureAspectProvider: AspectProvider {
var default: String?
var isAspect: Boolean
/** Returns true if this [aspect] is provided by this provider. */
fun matches(aspect: String): Boolean
/** Creates a [SpeciesFeature] that provides the respective [aspect]. */
fun from(aspect: String): SpeciesFeature<*>?
/** Sets the [aspect] that's provided. */
fun set(pokemon: Pokemon, aspect: String)
}
@@ -24,11 +24,11 @@ interface SingleConditionalAspectProvider : AspectProvider {
fun getForFeature(name: String): SingleConditionalAspectProvider {
return object : SingleConditionalAspectProvider {
override val aspect: String = name
override fun meetsCondition(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == true
override fun meetsCondition(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.value == true
override fun meetsCondition(pokemonProperties: PokemonProperties) = pokemonProperties
.customProperties
.filterIsInstance<FlagSpeciesFeature>()
.any { it.name == name && it.enabled }
.any { it.name == name && it.value }
}
}
@@ -1,54 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
/**
* Represents an evolution of a [Pokemon] that can only occur during specific actions and with added context.
* For the default implementations see [ItemInteractionEvolution] & [TradeEvolution].
*
* @param RC The context given at runtime when querying the [Evolution].
* @param TC The context that is serialized from JSON during species loading, this is what the [RC] is expected to match against.
* @author Licious
* @since March 19th, 2022
*/
interface ContextEvolution<RC, TC> : Evolution {
/**
* The target context for this [Evolution] to even be tested.
*/
val requiredContext: TC
/**
* Attempts to evolve the given [Pokemon] under the given context of type [RC].
*
* @param pokemon The [Pokemon] attempting to evolve.
* @param context The context of this query.
* @return If the evolution was successful.
*/
fun attemptEvolution(pokemon: Pokemon, context: RC): Boolean {
if (this.testContext(pokemon, context) && super.test(pokemon)) {
return super.evolve(pokemon)
}
return false
}
/**
* Checks if the given context is valid for the [requiredContext].
*
* @param pokemon The [Pokemon] attempting to evolve.
* @param context The context of this query.
* @return If the context matched the [requiredContext].
*/
fun testContext(pokemon: Pokemon, context: RC): Boolean
}
@@ -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.api.pokemon.evolution
import com.cobblemon.mod.common.api.pokemon.evolution.progress.EvolutionProgress
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.evolution.controller.ClientEvolutionController
import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController
/**
* Responsible for holding all available [EvolutionLike]s in the [Pokemon].
* Also handles all the networking behind them.
* For the Cobblemon default implementations see [ClientEvolutionController] & [ServerEvolutionController].
*
* @author Licious
* @since April 28th, 2022
*/
interface EvolutionController<T : EvolutionLike, out I : PreProcessor> : MutableSet<T> {
/**
* Resolves the [Pokemon] attached to this controller.
*
* @return The [Pokemon] this controller is attached to.
*/
fun pokemon(): Pokemon
/**
* Starts the given evolution on this controller.
* All the necessary networking will be handled.
*
* @param evolution The [EvolutionLike] starting.
*/
fun start(evolution: T)
/**
* Returns an immutable collection of [EvolutionProgress]es attached to this controller.
*
* @return The currently tracked [EvolutionProgress]es.
*/
fun progress(): Collection<EvolutionProgress<*>>
/**
* Attaches a [EvolutionProgress] to this controller.
*
* @param P The type of [EvolutionProgress].
* @param progress The [EvolutionProgress] being attached for tracking.
* @return The instance being tracked.
*/
fun <P : EvolutionProgress<*>> trackProgress(progress: P): P
/**
* Attempts to find an [EvolutionProgress] in [progress] or creates and tracks using [trackProgress].
*
* @param P The type of [EvolutionProgress].
* @param predicate The lambda attempting to find specific progress. This needs to ensure the [EvolutionProgress] is of type [P] or the cast will fail.
* @param progressFactory The lambda creating a new instance of type [P] if [predicate] can't find an element.
* @return The found match in [progress] or the created and now tracked with [trackProgress].
*/
fun <P : EvolutionProgress<*>> progressFirstOrCreate(predicate: (progress: EvolutionProgress<*>) -> Boolean, progressFactory: () -> P): P
fun asIntermediate(): I
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionDisplay
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
/**
* Represents an evolution of a [Pokemon], this is the client side counterpart of [Evolution].
* This has no attachments to any data regarding the evolution itself and only serves for display purposes and basic communication.
*
* @author Licious
* @since April 28th, 2022
*/
interface EvolutionDisplay : EvolutionLike {
/**
* The [Species] of the evolution result.
*/
val species: Species
/**
* The aspects of the evolution result.
* These are used by the client to resolve the entity it needs to display.
*/
val aspects: Set<String>
companion object {
@JvmStatic
val CODEC: Codec<EvolutionDisplay> = RecordCodecBuilder.create { instance ->
instance.group(
Codec.STRING.fieldOf("id").forGetter(EvolutionDisplay::id),
Species.BY_IDENTIFIER_CODEC.fieldOf("species").forGetter(EvolutionDisplay::species),
Codec.list(Codec.STRING).fieldOf("aspects").forGetter { it.aspects.toList() }
).apply(instance) { id, species, aspects -> CobblemonEvolutionDisplay(id, species, aspects.toSet()) }
}
}
}
@@ -1,59 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
/**
* Responsible for holding the different [EvolutionController] implementations based on the logical side.
* If you haven't already checked your side previously you can use [isClient].
*
* @param C The type of [EvolutionLike] on the client side.
* @param S The type of [EvolutionLike] on the server side.
* @param CI The type of [PreProcessor] on the client side.
* @param SI The type of [PreProcessor] on the server side.
*
* @author Licious
* @since June 18th, 2022
*/
interface EvolutionProxy<C : EvolutionLike, S : EvolutionLike, CI : PreProcessor, SI : PreProcessor> {
/**
* Checks if the current logical side is the client.
*
* @return True if the current logical side is the client.
*/
fun isClient(): Boolean
/**
* Returns the current [EvolutionController].
* Keep in mind this will have type erasure and as such most operations will not be possible.
* It's recommended to work with [client] or [server] when possible.
*
* @return The current [EvolutionController].
*/
fun current(): EvolutionController<out EvolutionLike, *>
/**
* The client side implementation of the [EvolutionController].
*
* @throws [ClassCastException] if called from the server side.
*
* @return The client side implementation of the [EvolutionController] if possible.
*/
fun client(): EvolutionController<C, CI>
/**
* The server side implementation of the [EvolutionController].
*
* @throws [ClassCastException] if called from the client side.
*
* @return The server side implementation of the [EvolutionController] if possible.
*/
fun server(): EvolutionController<S, SI>
}
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
/**
* Represents an evolution of a [Pokemon] that can occur without any additional context or actions.
* For the default implementation see [LevelUpEvolution].
*
* @author Licious
* @since March 19th, 2022
*/
interface PassiveEvolution : Evolution {
/**
* Checks if the given [Pokemon] satisfies the requirements.
* If yes the evolution will attempt to start.
*
* @param pokemon The [Pokemon] being tested.
* @return If the [Pokemon] will evolve.
*/
fun attemptEvolution(pokemon: Pokemon): Boolean {
if (super.test(pokemon)) {
return super.evolve(pokemon)
}
return false
}
/**
* If the evolution will disappear once the conditions are no longer met.
*/
val permanent: Boolean
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.adapters
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
/**
* Saves and loads [Evolution]s with JSON.
* For the default implementation see [CobblemonEvolutionAdapter].
*
* @author Licious
* @since March 20th, 2022
*/
interface EvolutionAdapter : JsonDeserializer<Evolution>, JsonSerializer<Evolution> {
/**
* Registers the given type of [Evolution] to it's associated ID for deserialization.
*
* @param T The type of [Evolution].
* @param id The id of the evolution event.
* @param type The [KClass] of the type.
*/
fun <T : Evolution> registerType(id: String, type: KClass<T>)
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.adapters
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
/**
* Saves and loads [EvolutionRequirement]s with JSON.
* For the default implementation see [CobblemonRequirementAdapter].
*
* @author Licious
* @since March 21st, 2022
*/
interface RequirementAdapter : JsonDeserializer<EvolutionRequirement>, JsonSerializer<EvolutionRequirement> {
/**
* Registers the given type of [EvolutionRequirement] to it's associated ID for deserialization.
*
* @param T The type of [EvolutionRequirement].
* @param id The id of the evolution event.
* @param type The [KClass] of the type.
*/
fun <T : EvolutionRequirement> registerType(id: String, type: KClass<T>)
}
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.progress
import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController
import com.cobblemon.mod.common.pokemon.evolution.progress.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.serialization.Codec
import com.mojang.serialization.Lifecycle
import net.minecraft.core.MappedRegistry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import kotlin.jvm.Throws
object EvolutionProgressTypes {
private val REGISTRY: Registry<EvolutionProgressType<*>> = MappedRegistry(
ResourceKey.createRegistryKey(cobblemonResource("evolution_progress")), Lifecycle.stable()
)
@JvmStatic
val DAMAGE_TAKEN = this.registerType(DamageTakenEvolutionProgress.ID, EvolutionProgressType(DamageTakenEvolutionProgress.CODEC))
@JvmStatic
val DEFEAT = this.registerType(DefeatEvolutionProgress.ID, EvolutionProgressType(DefeatEvolutionProgress.CODEC))
@JvmStatic
val LAST_BATTLE_CRITICAL_HITS = this.registerType(LastBattleCriticalHitsEvolutionProgress.ID, EvolutionProgressType(LastBattleCriticalHitsEvolutionProgress.CODEC))
@JvmStatic
val RECOIL = this.registerType(RecoilEvolutionProgress.ID, EvolutionProgressType(RecoilEvolutionProgress.CODEC))
@JvmStatic
val USE_MOVE = this.registerType(UseMoveEvolutionProgress.ID, EvolutionProgressType(UseMoveEvolutionProgress.CODEC))
/**
* Registers an evolution progress.
*
* @param T The type of the [EvolutionProgress].
* @param id The [Identifier].
* @param type The [EvolutionProgressType] record.
* @return The registered [EvolutionProgressType] of type [T].
*
* @throws [IllegalStateException] if [Registry.register] fails.
*/
@Throws(IllegalStateException::class)
@JvmStatic
fun <T : EvolutionProgress<*>> registerType(id: ResourceLocation, type: EvolutionProgressType<T>): EvolutionProgressType<T> {
return Registry.register(REGISTRY, id, type)
}
/**
* Creates a codec instance of this registry.
*
* @return The generated [Codec] of [EvolutionProgress].
*/
fun codec(): Codec<EvolutionProgress<*>> = REGISTRY.byNameCodec().dispatch(
ServerEvolutionController.ID_KEY,
{ it.type() },
{ it.codec }
)
}
@@ -12,7 +12,7 @@ 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 com.cobblemon.mod.common.pokemon.transformation.requirements.LevelRequirement
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.aspect.FeatureAspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -29,11 +29,11 @@ import net.minecraft.network.RegistryFriendlyByteBuf
*/
open class ChoiceSpeciesFeatureProvider(
override var keys: List<String>,
var default: String? = null,
override var default: String? = null,
var choices: List<String> = listOf(),
var isAspect: Boolean = true,
override var isAspect: Boolean = true,
var aspectFormat: String = "{{choice}}"
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, FeatureAspectProvider {
override var needsKey = true
override var visible = false
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
@@ -76,6 +76,28 @@ open class ChoiceSpeciesFeatureProvider(
return aspects
}
fun parseValue(aspect: String): String? {
if (!matches(aspect)) return null
val head = aspectFormat.substringBefore("{{choice}}")
val tail = aspectFormat.substringAfter("{{choice}}").takeIf { it.isNotBlank() }
var value = aspect.substringAfter(head)
tail?.let { value = value.substringBefore(it) }
return value
}
override fun matches(aspect: String): Boolean = this.isAspect && this.getAllAspects().contains(aspect)
override fun from(aspect: String): SpeciesFeature<*>? {
val value = parseValue(aspect)
return if (value != null && choices.contains(value)) this.fromString(value) else null
}
override fun set(pokemon: Pokemon, aspect: String) {
val value = parseValue(aspect)
if (value != null && choices.contains(value)) this.get(pokemon)?.value = value
}
override fun examples() = choices
internal constructor(): this(emptyList())
@@ -123,7 +145,9 @@ open class ChoiceSpeciesFeatureProvider(
override fun provide(pokemon: Pokemon): Set<String> {
return if (isAspect) {
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
get(pokemon)?.let {
setOf(getAspect(it))
} ?: emptySet()
} else {
emptySet()
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.aspect.FeatureAspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
@@ -30,65 +30,74 @@ import kotlin.random.Random
* @author Hiroku
* @since May 13th, 2022
*/
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature<Boolean>, CustomPokemonProperty {
constructor(name: String, enabled: Boolean): this(name) {
this.enabled = enabled
this.value = enabled
}
var enabled = false
override var value: Boolean = false
override fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag {
pokemonNBT.putBoolean(name, enabled)
pokemonNBT.putBoolean(name, value)
return pokemonNBT
}
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature {
enabled = if (pokemonNBT.contains(name)) pokemonNBT.getBoolean(name) else enabled
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<Boolean> {
value = if (pokemonNBT.contains(name)) pokemonNBT.getBoolean(name) else value
return this
}
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
pokemonJSON.addProperty(name, enabled)
pokemonJSON.addProperty(name, value)
return pokemonJSON
}
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<Boolean> {
val isEnabled = pokemonJSON.get(name)?.asBoolean
enabled = isEnabled ?: this.enabled
value = isEnabled ?: this.value
return this
}
override fun saveToBuffer(buffer: RegistryFriendlyByteBuf, toClient: Boolean) {
buffer.writeBoolean(enabled)
buffer.writeBoolean(value)
}
override fun loadFromBuffer(buffer: RegistryFriendlyByteBuf) {
enabled = buffer.readBoolean()
value = buffer.readBoolean()
}
override fun asString() = "$name=$enabled"
override fun asString() = "$name=$value"
override fun apply(pokemon: Pokemon) {
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
val existingFeature = pokemon.getFeature<FlagSpeciesFeature>(name)
if (existingFeature != null) {
existingFeature.enabled = enabled
existingFeature.value = value
} else {
pokemon.features.add(FlagSpeciesFeature(name, enabled))
pokemon.features.add(FlagSpeciesFeature(name, value))
}
pokemon.updateAspects()
}
}
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.value == value
}
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, FeatureAspectProvider {
override var keys: List<String> = emptyList()
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
override val needsKey get() = true
var default: String? = null
var isAspect = true
override var default: String? = null
override var isAspect = true
override fun matches(aspect: String): Boolean = this.isAspect && this.keys.first() == aspect
override fun from(aspect: String): SpeciesFeature<*>? = if (matches(aspect)) fromString(null) else null
override fun set(pokemon: Pokemon, aspect: String) {
this.get(pokemon)?.value = true
}
override var visible: Boolean = false
override fun invoke(buffer: RegistryFriendlyByteBuf, name: String): FlagSpeciesFeature? {
@@ -170,7 +179,7 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
}
override fun provide(pokemon: Pokemon): Set<String> {
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.enabled == true) {
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.value == true) {
setOf(keys.first())
} else {
emptySet()
@@ -178,7 +187,7 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
}
override fun provide(properties: PokemonProperties): Set<String> {
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.value == true) {
setOf(keys.first())
} else {
emptySet()
@@ -84,7 +84,7 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
}
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
fun <T : SpeciesFeature> register(name: String, providerLambda: () -> T) {
fun <T : SpeciesFeature<*>> register(name: String, providerLambda: () -> T) {
register(name, object : SpeciesFeatureProvider<T> {
override fun invoke(pokemon: Pokemon) = providerLambda()
override fun invoke(nbt: CompoundTag) = providerLambda().apply { loadFromNBT(nbt) }
@@ -26,8 +26,8 @@ import net.minecraft.world.phys.Vec3
* A species feature value that's just an integer. Complex stuff.
* @author Hiroku
*/
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
var value = 0
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature<Int>, CustomPokemonProperty {
override var value = 0
constructor() : this("")
constructor(name: String, value: Int) : this(name) {
@@ -39,7 +39,7 @@ class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature,
return pokemonNBT
}
override fun loadFromNBT(pokemonNBT: CompoundTag): SynchronizedSpeciesFeature {
override fun loadFromNBT(pokemonNBT: CompoundTag): SynchronizedSpeciesFeature<Int> {
value = pokemonNBT.getInt(name)
return this
}
@@ -49,7 +49,7 @@ class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature,
return pokemonJSON
}
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<Int> {
value = pokemonJSON.get(name).asInt
return this
}
@@ -24,10 +24,11 @@ import net.minecraft.nbt.CompoundTag
* @author Hiroku
* @since May 13th, 2022
*/
interface SpeciesFeature {
interface SpeciesFeature<T> {
val name: String
var value: T
fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag
fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature
fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<T>
fun saveToJSON(pokemonJSON: JsonObject): JsonObject
fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature
fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<T>
}
@@ -21,7 +21,7 @@ import net.minecraft.nbt.CompoundTag
* @author Hiroku
* @since November 29th, 2022
*/
interface SpeciesFeatureProvider<T : SpeciesFeature> {
interface SpeciesFeatureProvider<T : SpeciesFeature<*>> {
/**
* Attempts to create a species feature for the given [Pokemon]. It is appropriate to check for an existing feature
* that is compatible with this type and transfer / retain that feature instead of constructing a new one. For example
@@ -24,14 +24,14 @@ import net.minecraft.network.RegistryFriendlyByteBuf
*/
class StringSpeciesFeature(
override val name: String,
var value: String
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
override var value: String
) : SynchronizedSpeciesFeature<String>, CustomPokemonProperty {
override fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag {
pokemonNBT.putString(name, value)
return pokemonNBT
}
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature {
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<String> {
value = pokemonNBT.getString(name)?.takeIf { it.isNotBlank() }?.lowercase() ?: return this
return this
}
@@ -41,7 +41,7 @@ class StringSpeciesFeature(
return pokemonJSON
}
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<String> {
value = pokemonJSON.get(name)?.asString?.lowercase() ?: return this
return this
}
@@ -16,4 +16,4 @@ import com.cobblemon.mod.common.api.serialization.BufferSerializer
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeature : SpeciesFeature, BufferSerializer
interface SynchronizedSpeciesFeature<T> : SpeciesFeature<T>, BufferSerializer
@@ -22,7 +22,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, BufferSerializer {
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature<*>> : SpeciesFeatureProvider<T>, BufferSerializer {
var visible: Boolean
operator fun invoke(buffer: RegistryFriendlyByteBuf, name: String): T?
/** Gets the feature from this Pokémon, if it has been created yet. */
@@ -0,0 +1,88 @@
/*
* 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.pokemon.transformation
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.ContextTrigger
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Represents a transformation of a [Pokemon]. This is the server side counterpart of [TransformationDisplay] and serves as
* the driver of the transformation. These are triggered by a player-driven or world-driven event.
*
* @author Segfault Guy
* @since Sept 8th, 2023
*/
interface Transformation : TransformationLike {
/** The [TransformationTrigger] to attempt this transformation. */
val trigger: TransformationTrigger
/** The [TransformationRequirement]s that need to be satisfied for this transformation. */
val requirements: Set<TransformationRequirement>
/** If this transformation can be deferred by the user and started whenever. */
val optional: Boolean
/** If this transformation needs its requirements reevaluated to remain valid in a [TransformationController]. */
val reevaluate: Boolean
/** Checks if all [TransformationRequirement]s are met. */
fun checkRequirements(pokemon: Pokemon) = this.requirements.all { requirement -> requirement.check(pokemon) }
/**
* Tests if the given [Pokemon] passes all the conditions and is ready to transform.
*
* @param pokemon The [Pokemon] being queried.
* @param context The optional [ContextTrigger.Context] needed to transform.
* @return Whether the [Transformation] can start.
*/
fun test(pokemon: Pokemon, context: ContextTrigger.Context?) = this.trigger.testTrigger(context) && this.checkRequirements(pokemon)
/**
* Starts this transformation if conditions are met.
*
* @param pokemon The [Pokemon] being transformed.
* @param context The optional [ContextTrigger.Context] needed to transform.
* @return Whether the [Transformation] was successful.
*/
fun start(pokemon: Pokemon, context: ContextTrigger.Context?): Boolean {
if (test(pokemon, context)) {
forceStart(pokemon)
return true
}
return false
}
/**
* Starts this transformation without evaluating the requirements.
*
* @param pokemon The [Pokemon] being transformed.
*/
fun forceStart(pokemon: Pokemon) { this.requirements.forEach { it.fulfill(pokemon) } }
/**
* Converts this transformation to its client side representation.
*
* @param pokemon The [Pokemon] being transformed.
* @return The [TransformationDisplay] representing this transformation on the client.
*/
fun convertToDisplay(pokemon: Pokemon): TransformationDisplay
/**
* Validates whether a transformation still meets the [TransformationRequirement]s.
* If [Transformation.reevaluate] is false, this check will be bypassed.
*
* @param pokemon the [Pokemon] to test against.
* @return Whether this transformation is still valid and can be made for [pokemon].
*/
fun reevaluate(pokemon: Pokemon) = !this.reevaluate || this.checkRequirements(pokemon)
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.transformation
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
/**
* Represents a transformation of a [Pokemon], this is the client side counterpart of [Transformation].
* This has no attachments to any data regarding the transformation itself and only serves for display purposes and basic communication.
*
* @author Licious
* @since April 28th, 2022
*/
interface TransformationDisplay : TransformationLike {
/**
* The [Species] of the transformation result.
*/
val species: Species
/**
* The aspects of the transformation result.
* These are used by the client to resolve the entity it needs to display.
*/
val aspects: Set<String>
}
@@ -6,20 +6,20 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
package com.cobblemon.mod.common.api.pokemon.transformation
/**
* Represents the base of an evolution.
* It can either be the full data container present on the server side [Evolution].
* Or the client side representation for display [EvolutionDisplay].
* Represents the base of a transformation.
* It can either be the full data container present on the server side [Transformation].
* Or the client side representation for display [TransformationDisplay].
*
* @author Licious
* @since April 28th, 2022
*/
interface EvolutionLike {
interface TransformationLike {
/**
* The unique id of the evolution.
* The unique id of the transformation.
* It should be human-readable, I.E pikachu_level
*/
val id: String
@@ -0,0 +1,35 @@
/*
* 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.pokemon.transformation.adapters
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
import com.cobblemon.mod.common.pokemon.transformation.adapters.CobblemonRequirementAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
/**
* Saves and loads [TransformationRequirement]s with JSON.
* For the default implementation see [CobblemonRequirementAdapter].
*
* @author Licious
* @since March 21st, 2022
*/
interface RequirementAdapter : JsonDeserializer<TransformationRequirement>, JsonSerializer<TransformationRequirement> {
/**
* Registers the given type of [TransformationRequirement] to it's associated ID for deserialization.
*
* @param T The type of [TransformationRequirement].
* @param id The id of the evolution event.
* @param type The [KClass] of the type.
*/
fun <T : TransformationRequirement> registerType(id: String, type: KClass<T>)
}
@@ -0,0 +1,35 @@
/*
* 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.pokemon.transformation.adapters
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
import com.cobblemon.mod.common.pokemon.transformation.adapters.CobblemonTriggerAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
/**
* Saves and loads [TransformationTrigger]s with JSON.
* For the default implementation see [CobblemonTriggerAdapter].
*
* @author Licious
* @since March 20th, 2022
*/
interface TriggerAdapter : JsonDeserializer<TransformationTrigger>, JsonSerializer<TransformationTrigger> {
/**
* Registers the given type of [TransformationTrigger] to it's associated ID for deserialization.
*
* @param T The type of [TransformationTrigger].
* @param id The id of the evolution event.
* @param type The [KClass] of the type.
*/
fun <T : TransformationTrigger> registerType(id: String, type: KClass<T>)
}
@@ -0,0 +1,106 @@
/*
* 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.pokemon.transformation.controller
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationLike
import com.cobblemon.mod.common.api.pokemon.transformation.proxy.PreProcessor
import com.cobblemon.mod.common.api.pokemon.transformation.progress.TransformationProgress
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.controller.CobblemonClientTransformationController
import com.cobblemon.mod.common.pokemon.transformation.controller.CobblemonServerTransformationController
/**
* Responsible for holding all available [TransformationLike]s in the [Pokemon].
* Also handles all the networking behind them.
* For the Cobblemon default implementations see [CobblemonClientTransformationController] & [CobblemonServerTransformationController].
*
* @author Licious
* @since April 28th, 2022
*/
interface TransformationController<T : TransformationLike, out I : PreProcessor> : MutableSet<T> {
/**
* Resolves the [Pokemon] attached to this controller.
*
* @return The [Pokemon] this controller is attached to.
*/
fun pokemon(): Pokemon
/**
* Starts the given transformation on this controller.
* All the necessary networking will be handled.
*
* @param transformation The [TransformationLike] starting.
*/
fun start(transformation: T)
/** Reverts the active [TransformationLike] for this controller's [pokemon]. */
fun revert() // TODO dunno if i like this
/**
* Returns an immutable collection of [TransformationProgress]es attached to this controller.
*
* @return The currently tracked [TransformationProgress]es.
*/
fun progress(): Collection<TransformationProgress<*>>
/**
* Attaches a [TransformationProgress] to this controller.
*
* @param P The type of [TransformationProgress].
* @param progress The [TransformationProgress] being attached for tracking.
* @return The instance being tracked.
*/
fun <P : TransformationProgress<*>> trackProgress(progress: P): P
/**
* Attempts to find an [TransformationProgress] in [progress] or creates and tracks using [trackProgress].
*
* @param P The type of [TransformationProgress].
* @param predicate The lambda attempting to find specific progress. This needs to ensure the [TransformationProgress] is of type [P] or the cast will fail.
* @param progressFactory The lambda creating a new instance of type [P] if [predicate] can't find an element.
* @return The found match in [progress] or the created and now tracked with [trackProgress].
*/
fun <P : TransformationProgress<*>> progressFirstOrCreate(predicate: (progress: TransformationProgress<*>) -> Boolean, progressFactory: () -> P): P
fun asIntermediate(): I
/**
* Guard for [start] to prevent a transformation from executing while the [pokemon] is busy or if the transformation
* is not in the controller.
*
* @param transformation the [TransformationLike] to validate.
* @return whether this controller is capable of starting the transformation.
*/
fun canStart(transformation: T): Boolean
/**
* Whether this controller is capable of executing [start] for a transformation. This checks if the [pokemon] is busy
* or if there is a transformation ready in the controller.
*
* @return whether this controller is capable of starting a transformation.
*/
fun canStart(): Boolean
/**
* Validates the [TransformationLike] currently active for this controller's [pokemon] and determines whether it can be
* reverted and if the pokemon is capable of reverting right now.
*
* @return whether the transformation can be reverted.
*/
fun canRevert(): Boolean
/**
* Retrieves the [TransformationLike] in this controller that matches the provided id.
*
* @param id the identifier of the transformation.
* @return the transformation matching this id.
*/
fun getById(id: String) = this.firstOrNull { transformation -> transformation.id.equals(id, true) }
}
@@ -6,11 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
package com.cobblemon.mod.common.api.pokemon.transformation.evolution
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.StandardPreEvolution
import com.cobblemon.mod.common.pokemon.transformation.evolution.StandardPreEvolution
/**
* Represents the previous stage in the evolutionary line of a given Pokémon.
@@ -6,25 +6,36 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.progress
package com.cobblemon.mod.common.api.pokemon.transformation.progress
import com.cobblemon.mod.common.api.data.Identifiable
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionController
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
/**
* Represents a container of progression towards the completion of an evolution requirement.
* To register an evolution progress use [EvolutionProgressTypes.registerType].
* Represents a container of progression towards the completion of a [TransformationRequirement].
* To register a transformation progress use [TransformationProgressTypes.registerType].
*
* @param T The type of the progression.
*
* @author Licious
* @since January 27th, 2023
*/
interface EvolutionProgress<T> : Identifiable {
interface TransformationProgress<T : TransformationProgress.Progress> : Identifiable {
/**
* Represents progression towards a [TransformationRequirement].
*
* @property amount The value required to be complete.
*
* @author Segfault Guy
* @since April 18th, 2025
*/
interface Progress {
val amount: Int
}
/**
* Gets the current progress of type [T].
@@ -47,7 +58,7 @@ interface EvolutionProgress<T> : Identifiable {
fun reset()
/**
* Used to decide if the given progress should be kept in the [pokemon]s [EvolutionController].
* Used to decide if the given progress should be kept in the [pokemon]s [TransformationController].
*
* @param pokemon The [Pokemon] being queried.
* @return True if the progress is still required otherwise false.
@@ -55,16 +66,16 @@ interface EvolutionProgress<T> : Identifiable {
fun shouldKeep(pokemon: Pokemon): Boolean
/**
* The [EvolutionProgressType] of this.
* The [TransformationProgressType] of this.
*
* @return The [EvolutionProgressType] of this.
* @return The [TransformationProgressType] of this.
*/
fun type(): EvolutionProgressType<*>
fun type(): TransformationProgressType<*>
companion object {
@JvmStatic
fun codec() = EvolutionProgressTypes.codec()
fun codec() = TransformationProgressTypes.codec()
}
}
@@ -6,10 +6,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.progress
package com.cobblemon.mod.common.api.pokemon.transformation.progress
import com.mojang.serialization.MapCodec
data class EvolutionProgressType<T : EvolutionProgress<*>>(
data class TransformationProgressType<T : TransformationProgress<*>>(
val codec: MapCodec<T>
)
@@ -0,0 +1,72 @@
/*
* 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.pokemon.transformation.progress
import com.cobblemon.mod.common.pokemon.transformation.progress.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.serialization.Codec
import com.mojang.serialization.Lifecycle
import net.minecraft.core.MappedRegistry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import kotlin.jvm.Throws
/**
* Registry of [TransformationProgressType]s.
*
* @author Licious
* @since July 6th, 2024
*/
object TransformationProgressTypes {
private const val ID_KEY = "id"
private val REGISTRY: Registry<TransformationProgressType<*>> = MappedRegistry(
ResourceKey.createRegistryKey(cobblemonResource("transformation_progress")), Lifecycle.stable()
)
@JvmStatic
val DAMAGE_TAKEN = this.registerType(DamageTakenTransformationProgress.ID, TransformationProgressType(DamageTakenTransformationProgress.CODEC))
@JvmStatic
val DEFEAT = this.registerType(DefeatTransformationProgress.ID, TransformationProgressType(DefeatTransformationProgress.CODEC))
@JvmStatic
val LAST_BATTLE_CRITICAL_HITS = this.registerType(LastBattleCriticalHitsTransformationProgress.ID, TransformationProgressType(LastBattleCriticalHitsTransformationProgress.CODEC))
@JvmStatic
val RECOIL = this.registerType(RecoilTransformationProgress.ID, TransformationProgressType(RecoilTransformationProgress.CODEC))
@JvmStatic
val USE_MOVE = this.registerType(UseMoveTransformationProgress.ID, TransformationProgressType(UseMoveTransformationProgress.CODEC))
/**
* Registers a transformation progress.
*
* @param T The type of the [TransformationProgress].
* @param id The [ResourceLocation].
* @param type The [TransformationProgressType] record.
* @return The registered [TransformationProgressType] of type [T].
*
* @throws [IllegalStateException] if [Registry.register] fails.
*/
@Throws(IllegalStateException::class)
@JvmStatic
fun <T : TransformationProgress<*>> registerType(id: ResourceLocation, type: TransformationProgressType<T>): TransformationProgressType<T> {
return Registry.register(REGISTRY, id, type)
}
/**
* Creates a codec instance of this registry.
*
* @return The generated [Codec] of [TransformationProgress].
*/
fun codec(): Codec<TransformationProgress<*>> = REGISTRY.byNameCodec().dispatch(
ID_KEY,
{ it.type() },
{ it.codec }
)
}
@@ -6,13 +6,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution
package com.cobblemon.mod.common.api.pokemon.transformation.proxy
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* TODO
*/
fun interface PreProcessor {
fun create(pokemon: Pokemon): EvolutionController<*, *>
fun create(pokemon: Pokemon): TransformationController<*, *>
}
@@ -0,0 +1,64 @@
/*
* 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.pokemon.transformation.proxy
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationDisplay
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationLike
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
/**
* Responsible for holding the different [TransformationController] implementations based on the logical side.
* If you haven't already checked your side previously you can use [isClient].
*
* @param C The type of [TransformationDisplay] on the client side.
* @param S The type of [Transformation] on the server side.
* @param CI The type of [PreProcessor] on the client side.
* @param SI The type of [PreProcessor] on the server side.
*
* @author Licious, Segfault Guy
* @since January 19th, 2025
*/
interface TransformationProxy<C : TransformationDisplay, S : Transformation, CI : PreProcessor, SI : PreProcessor> {
/**
* Checks if the current logical side is the client.
*
* @return True if the current logical side is the client.
*/
fun isClient(): Boolean
/**
* Returns the current [TransformationController].
* Keep in mind this will have type erasure and as such most operations will not be possible.
* It's recommended to work with [client] or [server] when possible.
*
* @return The current [TransformationController].
*/
fun current(): TransformationController<out TransformationLike, *>
/**
* The client side implementation of the [TransformationController].
*
* @throws [ClassCastException] if called from the server side.
*
* @return The client side implementation of the [TransformationController] if possible.
*/
fun client(): TransformationController<C, CI>
/**
* The server side implementation of the [TransformationController].
*
* @throws [ClassCastException] if called from the client side.
*
* @return The server side implementation of the [TransformationController] if possible.
*/
fun server(): TransformationController<S, SI>
}
@@ -6,23 +6,22 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.evolution.requirements.template
package com.cobblemon.mod.common.api.pokemon.transformation.requirement
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.entity.LivingEntity
/**
* An [EvolutionRequirement] that expects a [LivingEntity] to be attached to the [Pokemon].
* A [TransformationRequirement] that expects a [LivingEntity] to be attached to the [Pokemon].
* It can be the [PokemonEntity] if present or the [LivingEntity] that owns it.
*
* Whenever an entity can't be resolved [EvolutionRequirement.check] will never succeed.
* Whenever an entity can't be resolved [TransformationRequirement.check] will never succeed.
*
* @author Licious
* @since March 21st, 2022
*/
interface EntityQueryRequirement : EvolutionRequirement {
interface EntityQueryRequirement : TransformationRequirement {
override fun check(pokemon: Pokemon): Boolean {
val queriedEntity = pokemon.entity ?: pokemon.getOwnerPlayer() ?: return false
return this.check(pokemon, queriedEntity)
@@ -32,7 +31,7 @@ interface EntityQueryRequirement : EvolutionRequirement {
* Checks if the given [Pokemon] & [LivingEntity] satisfies the requirement.
*
* @param pokemon The [Pokemon] being queried.
* @param queriedEntity The [LivingEntity] that can be compared in context of a [Level].
* @param queriedEntity The [LivingEntity] that can be compared in context of a [net.minecraft.world.level.Level].
* @return If the requirement was satisfied.
*/
fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.transformation.requirement
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
/**
* A [TransformationRequirement] that expects the [Pokemon] to have an owner.
* It can be the [ServerPlayer] or the [NPCEntity] that owns it.
*
* Whenever an owner can't be resolved [TransformationRequirement.check] will never succeed.
*
* @author Polymeta
* @since May 6th, 2025
*/
interface OwnerQueryRequirement : TransformationRequirement {
override fun check(pokemon: Pokemon): Boolean {
val owner = pokemon.getOwnerEntity() ?: return false
return when (owner) {
is ServerPlayer -> {
this.checkPlayer(pokemon, owner)
}
is NPCEntity -> {
this.checkNPC(pokemon, owner)
}
else -> false
}
}
/**
* Checks if the given [Pokemon] & [ServerPlayer] satisfies the requirement.
*
* @param pokemon The [Pokemon] being queried.
* @param owner The [ServerPlayer] that owns this [Pokemon].
* @return If the requirement was satisfied.
*/
fun checkPlayer(pokemon: Pokemon, owner: ServerPlayer): Boolean
/**
* Checks if the given [Pokemon] & [NPCEntity] satisfies the requirement.
* This method mainly exists as NPCs are different to players in a lot of ways (e.g., lack of advancements),
* so they need to be handled differently, either by checking an NPC config or always returning true/false
*
* @param pokemon The [Pokemon] being queried.
* @param owner The [NPCEntity] that owns this [Pokemon].
* @return If the requirement was satisfied.
*/
fun checkNPC(pokemon: Pokemon, owner: NPCEntity): Boolean
}
@@ -6,23 +6,22 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.evolution.requirement
package com.cobblemon.mod.common.api.pokemon.transformation.requirement
import com.cobblemon.mod.common.api.pokemon.evolution.ContextEvolution
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.ContextTrigger
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Represents a requirement for an evolution.
* Represents a requirement for a transformation.
* Requirements are constant and will never change regardless of the backing action.
* For dynamic requirements see [ContextEvolution].
*
* See [Evolution.requirements] & [Evolution.test] for usage.
* See [Transformation.requirements] & [Transformation.test] for usage.
*
* @author Licious
* @since March 19th, 2022
*/
fun interface EvolutionRequirement {
fun interface TransformationRequirement {
/**
* Checks if the given [Pokemon] satisfies the requirement.
@@ -32,4 +31,10 @@ fun interface EvolutionRequirement {
*/
fun check(pokemon: Pokemon): Boolean
/**
* Callback after the [Transformation] has been performed.
*
* @param pokemon The [Pokemon] being queried.
*/
fun fulfill(pokemon: Pokemon) {}
}
@@ -0,0 +1,55 @@
/*
* 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.pokemon.transformation.trigger
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.triggers.ItemInteractionTrigger
import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
/**
* Represents a [TransformationTrigger] of a [Pokemon] that can only occur during specific actions and with added context.
* For the default implementations see [ItemInteractionTrigger] & [TradeTrigger].
*
* @param TC The context that is serialized from JSON during species loading. This is what the [Context] provided
* to [testTrigger] is expected to match against.
*
* @author Licious
* @since March 19th, 2022
*/
interface ContextTrigger<TC>: TransformationTrigger {
/**
* Represents context provided for [TransformationTrigger] evaluation.
*
* @author Segfault Guy
* @since October 15th, 2023
*/
interface Context
/** The target context required for this [Transformation] to be successfully triggered. */
val requiredContext: TC
/**
* Checks if the given context is valid for the [requiredContext].
*
* @param context The [Context] given at runtime when querying the [Transformation].
* @return If the context matched the [requiredContext].
*/
fun testContext(context: Context): Boolean = false
/**
* Evaluates the triggering condition of a [Transformation].
*
* @param context The [Context] of the [Transformation].
* @return Whether the triggering condition is satisfied. If [context] is null, bypasses the condition check.
*/
override fun testTrigger(context: Context?) = context?.let { testContext(it) } ?: true
}
@@ -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.pokemon.transformation.trigger
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.transformation.triggers.LevelUpTrigger
/**
* Represents a [TransformationTrigger] of a [Pokemon] that is attempted periodically without any additional context or actions.
* For the default implementation see [LevelUpTrigger].
*
* Triggers that implement this marker will be tested every second.
*
* @author Licious
* @since March 19th, 2022
*/
interface PassiveTrigger : TransformationTrigger
@@ -0,0 +1,29 @@
/*
* 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.pokemon.transformation.trigger
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
/**
* Represents a condition that starts a [Transformation].
*
* @author Segfault Guy
* @since Sept 8th, 2023
*/
interface TransformationTrigger {
/**
* Evaluates the triggering condition of a [Transformation].
*
* @param context The optional [ContextTrigger.Context] for [ContextTrigger] evaluation.
* @return Whether the triggering condition is satisfied.
*/
fun testTrigger(context: ContextTrigger.Context?): Boolean = true
}
@@ -18,6 +18,5 @@ package com.cobblemon.mod.common.api.riding
enum class RidingStyle {
LAND,
LIQUID,
AIR,
COMPOSITE
AIR
}
@@ -28,7 +28,8 @@ import net.minecraft.world.phys.Vec3
*/
interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBehaviourState> {
val key: ResourceLocation
val style: RidingStyle
fun getRidingStyle(settings: Settings, state: State): RidingStyle
fun isActive(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -25,7 +26,10 @@ import net.minecraft.world.phys.Vec3
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
override val key = behaviour.key
override val style = behaviour.style
override fun getRidingStyle(settings: Settings, state: State): RidingStyle {
return behaviour.getRidingStyle(settings, state)
}
override fun isActive(settings: Settings, state: State, vehicle: PokemonEntity) =
behaviour.isActive(settings, state, vehicle)
@@ -175,4 +179,5 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
return behaviour.createDefaultState(settings)
}
}
@@ -38,7 +38,10 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
override val key: ResourceLocation = KEY
override val style = RidingStyle.AIR
override fun getRidingStyle(settings: BirdAirSettings, state: BirdAirState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<BirdAirSettings, BirdAirState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
@@ -35,7 +35,10 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override val key = KEY
override val style = RidingStyle.LIQUID
override fun getRidingStyle(settings: DolphinSettings, state: DolphinState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
@@ -33,7 +33,10 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
}
override val key = KEY
override val style = RidingStyle.LAND
override fun getRidingStyle(settings: GenericLandSettings, state: GenericLandState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<GenericLandSettings, GenericLandState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
@@ -35,7 +35,10 @@ class GenericSwimBehaviour : RidingBehaviour<GenericSwimSettings, RidingBehaviou
}
override val key = KEY
override val style = RidingStyle.LIQUID
override fun getRidingStyle(settings: GenericSwimSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<GenericSwimSettings, RidingBehaviourState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
@@ -35,7 +35,10 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
}
override val key = KEY
override val style = RidingStyle.AIR
override fun getRidingStyle(settings: GliderAirSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<GliderAirSettings, RidingBehaviourState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
@@ -39,7 +39,10 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
}
override val key = KEY
override val style = RidingStyle.AIR
override fun getRidingStyle(settings: HelicopterSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<HelicopterSettings, RidingBehaviourState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
@@ -33,7 +33,10 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
}
override val key = KEY
override val style = RidingStyle.AIR
override fun getRidingStyle(settings: HoverSettings, state: HoverState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<HoverSettings, HoverState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
@@ -35,7 +35,10 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
}
override val key = KEY
override val style = RidingStyle.AIR
override fun getRidingStyle(settings: JetAirSettings, state: JetAirState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<JetAirSettings, JetAirState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
@@ -34,7 +34,10 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
}
override val key = KEY
override val style = RidingStyle.LIQUID
override fun getRidingStyle(settings: SwimDashSettings, state: SwimDashState): RidingStyle {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<SwimDashSettings, SwimDashState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
@@ -53,7 +53,10 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
}
override val key = KEY
override val style = RidingStyle.LAND
override fun getRidingStyle(settings: VehicleLandSettings, state: VehicleLandState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<VehicleLandSettings, VehicleLandState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
@@ -30,8 +30,14 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
}
override val key = KEY
override val style = RidingStyle.COMPOSITE
override fun getRidingStyle(settings: CompositeSettings, state: CompositeState): RidingStyle {
return chooseBehaviour(settings, state) {
behaviour, behaviourSettings, behaviourState ->
behaviour.getRidingStyle(behaviourSettings, behaviourState)
}
}
override fun createDefaultState(settings: CompositeSettings): CompositeState {
val defaultBehaviour = RidingBehaviours.get(settings.defaultBehaviour.key)
val defaultState = defaultBehaviour.createDefaultState(settings.defaultBehaviour)
@@ -51,7 +57,7 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
): T {
val defaultBehaviour = RidingBehaviours.get(settings.defaultBehaviour.key)
val alternativeBehaviour = RidingBehaviours.get(settings.alternateBehaviour.key)
return when (state.activeController.get()) {
return when (state.activeBehaviour.get()) {
settings.defaultBehaviour.key -> action(
defaultBehaviour,
settings.defaultBehaviour,
@@ -63,7 +69,7 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
state.alternateBehaviourState
)
else -> error("Invalid controller: ${state.activeController.get()}")
else -> error("Invalid controller: ${state.activeBehaviour.get()}")
}
}
@@ -364,28 +370,28 @@ open class CompositeSettings : RidingBehaviourSettings {
}
class CompositeState(
private val defaultBehaviour: ResourceLocation,
val defaultBehaviour: ResourceLocation,
val defaultBehaviourState: RidingBehaviourState,
val alternateBehaviourState: RidingBehaviourState
) : RidingBehaviourState() {
var activeController = ridingState(defaultBehaviour, Side.CLIENT)
var activeBehaviour = ridingState(defaultBehaviour, Side.CLIENT)
var lastTransition = ridingState(-100L, Side.BOTH)
override val rideVelocity: SidedRidingState<Vec3>
get() = when (activeController.get()) {
get() = when (activeBehaviour.get()) {
defaultBehaviour -> defaultBehaviourState.rideVelocity
else -> alternateBehaviourState.rideVelocity
}
override val stamina: SidedRidingState<Float>
get() = when (activeController.get()) {
get() = when (activeBehaviour.get()) {
defaultBehaviour -> defaultBehaviourState.stamina
else -> alternateBehaviourState.stamina
}
override fun reset() {
super.reset()
activeController.set(defaultBehaviour, forced = true)
activeBehaviour.set(defaultBehaviour, forced = true)
lastTransition.set(-100L, forced = true)
defaultBehaviourState.reset()
alternateBehaviourState.reset()
@@ -394,7 +400,7 @@ class CompositeState(
override fun copy(): CompositeState {
val state =
CompositeState(defaultBehaviour, defaultBehaviourState.copy(), alternateBehaviourState.copy())
state.activeController.set(activeController.get(), forced = true)
state.activeBehaviour.set(activeBehaviour.get(), forced = true)
state.lastTransition.set(lastTransition.get(), forced = true)
state.rideVelocity.set(rideVelocity.get(), forced = true)
state.stamina.set(stamina.get(), forced = true)
@@ -403,7 +409,7 @@ class CompositeState(
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is CompositeState) return false
if (previous.activeController.get() != activeController.get()) return true
if (previous.activeBehaviour.get() != activeBehaviour.get()) return true
if (defaultBehaviourState.shouldSync(previous.defaultBehaviourState)) return true
if (alternateBehaviourState.shouldSync(previous.alternateBehaviourState)) return true
return super.shouldSync(previous)
@@ -411,14 +417,14 @@ class CompositeState(
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeResourceLocation(activeController.get())
buffer.writeResourceLocation(activeBehaviour.get())
defaultBehaviourState.encode(buffer)
alternateBehaviourState.encode(buffer)
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
activeController.set(buffer.readResourceLocation(), forced = true)
activeBehaviour.set(buffer.readResourceLocation(), forced = true)
defaultBehaviourState.decode(buffer)
alternateBehaviourState.decode(buffer)
}
@@ -44,10 +44,10 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
toState.stamina.set(fromState.stamina.get())
toState.rideVelocity.set(fromState.rideVelocity.get())
fromState.reset()
state.activeController.set(toSettings.key)
state.activeBehaviour.set(toSettings.key)
state.lastTransition.set(vehicle.level().gameTime)
val behaviour = RidingBehaviours.get(toSettings.key)
vehicle.setBehaviourFlag(PokemonBehaviourFlag.FLYING, behaviour.style == RidingStyle.AIR)
vehicle.setBehaviourFlag(PokemonBehaviourFlag.FLYING, behaviour.getRidingStyle(toSettings, toState) == RidingStyle.AIR)
}
}
@@ -9,13 +9,11 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -47,7 +45,7 @@ object FallStrategy : CompositeRidingStrategy<FallCompositeSettings> {
defaultSettings: RidingBehaviourSettings,
vehicle: PokemonEntity
): Boolean {
if (state.activeController.get() == defaultSettings.key) return false
if (state.activeBehaviour.get() == defaultSettings.key) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
if (!vehicle.onGround()) return false
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
@@ -61,7 +59,7 @@ object FallStrategy : CompositeRidingStrategy<FallCompositeSettings> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
if (state.activeController.get() == alternateSettings.key) return false
if (state.activeBehaviour.get() == alternateSettings.key) return false
if (!driver.jumping) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
@@ -8,13 +8,11 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.world.entity.player.Player
@@ -45,7 +43,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
defaultSettings: RidingBehaviourSettings,
vehicle: PokemonEntity
): Boolean {
if (state.activeController.get() == defaultSettings.key) return false
if (state.activeBehaviour.get() == defaultSettings.key) return false
if (!vehicle.onGround()) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
@@ -58,7 +56,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
if (state.activeController.get() == alternativeSettings.key) return false
if (state.activeBehaviour.get() == alternativeSettings.key) return false
if (!driver.jumping) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
@@ -45,7 +45,7 @@ object RunStrategy : CompositeRidingStrategy<CompositeSettings> {
entity: PokemonEntity,
driver: Player
): Boolean {
if (state.activeController.get() == defaultSettings.key) return false
if (state.activeBehaviour.get() == defaultSettings.key) return false
if (!entity.onGround()) return false
if (state.lastTransition.get() + 20 >= entity.level().gameTime) return false
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
@@ -60,7 +60,7 @@ object RunStrategy : CompositeRidingStrategy<CompositeSettings> {
driver: Player,
input: Vec3
): Boolean {
if (state.activeController.get() == alternativeSettings.key) return false
if (state.activeBehaviour.get() == alternativeSettings.key) return false
if (state.lastTransition.get() + 20 >= entity.level().gameTime) return false
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
return driver.isSprinting && input.z > 0.5 && alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, entity)
@@ -55,6 +55,7 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
CobblemonSpawnPools.onServerLoad(server)
}
precalculate()
this.observable.emit(this)
}
val details = mutableListOf<SpawnDetail>()
@@ -12,16 +12,14 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokemonGainedEvent
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.PassiveTrigger
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
import com.cobblemon.mod.common.util.*
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraft.network.chat.Component
import java.util.*
@@ -143,14 +141,16 @@ open class PlayerPartyStore(
}
}
// Passive evolutions
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
val removeList = mutableListOf<Evolution>()
pokemon.evolutionProxy.server().forEach {
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
removeList.add(it)
}
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
// Passive evolutions and form changes
pokemon.triggerTransformations(PassiveTrigger::class.java)
// Reevaluate active form
val form = pokemon.form
if (form is TemporaryForm && !form.reevaluate(pokemon)) pokemon.formProxy.server().revert() // TODO check locks?
// Reevaluate transformation requirements and remove invalid transformations from the controller
pokemon.formProxy.server().reevaluate()
pokemon.evolutionProxy.server().reevaluate()
}
// Friendship
// ToDo expand this down the line just a very basic implementation for the first releases
@@ -127,7 +127,8 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
// No PP or disabled or something
return false
}
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val availableTargets = (if (gimmickID != null && validGimmickMove) gimmickMove.target else move.target)
.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.battles.interpreter.*
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
@@ -22,9 +21,6 @@ import com.cobblemon.mod.common.battles.interpreter.instructions.*
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.runOnServer
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3
import java.util.UUID
import kotlin.collections.Iterator
import kotlin.collections.filter
@@ -36,7 +32,6 @@ import kotlin.collections.mutableMapOf
import kotlin.collections.set
import kotlin.collections.toMutableList
import kotlin.collections.toTypedArray
import kotlin.time.measureTime
@Suppress("KotlinPlaceholderCountMatchesArgumentCount", "UNUSED_PARAMETER")
object ShowdownInterpreter {
@@ -72,7 +67,7 @@ object ShowdownInterpreter {
updateInstructionParser["-copyboost"] = { _, _, message, _ -> CopyBoostInstruction(message) }
updateInstructionParser["-crit"] = { _, instructionSet, message, _ -> CritInstruction(message, instructionSet) }
updateInstructionParser["-curestatus"] = { _, _, message, _ -> CureStatusInstruction(message) }
updateInstructionParser["detailschange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
updateInstructionParser["detailschange"] = { _, _, message, _ -> FormChangeInstruction(message) }
updateInstructionParser["-endability"] = { _, _, message, _ -> EndAbilityInstruction(message) }
updateInstructionParser["-end"] = { _, _, message, _ -> EndInstruction(message) }
updateInstructionParser["-enditem"] = { _, _, message, _ -> EndItemInstruction(message) }
@@ -81,7 +76,7 @@ object ShowdownInterpreter {
updateInstructionParser["-fieldactivate"] = { _, _, message, _ -> FieldActivateInstruction(message) }
updateInstructionParser["-fieldend"] = { _, _, message, _ -> FieldEndInstruction(message) }
updateInstructionParser["-fieldstart"] = { _, _, message, _ -> FieldStartInstruction(message) }
updateInstructionParser["-formechange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
updateInstructionParser["-formechange"] = { _, _, message, _ -> FormChangeInstruction(message) }
updateInstructionParser["-hitcount"] = { _, _, message, _ -> HitCountInstruction(message) }
updateInstructionParser["-immune"] = { _, _, message, _ -> ImmuneInstruction(message) }
updateInstructionParser["-invertboost"] = { _, _, message, _ -> InvertBoostInstruction(message) }
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.pokemon.transformation.progress.LastBattleCriticalHitsTransformationProgress
import com.cobblemon.mod.common.util.battleLang
/**
* Format: |-crit|POKEMON
@@ -39,9 +39,10 @@ class CritInstruction(val message: BattleMessage, val instructionSet: Instructio
} else {
battle.broadcastChatMessage(battleLang("crit").yellow())
}
if (LastBattleCriticalHitsEvolutionProgress.supports(battlePokemon.effectedPokemon)) {
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsEvolutionProgress }) { LastBattleCriticalHitsEvolutionProgress() }
progress.updateProgress(LastBattleCriticalHitsEvolutionProgress.Progress(progress.currentProgress().amount + 1))
if (LastBattleCriticalHitsTransformationProgress.supports(battlePokemon.effectedPokemon)) {
// TODO refactor and apply to forms
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsTransformationProgress }) { LastBattleCriticalHitsTransformationProgress() }
progress.updateProgress(LastBattleCriticalHitsTransformationProgress.Progress(progress.currentProgress().amount + 1))
}
}
battle.minorBattleActions[pokemon.uuid] = message
@@ -25,10 +25,9 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.transformation.progress.DamageTakenTransformationProgress
import com.cobblemon.mod.common.pokemon.transformation.progress.RecoilTransformationProgress
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
@@ -75,7 +74,7 @@ class DamageInstruction(
private fun doRecoilEvoChecks(battlePokemon: BattlePokemon) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
if (RecoilTransformationProgress.supports(pokemon)) {
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException(
"Cant get recoil string"
)
@@ -84,9 +83,10 @@ class DamageInstruction(
?: throw UnsupportedOperationException("Cant get recoil string")
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
// TODO refactor and apply to forms
val progress = pokemon.evolutionProxy.current()
.progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
.progressFirstOrCreate({ it is RecoilTransformationProgress }) { RecoilTransformationProgress() }
progress.updateProgress(RecoilTransformationProgress.Progress(progress.currentProgress().amount + difference))
}
}
}
@@ -180,9 +180,10 @@ class DamageInstruction(
battlePokemon.effectedPokemon.currentHealth = remainingHealth
if (difference > 0) {
battlePokemon.effectedPokemon.let { pokemon ->
if (DamageTakenEvolutionProgress.supports(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
if (DamageTakenTransformationProgress.supports(pokemon)) {
// TODO refactor and apply to forms
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenTransformationProgress }) { DamageTakenTransformationProgress() }
progress.updateProgress(DamageTakenTransformationProgress.Progress(progress.currentProgress().amount + difference))
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
/**
* Format: |-formechange|POKEMON|SPECIES|HP STATUS or |detailschange|POKEMON|DETAILS|HP STATUS
*
* POKEMON has temporarily (-formechange) or permanently (detailschange) changed form to SPECIES/DETAILS.
* @author Segfault Guy
* @since December 9th, 2024
*/
class FormChangeInstruction(val message: BattleMessage): ActionEffectInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("formchange")
lateinit var battlePokemon: BattlePokemon
lateinit var speciesName: String
lateinit var formId: String
lateinit var typeKey: String
lateinit var pnx: String
lateinit var form: FormData
override fun preActionEffect(battle: PokemonBattle) {
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
battlePokemon = message.battlePokemon(0, battle) ?: return
speciesName = details.substringBefore('-')
formId = details.substringAfter('-').ifBlank { speciesName }
typeKey = if (message.id == "detailschange") "permanent" else "temporary"
pnx = message.pnxAndUuid(0)?.first ?: return
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), battlePokemon)
battle.dispatchGo {
battle.minorBattleActions[battlePokemon.uuid] = message
form = battlePokemon.effectedPokemon.species.getFormByShowdownId(formId)
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formId))
}
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
if (form is TemporaryForm) {
val actionEffect = (form as TemporaryForm).actionEffect
val providers = mutableListOf<Any>(battle)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
)
(form as TemporaryForm).forceStart(battlePokemon.effectedPokemon.entity!!, context)
}
else {
battlePokemon.effectedPokemon.formProxy.server().revert()
}
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatchWaiting {
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formId))
val pokemonName = battlePokemon.getName()
val lang = when (formId) {
"busted", "hero", "complete" -> return@dispatchWaiting
"school", "wishiwashi", "meteor", "minior" -> battleLang("formechange.$formId", pokemonName)
speciesName -> battleLang("formechange.default.temporary.end", pokemonName)
else -> battleLang("formechange.default.$typeKey", pokemonName, formId)
}
battle.sendSidedUpdate(
source = battlePokemon.actor,
allyPacket = BattleTransformPokemonPacket(pnx, battlePokemon, true),
opponentPacket = BattleTransformPokemonPacket(pnx, battlePokemon, false)
)
battle.broadcastChatMessage(lang)
}
}
}
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.util.battleLang
/**
* Format: |-formechange|POKEMON|SPECIES|HP STATUS or |detailschange|POKEMON|DETAILS|HP STATUS
*
* POKEMON has temporarily (-formechange) or permanently (detailschange) changed form to SPECIES/DETAILS.
* @author Segfault Guy
* @since December 9th, 2024
*/
class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction {
override fun invoke(battle: PokemonBattle) {
val battlePokemon = message.battlePokemon(0, battle) ?: return
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
val speciesName = details.substringBefore('-')
val formName = details.substringAfterLast('-').ifBlank { speciesName }
val typeKey = if (message.id == "detailschange") "permanent" else "temporary"
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), battlePokemon)
battle.dispatchWaiting {
battle.minorBattleActions[battlePokemon.uuid] = message
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
val pokemonName = battlePokemon.getName()
val lang = when(formName) {
"busted", "hero", "complete" -> return@dispatchWaiting
"school", "wishiwashi", "meteor", "minior" -> battleLang("formechange.$formName", pokemonName)
speciesName -> battleLang("formechange.default.temporary.end", pokemonName)
else -> battleLang("formechange.default.$typeKey", pokemonName, formName)
}
battle.broadcastChatMessage(lang)
}
}
}
@@ -26,7 +26,7 @@ import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
import com.cobblemon.mod.common.pokemon.transformation.progress.UseMoveTransformationProgress
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
@@ -70,9 +70,10 @@ class MoveInstruction(
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
userPokemon.effectedPokemon.let { pokemon ->
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
// TODO refactor and apply to forms
if (UseMoveTransformationProgress.supports(pokemon, move)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveTransformationProgress && it.currentProgress().move == move }) { UseMoveTransformationProgress() }
progress.updateProgress(UseMoveTransformationProgress.Progress(move, progress.currentProgress().amount + 1))
}
}
@@ -27,8 +27,8 @@ import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.server
import java.util.UUID
import net.minecraft.network.chat.MutableComponent
import java.util.function.Function
@@ -42,10 +42,12 @@ open class BattlePokemon(
companion object {
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
//TOOD figure out a closer registry access (might have to break some method signatures for this (1.7?)
val effectedPokemon = pokemon.clone(registryAccess = server()?.registryAccess() ?: throw IllegalStateException("No registry access available"))
val effectedPokemon = pokemon.clone()
BattleCloneProperty.isBattleClone().apply(effectedPokemon)
UncatchableProperty.uncatchable().apply(effectedPokemon)
val shouldRevertForm = (effectedPokemon.form as? TemporaryForm)?.revertBeforeBattle ?: (effectedPokemon.form as? TemporaryForm)?.battleOnly ?: false
if (shouldRevertForm)
effectedPokemon.formProxy.server().revert()
return BattlePokemon(
originalPokemon = pokemon,
effectedPokemon = effectedPokemon,
@@ -53,13 +55,18 @@ open class BattlePokemon(
)
}
fun playerOwned(pokemon: Pokemon): BattlePokemon = BattlePokemon(
originalPokemon = pokemon,
effectedPokemon = pokemon,
postBattleEntityOperation = { entity ->
entity.effects.wipe()
}
)
fun playerOwned(pokemon: Pokemon): BattlePokemon {
val shouldRevertForm = (pokemon.form as? TemporaryForm)?.revertBeforeBattle ?: (pokemon.form as? TemporaryForm)?.battleOnly ?: false
if (shouldRevertForm)
pokemon.formProxy.server().revert()
return BattlePokemon(
originalPokemon = pokemon,
effectedPokemon = pokemon,
postBattleEntityOperation = { entity ->
entity.effects.wipe()
}
)
}
}
val struct = QueryStruct(
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.battles.runner
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
import com.cobblemon.mod.common.battles.runner.socket.SocketShowdownService
import com.google.gson.JsonArray
import java.util.UUID
@@ -157,12 +157,16 @@ class GraalShowdownService : ShowdownService {
val jsArray = this.context.eval("js", "new Array();")
var index = 0L
PokemonSpecies.species.forEach { species ->
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, null)))
species.forms.forEach { form ->
if (form != species.standardForm) {
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, form)))
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownStandardForm(species.standardForm)))
species.permanentForms.forEach { permForm ->
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownPermanentForm(permForm)))
permForm.changeableForms.forEach { tempForm ->
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownTemporaryForm(tempForm)))
}
}
species.temporaryForms.forEach { tempForm ->
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownTemporaryForm(tempForm)))
}
}
receiveSpeciesDataFn.execute(jsArray)
}
@@ -123,8 +123,8 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
return gson.fromJson(response, JsonArray::class.java)
}
private fun sendSpeciesData(species: Species, form: FormData?) {
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
private fun sendSpeciesData(species: PokemonSpecies.ShowdownSpecies) {
writer.write(">receiveSpeciesData ${gson.toJson(species)}\n")
acknowledge()
}
@@ -137,12 +137,16 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
writer.write(">resetSpeciesData\n")
acknowledge()
PokemonSpecies.species.forEach { species ->
sendSpeciesData(species, null)
species.forms.forEach { form ->
if (form != species.standardForm) {
sendSpeciesData(species, form)
sendSpeciesData(PokemonSpecies.ShowdownStandardForm(species.standardForm))
species.permanentForms.forEach { permForm ->
sendSpeciesData(PokemonSpecies.ShowdownPermanentForm(permForm))
permForm.changeableForms.forEach { tempForm ->
sendSpeciesData(PokemonSpecies.ShowdownTemporaryForm(tempForm))
}
}
species.temporaryForms.forEach { form ->
sendSpeciesData(PokemonSpecies.ShowdownTemporaryForm(form))
}
}
}
@@ -1,133 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.phys.BlockHitResult
class StadiumBlock(properties: Properties): BaseEntityBlock(properties) {
companion object {
val CODEC = simpleCodec(::StadiumBlock)
}
init {
registerDefaultState(stateDefinition.any().setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH))
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return StadiumBlockEntity(pos, state)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return this.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HorizontalDirectionalBlock.FACING)
}
override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
return blockState.setValue(
HorizontalDirectionalBlock.FACING, rotation.rotate(blockState.getValue(
HorizontalDirectionalBlock.FACING)))
}
override fun mirror(blockState: BlockState, mirror: Mirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
}
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
}
//TODO: override getTicker
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun useWithoutItem(
state: BlockState,
world: Level,
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(pos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
player.sendSystemMessage(text("Adding you to Battle").red())
blockEntity.addPlayer(player)
return InteractionResult.SUCCESS
}
/*
override fun useWithoutItem(
blockState: BlockState,
world: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(blockPos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
// Toggle isInUse state
blockEntity.isInUse = !blockEntity.isInUse
// debug
val activateMessage = if (blockEntity.isInUse) "Stadium Activated!" else "Stadium Deactivated!"
player.sendSystemMessage(text(activateMessage).red())
if (blockEntity.isInUse) {
// Generate flight path when activated
blockEntity.generateFlightPath(world, radiusX = 5.0, radiusZ = 3.0, height = 10.0, steps = 20)
} else {
// Clear path when deactivated
blockEntity.pathPoints.clear()
}
val message = "Stadium Pos: " + blockEntity.blockPos +
"\nPlayer 1 Pos: " + blockEntity.getPlayer1Pos() +
"\nPoke 1 Pos: " + blockEntity.getPoke1Pos() +
"\nPlayer 2 Pos: " + blockEntity.getPlayer2Pos() +
"\nPoke 2 Pos: " + blockEntity.getPoke2Pos()
player.sendSystemMessage(text(message).red())
blockEntity.setChanged()
world.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
return InteractionResult.SUCCESS
}
*/
}
@@ -1,143 +0,0 @@
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
import com.cobblemon.mod.common.stadium.StadiumSingleBattle
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.Vec3
class StadiumBlockEntity(
pos: BlockPos,
blockState: BlockState
): BlockEntity(
CobblemonBlockEntities.STADIUM,
pos,
blockState
) {
var storedPlayers = mutableListOf<ServerPlayer>()
var battleDefenition = StadiumSingleBattle()
fun addPlayer(player: ServerPlayer) {
battleDefenition.addPlayer(player)
if (battleDefenition.readyForBattle()) battleDefenition.initializeBattle()
}
/*
var isInUse = false
val droneState = DroneState()
val pathPoints: MutableList<Vec3> = mutableListOf()
fun generateFlightPath(world: Level, radiusX: Double, radiusZ: Double, height: Double, steps: Int) {
pathPoints.clear()
for (i in 0 until steps) {
val angle = (i.toDouble() / steps) * Math.PI * 2
val xOffset = radiusX * Math.cos(angle)
val zOffset = radiusZ * Math.sin(angle)
val basePos = Vec3(blockPos.x + xOffset, blockPos.y + height, blockPos.z + zOffset)
val adjustedPos = getValidPoint(world, basePos)
pathPoints.add(adjustedPos)
}
}
private fun getValidPoint(world: Level, start: Vec3): Vec3 {
val maxHeight = 40 // Max height for drone path
val stepHeight = 1.0 // Step size for raycast
var adjustedHeight = start.y
for (h in 0..maxHeight step stepHeight.toInt()) {
val checkPos = BlockPos(start.x.toInt(), start.y.toInt() - h, start.z.toInt()) // Convert to BlockPos
if (!world.getBlockState(checkPos).isAir) { // Check if block is solid
return Vec3(start.x, start.y - h - 1, start.z) // Adjust position
}
}
return start // No obstruction found
}
fun getPlayer1Pos(): BlockPos {
return blockPos.north(10)
}
fun getPlayer2Pos(): BlockPos {
return blockPos.south(10)
}
fun getPoke1Pos(): BlockPos {
return blockPos.north(5)
}
fun getPoke2Pos(): BlockPos {
return blockPos.south(5)
}
/**
* Saves data to NBT.
*/
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
// Save the isInUse state
tag.putBoolean("IsInUse", isInUse)
// Save path points
val pathTag = ListTag()
for (point in pathPoints) {
val pointTag = CompoundTag()
pointTag.putDouble("x", point.x)
pointTag.putDouble("y", point.y)
pointTag.putDouble("z", point.z)
pathTag.add(pointTag)
}
tag.put("PathPoints", pathTag)
}
/**
* Loads data from NBT.
*/
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
// Load the isInUse state
isInUse = tag.getBoolean("IsInUse")
// Load path points
pathPoints.clear()
val pathTag = tag.getList("PathPoints", 10) // 10 is the ID for CompoundTag
for (i in 0 until pathTag.size) {
val pointTag = pathTag.getCompound(i)
val point = Vec3(pointTag.getDouble("x"), pointTag.getDouble("y"), pointTag.getDouble("z"))
pathPoints.add(point)
}
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
//println("getUpdatePacket called for NestBlockEntity at $blockPos with egg: $eggComponent")
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
val tag = saveWithoutMetadata(registryLookup)
//println("getUpdateTag called for NestBlockEntity at $blockPos with tag: $tag")
return tag
}
*/
}
@@ -26,7 +26,13 @@ import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
@@ -54,7 +60,6 @@ import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
import com.cobblemon.mod.common.stadium.StadiumClientManager
import com.cobblemon.mod.common.util.isLookingAt
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
@@ -111,7 +116,6 @@ object CobblemonClient {
CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
StadiumClientManager.onLogout()
}
fun initialize(implementation: CobblemonClientImplementation) {
@@ -256,8 +260,7 @@ object CobblemonClient {
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
CobblemonBlocks.GIMMIGHOUL_CHEST,
CobblemonBlocks.DISPLAY_CASE,
CobblemonBlocks.LECTERN,
CobblemonBlocks.STADIUM
CobblemonBlocks.LECTERN
)
this.createBoatModelLayers()
@@ -291,7 +294,6 @@ object CobblemonClient {
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.STADIUM, ::StadiumBlockRenderer)
}
private fun registerEntityRenderers() {
@@ -342,4 +344,6 @@ object CobblemonClient {
}
}
/** Checks if the player is preoccupied and cannot accept an action. */
fun isBusy() = battle != null || trade != null
}
@@ -14,7 +14,9 @@ import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.ForcePassActionResponse
import com.cobblemon.mod.common.battles.MoveActionResponse
import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ShowdownMoveset
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import java.util.UUID
@@ -80,6 +82,10 @@ class ClientBattle(
}
}
fun pendingGimmick(gimmick: ShowdownMoveset.Gimmick): Boolean {
return pendingActionRequests.none { (it.response as? MoveActionResponse)?.gimmickID == gimmick.id }
}
fun getPokemonFromPNX(pnx: String): Pair<ClientBattleActor, ActiveClientBattlePokemon> {
val actor = sides.flatMap { it.actors }.find { it.showdownId == pnx.substring(0, 2) }
?: throw IllegalStateException("Invalid pnx: $pnx - unknown actor")
@@ -58,8 +58,8 @@ class NPCClientDelegate : PosableState(), NPCSideDelegate {
override fun addToStruct(struct: QueryStruct) {
super.addToStruct(struct)
struct.addFunctions(functions.functions)
struct.addFunctions(ClientMoLangFunctions.clientFunctions)
struct.addFunctions(functions.functions)
runtime.environment.query.addFunctions(struct.functions)
}
}
@@ -104,6 +104,11 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
// force a model update - handles edge case where the PosableState's tracked PosableModel isn't updated until the LivingEntityRenderer render is run
currentModel = VaryingModelRepository.getPoser(identifier, this)
currentEntity.refreshRiding()
}
else if (data == PokemonEntity.FORM) {
val formId = currentEntity.entityData.get(PokemonEntity.FORM)
currentEntity.pokemon.form = currentEntity.pokemon.species.getFormByShowdownId(formId)
currentEntity.refreshRiding()
} else if (data == PokemonEntity.ASPECTS) {
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
currentEntity.pokemon.shiny = currentAspects.contains("shiny")
@@ -340,8 +345,8 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
override fun addToStruct(struct: QueryStruct) {
super.addToStruct(struct)
struct.addFunctions(functions.functions)
struct.addFunctions(ClientMoLangFunctions.clientFunctions)
struct.addFunctions(functions.functions)
runtime.environment.query = struct
}
@@ -75,6 +75,7 @@ fun drawProfilePokemon(
scale: Float = 20F,
applyProfileTransform: Boolean = true,
applyBaseScale: Boolean = false,
doQuirks: Boolean = false,
r: Float = 1F,
g: Float = 1F,
b: Float = 1F,
@@ -101,7 +102,7 @@ fun drawProfilePokemon(
context.put(RenderContext.ASPECTS, state.currentAspects)
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.PROFILE)
context.put(RenderContext.POSABLE_STATE, state)
context.put(RenderContext.DO_QUIRKS, false)
context.put(RenderContext.DO_QUIRKS, doQuirks)
state.currentModel = model

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