Compare commits

..
Author SHA1 Message Date
amo c7767e68bc Merge branch 'main' into 'observationally-overboard'
Adding an implementation of Observable Collections & utilities:

See merge request cable-mc/cobblemon!1109
2024-09-07 12:54:52 +00:00
amo 444a70e1fa e 2024-09-07 13:52:44 +01:00
Hiroku c19444816a Merge branch 'log-async' into 'main'
More safely check thread for spawners and influencers access and fix internal illegal access

See merge request cable-mc/cobblemon!1123
2024-09-07 10:11:27 +00:00
Paul 0610a997e2 Merge remote-tracking branch 'origin/main' into log-async
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/JsonPlayerDataStoreFactory.kt
2024-09-07 19:37:38 +10:00
Hiroku 10faab68f9 Merge branch 'DoublesAreTheFuture' into 'main'
Added Doubles/Triples/Multibattles

See merge request cable-mc/cobblemon!1091
2024-09-07 09:36:59 +00:00
Paul 42c91c667b Licenses AGAIN RAAAAA 2024-09-07 19:25:09 +10:00
Paul bde9ddd017 TODO clearing 2024-09-07 19:14:49 +10:00
Paul c19a92406e Resolving merge issues 2024-09-07 19:05:50 +10:00
Hiroku c2ac903180 Merge branch 'mod-compat' into 'main'
Re add JEI compat

See merge request cable-mc/cobblemon!1129
2024-09-07 06:47:59 +00:00
apion c475e51a1c Re add JEI compat 2024-09-07 02:16:02 -04:00
Paul b9d3966cfe Merge remote-tracking branch 'origin/main' into DoublesAreTheFuture
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/gui/GuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/SwitchInstruction.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/battle/ClientBattle.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PokemonGuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/BattleOverlay.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/RenderHelper.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
#	common/src/main/resources/assets/cobblemon/sounds/item/pokedex/pokedex_show.ogg
2024-09-07 14:26:07 +10:00
Paul 682325af59 Merge remote-tracking branch 'origin/main' into evolution-effect 2024-09-07 14:03:45 +10:00
Paul d145969631 Removed unneeded glowTime code 2024-09-07 14:03:23 +10:00
Hiroku 432ccf17c0 Merge branch 'evolution-effect' into 'main'
Evolution effect

See merge request cable-mc/cobblemon!970
2024-09-07 04:03:14 +00:00
Paul c6452d1905 I will teach the license file the true meaning of pain (bread) 2024-09-07 13:54:16 +10:00
Paul f72f422ef0 Conflict fixes and fixes to recent animation function changes 2024-09-07 13:43:56 +10:00
Paul 39fc384b67 Merge remote-tracking branch 'origin/main' into evolution-effect
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/evolution/Evolution.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/snowstorm/ParticleEvent.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/animation/PoseTransitionAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/animation/PrimaryAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/animation/StatefulAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/bedrock/animation/BedrockActiveAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
2024-09-07 13:09:21 +10:00
Paul 477318b124 Imports 2024-09-07 12:53:10 +10:00
Paul 367d79a976 Merge remote-tracking branch 'origin/main' into poker-decks 2024-09-07 12:52:01 +10:00
Hiroku eb36320396 Merge branch 'sega_bass_pro_fishing_2' into 'main'
Make it possible to add new animation molang functions.

See merge request cable-mc/cobblemon!1115
2024-09-07 02:45:21 +00:00
Hiroku 98e4f6facc Merge branch 'stone-blocks' into 'main'
Blocks! More Blocks! Stones, even!

Closes #930

See merge request cable-mc/cobblemon!1120
2024-09-07 02:16:31 +00:00
Hiroku 5ea8bbd08e Merge branch 'TheSketchySmeargle' into 'main'
Sketch and Forget

See merge request cable-mc/cobblemon!1093
2024-09-07 01:55:18 +00:00
Paul e7efb94ee9 Merge remote-tracking branch 'origin/main' into poker-decks 2024-09-07 10:51:04 +10:00
Hiroku c3230f0722 Update CHANGELOG.md 2024-09-07 00:49:52 +00:00
Paul 0fd6328ae4 Fixed compile error from mappings switch 2024-09-07 10:43:43 +10:00
Hiroku 43a05e5037 Merge branch 'feature/neoforge-bump-fixes' into 'main'
bump neoforge to stable version and fix breaking changes (and add missing license headers)

Closes #961 and #960

See merge request cable-mc/cobblemon!1128
2024-09-07 00:38:35 +00:00
Polymeta c2b5eca4c7 bump neoforge to stable version and fix breaking changes (and add missing license headers) 2024-09-07 00:38:35 +00:00
Hiroku e9c88ae899 Merge branch 'feature/poker-chips' into 'poker-decks'
variation tooltip to display when buttons are hovered

See merge request cable-mc/cobblemon!1125
2024-09-07 00:38:01 +00:00
Polymeta fece798b92 variation tooltip to display when buttons are hovered 2024-09-07 00:38:01 +00:00
Licious a0c3e40cc4 Merge branch 'fix_moves_description' into 'main'
Fix moves descriptions cutting off

See merge request cable-mc/cobblemon!1126
2024-09-06 13:10:23 +00:00
Erusel 1da011026e Fix moves descriptions cutting off 2024-09-06 13:10:23 +00:00
JazzMcNade 3981ccc332 Player Interactions: Server now informs the client on why an option is unavailable. 2024-09-06 01:31:48 -05:00
Lily e5c2daac2e added cosmetic variants for several pokemon in the pokedex 2024-09-04 21:28:57 -04:00
Lily cccd3aa310 fixed searching 2024-09-04 17:56:44 -04:00
Lily b8dc811a4f fixed missing drop button 2024-09-04 12:51:48 -04:00
Lily 2e8529cc62 oops 2024-09-04 12:34:43 -04:00
Lily 5f8a49e008 re-ordered paldean dex; fixed johto dex missing from the national category 2024-09-04 12:28:59 -04:00
JazzMcNade a945c839f0 Greyed out trade request option if it's unavailable 2024-09-03 23:51:22 -05:00
Test 607413db61 Merge branch 'fix_pc_arrow_hover' into 'main'
Fix NavigationButton hover

See merge request cable-mc/cobblemon!1124
2024-09-04 04:07:32 +00:00
Erusel d478eecf47 Fix NavigationButton hover 2024-09-04 04:07:31 +00:00
SPGesus dd624d06f3 Update galar.json 2024-09-04 01:26:13 +00:00
SPGesus bc596a48ed Update alola.json 2024-09-03 22:59:34 +00:00
dhi_holo c565a79697 Change Instrument
Evolution Blocks now use the Bit instrument (from Emerald Blocks) instead of Basedrum (from stone)
2024-09-02 16:30:13 +10:00
dhi_holo 81f82faa2d Updated to 1.21
Updated to Main/1.21
2024-09-02 16:20:28 +10:00
dhi_holo 978e886b6e Merge branch 'main' into stone-blocks 2024-09-02 15:35:58 +10:00
Paul 6d7784cb3a Added dex additions and dex entry additions 2024-09-02 15:11:03 +10:00
JazzMcNade 63de39b970 Merged Main into DoublesAreTheFuture 2024-09-01 19:18:50 -05:00
JazzMcNade 05a8154d2e Merge Conflicts 2024-09-01 18:32:30 -05:00
JazzMcNade 134b071a1d Merge branch 'main' into TheSketchySmeargle 2024-09-01 18:32:08 -05:00
Paul 5f4366735f Merge remote-tracking branch 'origin/poker-decks' into poker-decks 2024-09-01 22:00:00 +10:00
apion 7fd0f2710e Fixed pokedex grant all command 2024-09-01 07:09:55 -04:00
Paul 7a4760e1bc Merge remote-tracking branch 'origin/main' into poker-decks
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/gui/GuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PokemonGuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/AerodactylModel.kt
#	common/src/main/resources/assets/cobblemon/sounds.json
2024-09-01 20:24:24 +10:00
apion 6574976f2d Pokedex scrolls to correct mon when opened via scanner 2024-09-01 06:18:34 -04:00
apion 6126653a2c Fix neoforge scanner rendering 2024-09-01 05:38:32 -04:00
Paul f45bc68ee6 Changelog! Again! Check again tomorrow for more (also mojmaps) 2024-09-01 19:33:07 +10:00
Paul 1c71e68323 Merge remote-tracking branch 'origin/main' into log-async 2024-09-01 19:29:56 +10:00
Paul 2d9523b9a1 Deleted unused posers 2024-09-01 19:27:00 +10:00
Paul 5ed9fba170 Changelog squashing for 1.6 2024-09-01 19:25:40 +10:00
Hiroku 22583bd207 Merge branch 'sega_bass_pro_fishing_2' into 'main'
1.6 Merge

Closes #916

See merge request cable-mc/cobblemon!1107
2024-09-01 07:53:43 +00:00
Paul 32d68ce554 Conflicto begonus 2024-09-01 17:10:26 +10:00
Paul 72a897063c Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/PlayerMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/item/PokemonSelectingItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/evolution/Evolution.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/SwitchInstruction.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/HealingMachineBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/PCBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/battle/BattleMusicHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/battle/BattleSwitchPokemonHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/sound/battle/BattleMusicInstance.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/battle/BagItemConvertible.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/ReviveItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/controller/ServerEvolutionController.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/world/gamerules/CobblemonGameRules.kt
#	common/src/main/resources/data/cobblemon/loot_table/ruins/common/luna_henge_ruins.json
#	common/src/main/resources/data/cobblemon/loot_table/ruins/common/sol_henge_ruins.json
#	common/src/main/resources/data/cobblemon/loot_table/ruins/common/stonjourner_henge_ruins.json
#	common/src/main/resources/data/cobblemon/loot_table/ruins/uncommon/luna_henge_ruins.json
#	common/src/main/resources/data/cobblemon/loot_table/ruins/uncommon/sol_henge_ruins.json
#	common/src/main/resources/data/cobblemon/loot_table/ruins/uncommon/stonjourner_henge_ruins.json
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_center1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_center1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_center1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side2a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side2b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side2c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side3a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side3b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side3c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side4a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side4b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/lunatone_side4c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_center1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_center1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_center1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side2a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side2b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side2c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side3a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side3b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side3c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side4a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side4b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/solrock_side4c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_center1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_center1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_center1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner10a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner10b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner10c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner11a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner11b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner11c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner12a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner12b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner12c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner2a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner2b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner2c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner3a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner3b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner3c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner4a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner4b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner4c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner5a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner5b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner5c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner6a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner6b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner6c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner7a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner7b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner7c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner8a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner8b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner8c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner9a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner9b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_corner9c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side1a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side1b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side1c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side2a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side2b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side2c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side3a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side3b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side3c.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side4a.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side4b.nbt
#	common/src/main/resources/data/cobblemon/structure/ruins/stonjourner_side4c.nbt
2024-09-01 16:32:14 +10:00
Paul a471a0183f Fixed form unlocking not working properly and gender ratios misbehaving in dex 2024-09-01 16:25:37 +10:00
Hiroku 11cfb7184d Merge branch 'sound' into 'main'
Sound updates

See merge request cable-mc/cobblemon!1121
2024-09-01 06:15:36 +00:00
Hiroku e2645e02d1 Merge branch 'poker-table' into 'poker-decks'
Table for poker decks

See merge request cable-mc/cobblemon!1122
2024-09-01 04:50:21 +00:00
Test 33ff9e90b6 Table for poker decks 2024-09-01 04:50:21 +00:00
Test e75e2801ce fix slot spacing 2024-08-31 23:53:53 -04:00
Tyzillion 7f77abd9a2 License update and changelog update. 2024-08-31 23:13:46 -04:00
Paul 592e6ce5a8 Properly setup dex data with ordering 2024-09-01 11:47:52 +10:00
JazzMcNade f653d325bb Battle wheel option is now greyed out if the target is unable to battle. 2024-08-31 19:11:40 -05:00
Paul 859ca62dc8 Fixed flashing dex screen and seen pokemon not showing properly 2024-08-31 12:25:05 +10:00
Paul 148f4be444 Dex sort order 2024-08-31 11:07:45 +10:00
Paul 4bf6ec80b4 re-enabled dex scanning but it blows up the game 2024-08-30 22:12:50 +10:00
Paul d9a0998213 Added variation controls to dex, fixed various bugs with dex 2024-08-30 22:00:14 +10:00
JazzMcNade eeec0e853b Fixed request button text. Updated team leave icon. 2024-08-30 00:34:42 -05:00
Tyzillion 925b18d809 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-08-28 20:37:39 -04:00
Paul 9c817f77dc Dex initialization fixes, display correct first-known-forms in national dex, fixed species features not saving 2024-08-26 21:36:12 +10:00
Paul b044fe01bb Form dex squashing, Alola dex with separated Rattata as example, various fixes for UI knowledge 2024-08-26 17:03:09 +10:00
Paul d46c88dcf3 Significant restructuring of pokedex entry data 2024-08-26 12:26:20 +10:00
JazzMcNade 03231beab7 Sketch's new move is now immediately applied to effectedpokemon, rather than during postbattle. 2024-08-25 19:44:43 -05:00
amo b4919d18d1 🌠 Fix: Fix friendship only ever being updated clientside & chance inverted check 2024-08-25 14:05:26 +01:00
amo d9e8283743 🌠 Chore: Remove strict inequality from bobber rendering check 2024-08-25 12:59:15 +01:00
apion 3d84063f9a Fix licenses, delete unnecessary files 2024-08-25 02:17:18 -04:00
apion 73237073c2 Lang fixes 2024-08-25 02:09:47 -04:00
apion 05b04cfa67 Added swapping between dexes 2024-08-25 02:09:00 -04:00
apion 4187c04374 Added Johto pokemon, and allowed for aggregation of separately defined dexes 2024-08-25 01:48:05 -04:00
apion d5fca78ba0 Merge remote-tracking branch 'origin/poker-decks-key-value' into poker-decks-key-value
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
2024-08-24 23:25:28 -04:00
apion 8411cd44f9 Added a codec backed GSON adapter, added a codec for ExpressionLikes, setup infrastructure for different types of dexes 2024-08-24 23:24:54 -04:00
Paul e1dc9b1b1b Merge relics 2024-08-25 11:40:41 +10:00
Paul 595a079625 Merge remote-tracking branch 'origin/poker-decks' into poker-decks-key-value
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexJSONRegistry.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexRecord.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/SpeciesPokedexEntry.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokedex/DexData.kt
#	common/src/main/resources/data/cobblemon/pokedexes/national.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_alola.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_galar.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_hisui.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_hoenn.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_johto.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_kalos.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_kanto.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_paldea.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_sinnoh.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_unknown.json
#	common/src/main/resources/data/cobblemon/pokedexes/national_unova.json
2024-08-25 11:11:14 +10:00
apion cf5bd4dca6 Fix conditions (again) 2024-08-24 16:59:42 -04:00
apion a0d9322e55 Fix conditions 2024-08-24 16:52:23 -04:00
apion 304f6c83ee Fix compile errors and encode/decode issues 2024-08-24 16:20:22 -04:00
Paul 8d4a7de267 Calculated pokedex data, some clean up of unused stuff, need to fix up dex client 2024-08-24 22:36:57 +10:00
apion 3b302d6777 Add referential integrity 2024-08-23 23:14:40 -04:00
Paul 59a92391cd Merge remote-tracking branch 'origin/poker-decks-key-value' into poker-decks-key-value 2024-08-23 22:27:55 +10:00
Paul d18a3e43e4 Reworking dex data to be declarative 2024-08-23 22:27:36 +10:00
apion c9baf9332b Add forms 2024-08-22 18:27:44 -04:00
apion ccf3d8eefe Add the 151 OG pokemon as dex entries 2024-08-22 17:24:24 -04:00
Sterrezo 9ac8ca3a7b Final tweaks to the new evolution sound 2024-08-22 14:08:58 +02:00
apion 0698f8265b Fixed numbering 2024-08-21 22:39:41 -04:00
apion a10622151c Renamed stuff to be pokedex specific 2024-08-21 21:15:21 -04:00
apion f69ed78582 Cleaned up event subscriptions a bit, hooked into player party store instead of trying to track pokedex events everywhere. 2024-08-21 19:43:12 -04:00
apion c52ad67435 Fix pokemon rendering when not supposed to 2024-08-21 19:06:01 -04:00
dhi_holo a79428cfb6 Update CobblemonBlocks.kt
Made Ice Block use the Ice colour on maps (why are the map colour names so annoying)
2024-08-21 17:17:17 +10:00
dhi_holo a0672f6354 Data and Textures
all the data files for the smooth tumblestone block suites, and the textures updated since past commit
2024-08-21 16:40:49 +10:00
dhi_holo 72000a1340 More Tumblestone Blocks WIP
Needing to take a break, so untested WIP push just so it's saved. Adds all 9 smooth blocks, and updates older textures. Should be fully functional on the asset side as of this commit, data side will be done later.
2024-08-21 14:57:16 +10:00
Sterrezo 9d25da31dc Fixed block sounds playing at an incorrect pitch
Increased the pitch in CobblemonSounds.kt, workaround for #929
Tweaked volumes again.
2024-08-20 13:10:58 +02:00
Sterrezo 96778d2de0 Implemented sounds for evolution stone blocks 2024-08-20 11:58:19 +02:00
dhi_holo c646f7555e Blocks In
All 10 blocks implemented in near full.
Block data, models, loot tables, recipes, advancements, item groups, lang. Only missing the textures for 6 of the blocks, the sounds (needs changing in Blocks as well as implementation), and confirmation for one texture.

Ice Stone texture also updated
2024-08-20 12:30:07 +10:00
Test c5600ff881 rolling scan lines 2024-08-18 23:17:08 -04:00
apion a39b3092be Actually added a pokemon with an alternate form (rattata) 2024-08-18 17:21:04 -04:00
apion 8c5a24c431 More progress 2024-08-18 16:38:53 -04:00
Sterrezo fb84c48d4d Merge branch 'main' into sound 2024-08-18 21:20:54 +02:00
Sterrezo efc59ab2be More updates to the UI evolution sound 2024-08-18 21:19:09 +02:00
apion 44560195fc Things are kinda working 2024-08-18 14:17:15 -04:00
apion 9b2f0c9375 More work trying to convert to new data format 2024-08-18 13:34:38 -04:00
Hiroku 5cbcef5a88 Merge branch 'remove-unused-event' into 'main'
Remove unused held item update event

See merge request cable-mc/cobblemon!1118
2024-08-18 02:46:56 +00:00
Hiroku 7aa76587df Merge branch 'whatsy/poker-decks' into 'poker-decks'
poker decks

See merge request cable-mc/cobblemon!1089
2024-08-18 02:46:37 +00:00
JazzMcNade ebbbec9d6c Fixed merge conflicts 2024-08-17 21:30:46 -05:00
JazzMcNade 8bb2d1321d Merge branch 'main' into TheSketchySmeargle 2024-08-17 21:30:20 -05:00
Tyzillion e5ffb6db96 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-08-17 22:23:57 -04:00
apion 7700a606d0 IDK if this stuff is staying 2024-08-17 20:52:49 -04:00
Paul 57dbb9fc02 Conflicto confundo 2024-08-18 10:01:47 +10:00
Paul 938cd995d7 Merge remote-tracking branch 'origin/poker-decks' into whatsy/poker-decks
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/pokedex/MapUpdatePacketHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokedex/scanner/PokedexUsageContext.kt
2024-08-18 09:58:41 +10:00
Paul 112649081f Properly handle the ordering of dexes 2024-08-18 09:54:36 +10:00
apion d27ee304cb This ones for you Village 2024-08-17 13:53:44 -04:00
apion 1b5ad6e860 Fix server crashes 2024-08-17 10:58:44 -04:00
apion d283eb061a Fix issue where only assets for the red pokedex are used 2024-08-17 09:46:08 -04:00
Paul 92111b1046 Ported some dex stuff to mojmaps, scanning owned pokemon adds to Caught status in dex 2024-08-17 21:35:23 +10:00
Paul fbc976bfa0 Merge branch 'poker-decks' into whatsy/poker-decks
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexJSONRegistry.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/keybind/keybinds/PokedexBinding.kt
2024-08-17 19:37:52 +10:00
Landon Wills 5c6ecb1807 Remove unused held item update event 2024-08-16 23:13:39 -04:00
Paul cb266499a3 Use a snapshot of the storm matrix for event particle type EMITTER 2024-08-16 17:28:29 +10:00
Paul 094f860b80 Fixed evo burst particle being emitter bound 2024-08-16 15:53:04 +10:00
Paul 79c1259cae Restored original particle event source matrix for EMITTER and EMITTER_BOUND types 2024-08-16 15:43:53 +10:00
Sterrezo 01f922b653 Clarified a change in the changelog and re-added a sound for evolution in sounds.json 2024-08-16 01:11:52 +02:00
Sterrezo 71d1730697 Merge branch 'main' into sound 2024-08-16 00:58:52 +02:00
Apion 84dfd56c12 Merge branch 'Slippy-is-a-Coder-Now' into 'main'
Slippy is a coder now

See merge request cable-mc/cobblemon!1108
2024-08-15 21:25:57 +00:00
Nick 770abc2b9b Merge branch 'feature/fix-license' into 'main'
fix missing license headers

See merge request cable-mc/cobblemon!1116
2024-08-15 17:35:28 +00:00
Polymeta 353069803f fix missing license headers 2024-08-15 18:17:55 +01:00
Waterpicker 03227d07f2 Make it possible to add new animation molang functions. 2024-08-15 02:06:51 -05:00
Paul b8d913bd2b Fixed weird waterlogging issues in pasture and PC 2024-08-12 15:34:57 +10:00
Paul 9339d91cbb Missing licenses 2024-08-12 13:47:15 +10:00
Paul 8b255c784a Fixed monitor block rotations from structures maybe 2024-08-11 18:17:27 +10:00
apion d515091b30 Add all unimplemented mons to dex 2024-08-11 01:30:14 -04:00
apion 1f3e408396 Made pokemon types into an enum, changed how server side scan validation works 2024-08-10 22:38:03 -04:00
apion f800070f91 Made scanning actually add pokedex records 2024-08-10 20:27:21 -04:00
apion 5f4c2d1d7e God have mercy on my soul 2024-08-10 19:15:33 -04:00
Tyzillion 4efd8a7fef Tweaked evolution particles. 2024-08-10 15:52:27 -04:00
Tyzillion e7911644d8 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/evolution/Evolution.kt
2024-08-10 14:57:33 -04:00
apion 590c3cfcd2 Added zooming back 2024-08-10 10:05:40 -04:00
apion 2b1b393c92 Added back the display of the species name 2024-08-10 07:27:56 -04:00
apion 0b3d78e509 More cleanup 2024-08-10 06:30:47 -04:00
Hiroku 4cdea8d1fc Merge branch 'sprites' into 'sega_bass_pro_fishing_2'
Gui Sprites

See merge request cable-mc/cobblemon!1102
2024-08-10 00:43:54 +00:00
Waterpicker 1b6139b386 Gui Sprites 2024-08-10 00:43:54 +00:00
Hiroku de3342226b Merge branch 'battle_bug_fixes' into 'main'
Battle bugs pentakill

Closes #865, #912, #880, #466, and #819

See merge request cable-mc/cobblemon!1105
2024-08-10 00:37:40 +00:00
Hiroku ca890133ba Merge branch 'Myslippy-main-patch-47052' into 'main'
Update CHANGELOG.md

See merge request cable-mc/cobblemon!1111
2024-08-10 00:36:24 +00:00
Hiroku c452aa77a9 Merge branch 'fix-missing-stellar-message' into 'main'
Fix terastallize message missing when using stellar

See merge request cable-mc/cobblemon!1112
2024-08-10 00:36:12 +00:00
Landon Wills fba2217e6f Fix terastallize message missing when using stellar 2024-08-09 02:00:10 -04:00
Apion 2bd1382b85 Merge branch 'feature/simplify-modelloader-mixin' into 'sega_bass_pro_fishing_2'
Stop throwing errors in the model loader that generate misleading crash-reports

See merge request cable-mc/cobblemon!1110
2024-08-08 16:11:08 +00:00
Polymeta 7791e72f72 Stop throwing errors in the model loader that generate misleading crash-reports 2024-08-08 16:11:07 +00:00
Myslippy 8026a7c0b7 Update CHANGELOG.md 2024-08-08 03:16:19 +00:00
Segfault Guy 640c7b1086 Switch clarity and cleanup to address log flooding. 2024-08-05 23:51:07 -04:00
Eight 8a58142b62 Implemented ObservableSubList.kt, tests, & updated iterators:
1. ObservableSubList:
    - Switched from returning kotlin sublist to an observable implementation that can still be subscribed to.
    - Split MutableObservableSubList functions to match the other observable collections.
    - Implemented iterators.

2. Tests:
    - Implemented tests for the current functionality to help with future iterations & reliability.
    - AllCollectionTests.kt
    - ConstructionTests.kt
    - EmittingReferenceTypeTests.kt
    - EmittingTests.kt
    - IteratorTests.kt

3. Updated iterators:
    - Removed redundant construction of kotlin iterators with observable collection iterators where possible.
    - Fixed a possible out of range exception when attempting to get an element from an empty sublist iterator & an observable list with no element below the fromIndex.
2024-08-05 17:37:48 -07:00
Eight a108119420 Merge branch 'master' into 'main'
Observable collections & utilities:

See merge request SomeGuyEight/cobblemon!1
2024-08-05 06:19:52 +00:00
Eight 6572478ee1 Adding an implementation of Observable Collections & utilities:
These are collection implementations that extend the observable interface & api. The collections can manage all observables within the stored elements & entries. Each will emit with any mutation to the elements contained or a mutation of the collection like addition, removal, set, or swap. Each collection implements a subscription object that holds the subscriptions of all possible observables for the associated collection.

    Each collection behaves as the kotlin implementation is expected to aside from the sublist. The sublist behaves as the kotlin equivalent until the backing list is mutated. When the backing list of the kotlin sublist is mutated, accessing the sublist will throw an exception, but the MutableObservableSubList.kt will continue to stay synced. It will continue to emit & return values from the backing list for the index window it is constructed with.

1. Observable Collections implemented:
    A) ObservableCollection.kt & MutableObservableCollection.kt interfaces
        - ObservableSet.kt & MutableObservableSet.kt implementations
        - ObservableList.kt & MutableObservableList.kt implementations
        - MutableObservableSubList.kt implementation
    B) ObservableMap.kt & MutableObservableMap.kt

2. Observable Subscriptions:
    A) CollectionSubscription.kt interface
        - SetSubscription.kt
        - ListSubscription.kt
        - IndexedListSubscription
    B) MapSubscription.kt

3. Iterators:
    A) ObservableSetIterator.kt & MutableObservableListIterator.kt
    B) ObservableListIterator.kt & MutableObservableListIterator.kt
    C) MutableObservableSubListIterator.kt
    D) ObservableMapIterator.kt & MutableObservableMapIterator.kt

4. Nbt serialization utils:
    A) ObservableSetSerialization.kt
    B) ObservableListSerialization.kt
    C) ObservableMapSerialization.kt
2024-08-04 22:56:22 -07:00
apion 301b210d5a Started some big boy cleanup for the dex 2024-08-04 16:31:04 -04:00
apion b942c3a45e Fix dimension serialization 2024-08-04 13:17:04 -04:00
apion 8b053f491b Another pass 2024-08-04 09:05:42 -04:00
apion a65c82c2b4 Third pass at resolving mapping problems 2024-08-04 08:27:13 -04:00
apion 813258064c Second pass at resolving mapping problems 2024-08-04 08:16:58 -04:00
apion c5124cf19d First pass at resolving mapping problems 2024-08-04 03:46:24 -04:00
apion 6ffa7b1be3 Merge branch 'refs/heads/sega_bass_pro_fishing_2' into poker-decks
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/PartyScrollMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/gui/GuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/GeneralPlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerDataStoreManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/JsonPlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/MongoPlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/JsonPlayerDataStoreFactory.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/PlayerDataStoreFactory.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonBakingOverrides.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PokemonGuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/BattleOverlay.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/keybind/CobblemonKeyBinds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/SetClientPlayerDataHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/RenderHelper.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PosableModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/bedrock/animation/BedrockAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/ChangeScaleAndSize.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/ChangeWalkSpeed.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/OpenStarterScreenCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/battle/BattleInitializePacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/starter/SetClientPlayerDataPacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/serverhandling/starter/RequestStarterScreenHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/starter/CobbledStarterHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MiscUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
#	common/src/main/resources/cobblemon-common.accesswidener
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/client/CobblemonFabricClient.kt
2024-08-04 02:53:12 -04:00
Hiroku cd68665cf4 Merge branch 'main' into 'main'
Enhance Test Coverage for IVs.createRandomIVs

See merge request cable-mc/cobblemon!1101
2024-08-03 01:34:39 +00:00
Hiroku de9d9ec645 Merge branch 'battle_theme_tweaks' into 'main'
Battle theme Sound Physics Remastered compat + fade out override

See merge request cable-mc/cobblemon!1106
2024-08-03 01:33:58 +00:00
Hiroku 598748220f Less code = better 2024-08-03 01:05:05 +00:00
JazzMcNade 0fd4f68f69 Corrected Lang 2024-08-02 19:30:25 -05:00
Paul 313250347c More safely check thread for spawners and influencers access 2024-08-03 10:09:56 +10:00
Sterrezo cbdb63b102 I never forget to ctrl + s the changelog 2024-08-02 15:10:05 +02:00
Sterrezo d211fb2310 Updated sounds for clicks, evolution and evolution notification. Renamed and relocated evolution sound assets.
Evolution sounds were scattered in the folder with Pokemon. I moved them all to their own folder and renamed them to be more descriptive as to where they play both in files and in code.
2024-08-02 15:07:17 +02:00
Sterrezo 2f730be207 Merge branch 'main' into sound 2024-08-02 13:53:20 +02:00
Slippy 761eb07300 Merge branch 'Slippy-is-a-Coder-Now' of https://gitlab.com/cable-mc/cobblemon into Slippy-is-a-Coder-Now 2024-08-02 00:11:15 -05:00
Slippy fec9ace152 PC loot table 2024-08-02 00:10:55 -05:00
tmetcalfe89 63e5274fc9 healer pools and entries 2024-08-01 21:32:10 -07:00
Slippy ae28d186a0 When PC and Healer blow up and is set to natural = false, they won't drop anything. 2024-08-01 22:16:46 -05:00
Slippy e901d3d1e7 PC and Healing Machine now drop iron ingots when natural = true 2024-08-01 22:11:00 -05:00
Plastered_Crab 1e0c2005da - fixed closing animation 2024-08-01 14:00:56 -07:00
Slippy 33aa6d4591 I coded something. hope it doesnt break :) 2024-08-01 11:30:42 -05:00
Segfault Guy 14c3e4e9a0 Merge remote-tracking branch 'origin/main' into battle_theme_tweaks 2024-07-30 23:25:32 -04:00
Segfault Guy b89cd88639 Merge remote-tracking branch 'origin/main' into battle_bug_fixes 2024-07-30 23:00:49 -04:00
Segfault Guy 87a106bd1e Changelogs 2024-07-30 23:00:11 -04:00
Segfault Guy 1aacad2733 Battle start + invulnerability cleanup. 2024-07-30 21:50:39 -04:00
Plastered_Crab 89dca86a21 - added test compass to top of scanner 2024-07-30 16:19:12 -07:00
Plastered_Crab cdcd7bdec6 - More adjustments to the smaller hitboxes during scanning so scanning tiny pokemon (like joltik) is easier 2024-07-30 13:22:07 -07:00
Plastered_Crab 35c3e8f245 - scanner range adjustment when zooming and fixed my goofs with the grace period 2024-07-30 09:56:13 -07:00
plastered_crab 678b0cc7fd - making smaller hitboxes bigger when scanning (needs fine tuning) 2024-07-30 00:32:49 -07:00
plastered_crab f2dd1a5c62 - overlay is kill (for real this time) 2024-07-29 22:23:05 -07:00
Segfault Guy 7c26008ad4 Turn 0, battle init fix, switch tile changes, eject pack implementation. 2024-07-29 23:52:56 -04:00
plastered_crab b54d284fdb - kill overlay upon scan finishing 2024-07-29 20:35:19 -07:00
plastered_crab 79d8425749 - added grace period for scanning 2024-07-29 20:13:40 -07:00
Test 36c4e4e553 overlay screen tests 2024-07-29 22:56:21 -04:00
Paul c28c17b514 Smoothed dex zoom in and zoom out transitioning 2024-07-28 11:06:50 +10:00
Test 75faa7b798 overlay screen tests 2024-07-27 20:42:35 -04:00
VasconcelosJoao 0b7c183eeb New test cases 2024-07-24 18:40:39 -03:00
VasconcelosJoao e790f03066 New test cases 2024-07-24 18:38:52 -03:00
Segfault Guy faa9e73d22 Merge remote-tracking branch 'origin/main' into battle_bug_fixes 2024-07-23 00:24:37 -04:00
Segfault Guy b2fe1c2fb4 Switch and withdraw logs. 2024-07-23 00:24:07 -04:00
Segfault Guy 0011cab21e Fix nullptr exceptions when using revive or healing bench pokemon in battle. 2024-07-21 12:22:38 -04:00
Paul d282631662 Added logging for unsafe access to various APIs and moved JOIN and DISCONNECT platform events to always be sync 2024-07-21 13:16:13 +10:00
Segfault Guy 260767bed4 Add gamerule for invulnerability during battle. 2024-07-20 19:58:56 -04:00
Slippy 2895c0a2ef I hate the crumbling arch 2024-07-18 08:57:11 -05:00
Slippy acc87f9eae Solhenge, Lunahenge, and Stonjournerhenge structures completed. 2024-07-17 12:31:55 -05:00
plastered_crab 26d431cd5e - wip function for rending scan progress 2024-07-13 16:34:44 -07:00
JazzMcNade 2f32886576 Changed BenchMovePacket to not map the empty string to null. Updated Changelog 2024-07-13 01:23:12 -05:00
JazzMcNade 074b32ef9c Merge branch 'main' into TheSketchySmeargle 2024-07-12 20:53:27 -05:00
plastered_crab db048b68bb - tweaked scanning distance to feel a bit better based on zoom level 2024-07-12 00:41:45 -07:00
plastered_crab 76c3cba300 - tweaks to Dex Scanner mode so Scanner overlay doesn't get stuck on randomly 2024-07-11 23:31:09 -07:00
Segfault Guy 653e048d2e Fix new battle music instance not playing when old instance is still fading out 2024-07-11 22:22:26 -04:00
Segfault Guy 36f87425c9 Sound Physics Remastered compat 2024-07-11 21:37:44 -04:00
plastered_crab 57603e60e6 - distance of scanning and detection of pokemon is determined by Zoom Level now 2024-07-11 01:05:40 -07:00
plastered_crab 487361208e - pokemon are detected if scanner is aimed at them when initiating scan overlay 2024-07-11 00:41:14 -07:00
plastered_crab 20b2711703 - attempt to fix HUD UI of Scanner getting stuck 2024-07-11 00:11:23 -07:00
Lily 43d4eacefc Merge branch 'poker-decks' into whatsy/poker-decks 2024-07-09 10:49:15 -04:00
Lily caa9ff5eba text now shows up when there's no drops available for a mon 2024-07-09 10:47:20 -04:00
Sterrezo 3f5286c0a9 Increased snap sound's asset volume 2024-07-09 00:10:29 +02:00
Sterrezo fedf2eb950 Decreased the scan's volume 2024-07-08 23:44:40 +02:00
Sterrezo 56b2951d88 Sound updates and fixes 2024-07-08 23:29:37 +02:00
Sterrezo 35d96d2eb7 A lot of sound updates to herbs and blocks 2024-07-08 13:35:36 +02:00
Sterrezo b6e6d9a151 Merge branch 'main' into sound 2024-07-08 11:35:44 +02:00
plastered_crab 2013f890a5 - no more transparent issues 2024-07-06 16:24:18 -07:00
plastered_crab 42efbe5a07 - new sounds (again) 2024-07-05 17:10:04 -07:00
plastered_crab cf17f2f4ac - new sounds 2024-07-05 17:04:48 -07:00
Lily 301d1c377a fix drop percentage display showing decimals 2024-07-05 12:05:37 -04:00
plastered_crab df3d2f99d4 - test feature trimming of borders 2024-07-04 17:45:07 -07:00
Lily 2c6229417f drop table fixes 2024-07-04 17:25:39 -04:00
Lily 94d18593a0 region selection and drops info tab 2024-07-04 16:48:47 -04:00
Lily b4b6606f39 work on region selection and viewing pokemon drops 2024-07-04 10:48:22 -04:00
plastered_crab a70c08f484 - hopefully this won't end badly 2024-07-04 00:11:01 -07:00
plastered_crab b19a5f637a - I broke everything.... put it back 2024-07-03 23:51:27 -07:00
plastered_crab 331551a614 - tweak to test feature 2024-07-03 23:33:56 -07:00
Plastered_Crab 0897d3afb5 - the maps Todd Howards better now 2024-07-03 12:51:28 -07:00
Plastered_Crab a2be8c9fdd - maps interacting with decks starting to Todd Howard 2024-07-03 09:26:57 -07:00
plastered_crab 9f71d45eeb - new classes for a test feature 2024-07-03 08:03:35 -07:00
plastered_crab 910d42af16 - even MORE prep for the handoff for the Village people 2024-07-02 21:09:19 -07:00
plastered_crab 8af83a2cb9 - extra prep for big flashy features 2024-07-02 20:57:53 -07:00
Plastered_Crab 04c3a1cd0d - more important stuff 2024-07-02 20:39:44 -07:00
Plastered_Crab 6f5efa3d62 - more stuff for the new features 2024-07-02 16:02:10 -07:00
Plastered_Crab 32c7f178b5 Merge remote-tracking branch 'origin/poker-decks' into poker-decks 2024-07-02 13:08:52 -07:00
Plastered_Crab 4129f29da0 - lots of changes for a top-secret mysterious feature 2024-07-02 13:08:31 -07:00
Lily 766a7e5595 filled in a ton of missing langs 2024-06-26 19:43:46 -04:00
Paul 9f75d842d8 Lil generic cleanup 2024-06-26 19:47:58 +10:00
Paul 636e938789 Broader applyEffects functionality, implemented cry function in dex 2024-06-26 19:28:25 +10:00
Paul 26a4e9a7d8 Fixed misplaced \r in PokedexEntryProgress tripping up the license checker 2024-06-24 18:59:11 +10:00
Paul 6fd1e31c87 Fixed size display for dex 2024-06-24 18:41:00 +10:00
Paul 85a855be97 Licenses 2024-06-24 14:39:05 +10:00
Paul 4d108e424e Dex ported to 1.21 2024-06-24 14:37:34 +10:00
Paul b1333e490f Merge branch 'refs/heads/sega_bass_pro_fishing_2' into poker-decks
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/advancement/criterion/BattleCountableCriterion.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/gui/GuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonBakingOverrides.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PokemonGuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/BattleOverlay.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleSwitchPokemonSelection.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/battle/BattleInitializePacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/starter/SetClientPlayerDataPacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2024-06-24 11:20:24 +10:00
Village 9a57fc5d40 🃏 2024-06-23 20:08:32 -04:00
Paul 78bce64df2 Fixed hisui pokedex data not migrated to new format stuff 2024-06-23 16:53:31 +10:00
JazzMcNade 7a97b7e92a Added the ability to empty a move slot (forget a move) on the move swap screen 2024-06-08 17:28:09 -05:00
JPAKx4 2ac91b0731 Added data for pokedex and classes that process that data. Utility Scripts also contains two scripts used for generating data. Data not final. 2024-06-04 20:36:24 -05:00
JazzMcNade c689f7c48e Merge branch 'main' into TheSketchySmeargle 2024-06-04 00:24:20 -05:00
JazzMcNade 12592f437e Fixed bug with clients not receiving power point updates post battle for Sketch acquired moves on Smeargle 2024-06-04 00:23:01 -05:00
JazzMcNade 27b276fc36 Moves learned via Sketch now persist post battle 2024-05-28 16:17:12 -05:00
Sterrezo 0f3104f9d4 Merge branch 'main' into sound 2024-05-27 01:33:14 +02:00
Sterrezo 21a32bd459 Updated all generic impact assets
Increased the volume of all generic impact sounds. Audio assets have been updated to have a 0.2ms delay to account for ogg file compression cutting the initial start of the sound, making the attack have less impact.
2024-05-26 14:25:34 +02:00
Tyzillion f9a37791b7 Evolution can now be canceled when withdrawn during evolution animation. 2024-05-26 00:17:22 -04:00
Tyzillion fb6e9f3493 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/evolution/Evolution.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
2024-05-26 00:04:59 -04:00
JPAKx4 ee73058f5b Added a count for amount of species with a specific knowledge level and getting the knowledge level of a specific species in ClientPokedex.kt . 2024-05-20 06:35:31 -05:00
JPAKx4 1d20051888 ScrollingWidget has a function for rendering the scrollbar, which is able to be overriden. 2024-05-19 18:15:42 -05:00
JPAKx4 5ec1ef72be Added searching to the Pokedex, and the "Invisible" property for pokemon. 2024-05-14 22:25:12 -05:00
JPAKx4 76560198b8 Updated Pokedex to Cobblemon 1.5 2024-05-11 19:57:43 -05:00
JPAKx4 b546327af9 Merge branch 'main' into its-dexter-time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/keybind/CobblemonKeyBinds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/battle/BattleInitializePacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/starter/SetClientPlayerDataPacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
2024-05-11 19:45:55 -05:00
JPAKx4 eead2724e7 Pokedex is Datapackable probably 2024-05-10 22:22:16 -05:00
JPAKx4 2b68ccd0f9 Forms in the Pokedex are read from the Pokedex Files now. 2024-05-02 14:49:28 -05:00
JPAKx4 5adefaac5e Pokedex is now based on files in the resources, and such is also datapackable. Forms still need to be fixed and the spreadsheet needs expanded as well. 2024-05-01 16:36:02 -05:00
Paul ba6513942d Fixed evo_glow_time call not using query struct 2024-04-24 12:12:00 +10:00
Paul 84c7d36840 Merge remote-tracking branch 'origin/main' into evolution-effect 2024-04-24 12:00:20 +10:00
amo 084e5f744a early flash layer impl 2024-04-23 14:53:36 +01:00
Paul 90a1acb56e Fixed evolution animation being cancelled when the entity is pushed, minor fix to action effects 2024-04-22 15:03:44 +10:00
JPAKx4 afb93ba15c Cleaned up the ScrollingWidget.kt and the children now that the Mixin is implemented. 2024-03-19 06:16:17 -05:00
Nick fef655065b Merge branch 'its-dexter-time' of gitlab.com:cable-mc/pokemon-cobbled into its-dexter-time 2024-03-18 19:18:58 -07:00
Nick 1d2ce79fba Fix scroll widget being slightly off on both x and y axis 2024-03-18 19:18:50 -07:00
JPAKx4 0b13ad2fbc Fixed the ScrollingWidget.kt to properly select entries based on mouse position. 2024-03-18 15:13:47 -05:00
JPAKx4 365dd2c45c Began doing the "deltaDex" config file 2024-03-07 22:48:15 -06:00
JPAKx4 e9d4524a4f Worked on forms and added Weight/Height stuff. 2024-02-27 20:24:12 -06:00
JPAKx4 78c32e5396 Created a generic ScrollingWidget.kt and added a DescriptionWidget.kt for the PokedexGUI.kt 2024-02-27 13:35:09 -06:00
JPAKx4 58cb6295fa Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time 2024-02-26 18:17:35 -06:00
JPAKx4 4bc94f8deb Made the info section render an animated pokemon. 2024-02-26 18:15:27 -06:00
apion 25d3d9f7da Made tint values travel down to emissive layers 2024-02-26 18:04:30 -05:00
JPAKx4 e5235cb121 Added Scroll bar to the PokedexGUI.kt 2024-02-26 16:42:22 -06:00
apion 3534dc0722 Added revoke command 2024-02-26 17:07:02 -05:00
apion ce5b94745e Fixed add only for pokemon with no alternate forms 2024-02-26 16:49:07 -05:00
apion 0868d71c59 Added command to fill pokedex, licenses 2024-02-26 05:42:28 -05:00
JPAKx4 801de096fd Work on Scrollbar and added the starter event to the PokedexHandler.kt 2024-02-25 22:04:25 -06:00
JPAKx4 347c56c484 Added Portraits and Text 2024-02-25 19:59:51 -06:00
JPAKx4 a184c8b960 Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time 2024-02-25 17:46:47 -06:00
apion 950994d95c Added tracked data serialization 2024-02-25 06:29:41 -05:00
apion 19535fc81e Cleanup 2024-02-25 02:26:34 -05:00
apion a80938badf Moved pokedex storage to use nbt 2024-02-25 00:45:43 -05:00
apion a484ba6c68 Cool codec stuff 2024-02-24 22:55:51 -05:00
JPAKx4 8afe6d5b22 Added Slots for pokedex and sorting for the species. 2024-02-24 21:48:09 -06:00
apion 30ba386294 Started trying to do incremental updates 2024-02-24 20:20:22 -05:00
apion f2ae51df7d Small fixes 2024-02-24 17:57:32 -05:00
apion b5d354164c Added back previously tracked events 2024-02-24 16:18:37 -05:00
apion fe69659191 Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time 2024-02-24 03:22:18 -05:00
apion 536fac3904 Restructured the pokedex so that we can configure exactly what data gets tracked. It is super cool 2024-02-24 03:22:08 -05:00
JPAKx4 77a416b699 Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time 2024-02-23 21:10:23 -06:00
apion 1e05d35e06 Added Justify Text Right and new text for the pokedex. 2024-02-23 21:10:07 -06:00
apion a84c72d703 Merge branch 'main' into its-dexter-time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/GeneralPlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerDataStoreManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/JsonPlayerDataStoreFactory.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
2024-02-23 20:39:27 -05:00
apion 0b29bbf82c Merge branch 'main' into its-dexter-time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/GeneralPlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerDataStoreManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/JsonPlayerDataStoreFactory.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
2024-02-23 20:39:07 -05:00
JPAKx4 35085c462e Created layout for Pokedex. Placeholder images added for the background. 2024-02-23 11:56:58 -06:00
apion 809f1aeca3 Got incremental pokedex updates working 2024-02-23 01:06:56 -05:00
apion dfa76da95e Move onBattle logic to PokedexHandler 2024-02-23 00:07:36 -05:00
apion 776883c96d Small cleanup 2024-02-22 23:44:19 -05:00
JPAKx4 c5c58c0c60 Added events in PokedexHandler that update the server and client side pokedexes and added an optimization to track when a PokedexEntry was actually changed to reduce how much is sent on updates 2024-02-22 21:50:29 -06:00
JPAKx4 9861877f45 Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time 2024-02-22 19:41:27 -06:00
apion 338a312485 Darken icon for encountered only mons 2024-02-22 20:14:32 -05:00
JPAKx4 a3b2bed484 Merge remote-tracking branch 'origin/its-dexter-time' into its-dexter-time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/pokedex/Progress.kt
2024-02-22 19:00:34 -06:00
JPAKx4 fa2ac5d984 Refactored packages for pokedex networking 2024-02-22 19:00:02 -06:00
apion 951676e6bf Added icon to pokemon who have been encountered before 2024-02-22 19:56:33 -05:00
apion 15940ff0a6 Added Pokedex store, plus added tracking of wild pokemon encounters 2024-02-22 01:00:42 -05:00
apion bec608be96 Refactored storage system to make adding more types of stores easier. 2024-02-21 22:55:09 -05:00
JPAKx4 3704865ddb Created several classes related to creating the pokedex. DTO has been handled, but may need to change depending on functionality. Added keybind for "k", which all it currently does is display test text and print the pokedex entry class memories into a log. 2023-11-25 22:31:29 -06:00
JPAKx4 8815e8f03e Created several classes related to creating the pokedex. DTO has been handled, but may need to change depending on functionality. Added keybind for "k", which all it currently does is display test text and print the pokedex entry class memories into a log. 2023-11-25 22:00:26 -06:00
JPAKx4 a2414f9ffc Created several classes related to creating the pokedex. DTO has been handled, but may need to change depending on functionality. Added keybind for "k", which all it currently does is display test text and print the pokedex entry class memories into a log. 2023-11-25 22:00:26 -06:00
JPAKx4 db8ee43fd3 Merge remote-tracking branch 'origin/main' into its-dexter-time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
2023-11-19 16:19:21 -06:00
JPAKx4 7e755721a8 Added pokedex UI and net (very early progress) 2023-11-19 16:18:22 -06:00
MoeBoy76 231b3d2eac Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into its-dexter-time 2023-03-03 11:44:23 +11:00
MoeBoy76 ac4d36100a updated some parts per design and added some fail-safe checking 2023-03-03 11:43:37 +11:00
MoeBoy76 2bf1c39839 Initial dex code and testing for datapacks 2023-02-24 08:38:06 +11:00
MoeBoy76 ad2c307b60 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into its-dexter-time
# Conflicts:
#	common/src/main/resources/assets/cobblemon/sounds.json
2023-02-17 11:39:00 +11:00
MoeBoy76 74ba080937 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into its-dexter-time
# Conflicts:
#	common/src/main/resources/assets/cobblemon/sounds.json
2022-12-23 16:19:56 +11:00
1951 changed files with 59837 additions and 2055 deletions

No files matched your search

+2 -1
View File
@@ -39,4 +39,5 @@ common/src/main/resources/assets/cobblemon/showdown
common/.fabric
# MacOS
.DS_Store
.DS_Store
.kotlin/sessions/kotlin-compiler-16609242564043830292.salive
+3
View File
@@ -0,0 +1,3 @@
### Additions
- Added the Poké Dex as a craftable item.
- Poké Dexes can be placed on lecterns.
+71 -17
View File
@@ -60,24 +60,16 @@
- Hidden Power now displays its effective typing.
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone Storage Blocks.
- Added Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
### Added cries to the following Pokémon
- Timburr, Gurdurr, Conkeldurr
- Golett, Golurk
### Changes
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
- Adjusted some berry balance values like yield and growth times
- Changes to mulch buffs/durations
- Dreepy is now shoulder mountable
- Link Cable recipe has been redesigned
- Updated potion sprites
- Weedle, Caterpie, Spearow, Smoochum, Mew, Murkrow, Larvitar, Taillow, Plusle, Minun, Beldum, Starly, Buneary, Combee, Pachirisu, Pidove, Petilil, Hisui Bias Petilil, Zorua, Hisuian Zorua, Elgyem, Fletchling, Skrelp, Klefki, Fomantis, Morelull, Dreepy, Shroodle, Tatsugiri, Glimmet, and Roaming Gimmighoul are now shoulder mountable
- Chikorita's ability to look around removed due to the awkwardness of it.
- Link Cable recipe has been redesigned
- Updated potion sprites
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
- Moves learned via Sketch now persist after battle.
- Added ston_henge_ruins, luna_henge_ruins, and sol_henge_ruins structures.
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle
- Added battle log messages for switching out Pokémon
- Added Evolution particles and sounds for Pokemon that are sent out when evolution is started.
### Pokémon Added
#### Gen 2
@@ -94,10 +86,72 @@
#### Gen 9
- Farigiraf
### Added cries to the following Pokémon
- Timburr, Gurdurr, Conkeldurr
- Golett, Golurk
### Changes
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
- Adjusted some berry balance values like yield and growth times
- Adjusted volumes of sounds made by Display Cases, Berry Bushes, Energy Root, Medicinal Leek, Vivichoke, Mints, Revival Herbs and Gilded Chests.
- Changes to mulch buffs/durations
- Dreepy is now shoulder mountable
- Link Cable recipe has been redesigned
- Updated potion sprites
- Weedle, Caterpie, Spearow, Smoochum, Mew, Murkrow, Larvitar, Taillow, Plusle, Minun, Beldum, Starly, Buneary, Combee, Pachirisu, Pidove, Petilil, Hisui Bias Petilil, Zorua, Hisuian Zorua, Elgyem, Fletchling, Skrelp, Klefki, Fomantis, Morelull, Dreepy, Shroodle, Tatsugiri, Glimmet, and Roaming Gimmighoul are now shoulder mountable
- Chikorita's ability to look around removed due to the awkwardness of it.
- Link Cable recipe has been redesigned
- Updated potion sprites
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs
- Updated UI sounds for clicking and evolving Pokémon
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
- Kakuna, Beedrill, Snorlax, Munchlax, and Lurantis received model updates
### Fixes
- Fixed trading Pokémon setting their friendsip to 0 instead of the base value.
- Fixed a scenario where a Pokémon that rolled to spawn with a special Tera type sometimes has a Tera type that is already a part of their natural typing.
- Fixed PokemonProperty argument suggestions for ``tera`` and ``tera_type`` not suggesting ``stellar``.
- Fixed being able to stack Relic Coin Pouches on top of each other.
- Fixed Model loader generating misleading crash-reports when client is crashing
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
- Big Roots and Energy Roots now share the same sounds as intented.
- Fixed Cubone's cry not having a sound.
- Fixed sounds made by blocks playing at a lower pitch than intended.
- Fixed the Seafloor spawning context not being a usable context.
- Fixed Pokemon spawning in non-full blocks like slabs.
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
- Hidden Power no longer plays the water type action effect. (It now plays the normal type action effect.)
- Fixed Chimchar and Monferno T posing whenever they sleep.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
- Fixed Bellossom clipping into the player head when shoulder mounted.
- Fixed Shroomish's look range to prevent it from looking higher than it should.
- Fixed Crumbling Arch not blending in with the world.
- Fixed Energy Root applying being usable on a fainted Pokemon.
- Fixed Pokémon entity not spawning when starting a battle while recalling said Pokémon
- Fixed species comparison not using namespace for both sides in PokemonProperties.isSubsetOf.
- Fixed PokemonProperties#asString prefixing nicknames with a '$'
- Fixed reviving items throwing an exception when used in battle
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive
- Fixed PC not saving when Pokemon was changed outside.
- Fixed shearble Pokemon not dropping correct wool colors.
- Fixed pasture spawning Pokemon inside solid blocks when closest spawning position is blocked off.
- Fixed Tumbling Down advancement not being granted by tumblestone variants.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server
- Fixed empty JsonPlayerData files resulting in players being unable to join server/world
- Fixed crumbling_arch_ruins from generating a giant cube of air and removed the chest
- Fixed possible crash on large population servers due to concurrent access of data.
### Developer
- SpawnCause is now an implementation of SpawningInfluence.
- Added "forcedAspects" to Pokemon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the Pokemon.aspects setter private. This could technically break sidemods but if you are affected by this then you were using it wrong! Use Pokemon.forcedAspects to fix it.
- Many types related to ``Pokemon`` including itself now have dedicated ``Codec``, please migrate to them from the NBT/JSON/PacketBuffer write/read methods.
- ``TeraTypes`` now implements ``Iterable``.
- PokemonBattle now starts on turn 0.
- All sounds related to evolving Pokémon have been moved to the "sounds/evolution" folder.
### Data Pack & Resource Pack Creators
- Added support for "shedders" similar to Shedinja's evolution logic.
+1 -1
View File
@@ -40,7 +40,7 @@ dependencies {
modApi(libs.molang)
// Integrations
// compileOnlyApi(libs.jei.api)
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/*
I think it would be nice to maybe use the existing mixin we have for custom item rendering [BuiltinModelItemRendererMixin]
Obviously that would require changes. Potentially each item renderer defines what modes it overrides for?
*/
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Shadow @Final private ItemModelShaper itemModelShaper;
@Shadow public abstract void render(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model);
@Inject(
method = "Lnet/minecraft/client/renderer/entity/ItemRenderer;render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$overridePokedexModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int i, int j, BakedModel model, CallbackInfo ci) {
boolean shouldOverride = renderMode != ItemDisplayContext.GUI && renderMode != ItemDisplayContext.FIXED;
if (shouldOverride && stack.getItem() instanceof PokedexItem) {
BakedModel replacementModel = CobblemonBakingOverrides.INSTANCE.getPokedexOverride(((PokedexItem) stack.getItem()).getType()).getModel();
if (!model.equals(replacementModel)) {
ci.cancel();
render(stack, renderMode, leftHanded, matrices, multiBufferSource, i, j, replacementModel);
}
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import net.minecraft.client.color.block.BlockColors;
@@ -20,8 +19,6 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.profiling.ProfilerFiller;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -31,16 +28,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ModelBakery.class)
public abstract class ModelLoaderMixin {
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
@Shadow protected abstract void registerModel(ModelResourceLocation modelId, UnbakedModel unbakedModel);
@Final
@Shadow
private Map<ResourceLocation, UnbakedModel> unbakedCache;
@Final
@Shadow
private static Logger LOGGER;
@Inject(
method = "<init>",
at = @At("TAIL")
@@ -51,19 +42,8 @@ public abstract class ModelLoaderMixin {
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
CallbackInfo ci) {
CobblemonBakingOverrides.INSTANCE.getModels().forEach(bakingOverride -> {
try {
BlockModel unbakedModel = this.loadBlockModel(bakingOverride.getModelLocation());
this.unbakedCache.put(bakingOverride.getModelIdentifier().id(), unbakedModel);
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
} catch (IOException e) {
LOGGER.error("Error loading a Cobblemon BakedModel:", e);
throw new RuntimeException(e);
}
var unbakedModel = this.getModel(bakingOverride.getModelLocation());
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
});
}
@Shadow
private BlockModel loadBlockModel(ResourceLocation id) throws IOException {
return null;
}
}
@@ -10,8 +10,13 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.util.Mth;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -23,6 +28,8 @@ public class PartyScrollMixin {
@Shadow
private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Inject(
method = "onScroll",
at = @At(
@@ -34,7 +41,7 @@ public class PartyScrollMixin {
),
cancellable = true
)
public void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) {
public void cobblemon$scrollParty(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int)accumulatedScrollY;
if (i > 0) {
@@ -50,4 +57,21 @@ public class PartyScrollMixin {
}
}
}
@Inject(
method = "onScroll",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Inventory;swapPaint(D)V"
),
cancellable = true
)
public void cobblemon$doPokedexZoom(long window, double horizontal, double vertical, CallbackInfo ci) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
usageContext.adjustZoom(vertical);
ci.cancel();
}
}
}
@@ -18,7 +18,18 @@ import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.pokemon.Pokemon;
import com.cobblemon.mod.common.util.CompoundTagExtensionsKt;
import com.cobblemon.mod.common.util.DataKeys;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -29,15 +40,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects;
import java.util.UUID;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity {
@@ -63,7 +65,7 @@ public abstract class PlayerMixin extends LivingEntity {
super(p_20966_, p_20967_);
}
@Inject(method = "respawnEntityOnShoulder", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/Level;)Ljava/util/Optional;"), cancellable = true)
@Inject(method = "respawnEntityOnShoulder", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/level/Level;)Ljava/util/Optional;"), cancellable = true)
private void cobblemon$removePokemon(CompoundTag nbt, CallbackInfo ci) {
if (CompoundTagExtensionsKt.isPokemonEntity(nbt)) {
final UUID uuid = this.getPokemonID(nbt);
@@ -159,4 +161,16 @@ public abstract class PlayerMixin extends LivingEntity {
}
}
}
@Inject(method = "isInvulnerableTo", at = @At("HEAD"), cancellable = true)
public void isInvulnerableTo(DamageSource source, CallbackInfoReturnable<Boolean> ci) {
if (!level().isClientSide) {
ServerPlayer player = (ServerPlayer)(Object)this;
boolean invulnerableInBattle = this.level().getGameRules().getBoolean(CobblemonGameRules.BATTLE_INVULNERABILITY);
boolean inBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (invulnerableInBattle && inBattle) {
ci.setReturnValue(true);
}
}
}
}
@@ -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.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(TargetingConditions.class)
public class TargetingConditionsMixin {
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
if (targetEntity instanceof ServerPlayer player) {
boolean canTargetInBattle = player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE);
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (!canTargetInBattle && targetInBattle) ci.setReturnValue(false);
}
}
}
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(AbstractSelectionList.class)
public interface EntryListWidgetAccessor {
@Accessor("renderHeader")
boolean getRenderHeader();
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import net.minecraft.client.player.AbstractClientPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(AbstractClientPlayer.class)
public class AbstractClientPlayerMixin {
@Inject(
method = "getFieldOfViewModifier()F",
at = @At(
value = "HEAD"
),
cancellable = true
)
public void cobblemon$getPokedexFovMultiplier(CallbackInfoReturnable<Float> cir) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
cir.setReturnValue(usageContext.getFovMultiplier());
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.gui.ScrollingWidget;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(AbstractSelectionList.class)
public abstract class EntryListWidgetMixin {
@SuppressWarnings("ConstantValue")
@ModifyConstant(method = "getEntryAtPosition", constant = @Constant(intValue = 4))
public int cobblemon$adjustOnlyIfNecessary(int input) {
if(ScrollingWidget.class.isInstance(this)) {
return 0;
}
return input;
}
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.CobblemonClient;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphics;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* Mixin to give us a hook to render the PartyOverlay below the Chat
*
* @author Qu
* @since 2022-02-22
*/
@Mixin(Gui.class)
public class GuiMixin {
//This injects into the actual BooleanSupplier lambda for the "this.layers.add(layeredDraw, () -> !minecraft.options.hideGui)" call
// sus af. You have to get the method name by looking at the bytecode of the class. Can do in intellij By clicking View -> Show Bytecode (top left)
@Inject(method = "method_55797", at = @At("HEAD"), cancellable = true)
private static void cobblemon$dontRenderUiInPokedex(Minecraft minecraft, CallbackInfoReturnable<Boolean> cir) {
if (CobblemonClient.INSTANCE.getPokedexUsageContext().getScanningGuiOpen()) {
cir.setReturnValue(false);
}
}
}
@@ -34,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
@Unique private static final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
@Unique private final String MODEL_PATH = Cobblemon.implementation.getModAPI() == ModAPI.FABRIC ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@@ -9,6 +9,9 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.CobblemonBuildDetails.smallCommitHash
//import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.SeasonResolver
import com.cobblemon.mod.common.api.data.DataProvider
@@ -16,11 +19,14 @@ import com.cobblemon.mod.common.api.drop.CommandDropEntry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_STARTED_POST
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_SCANNED
import com.cobblemon.mod.common.api.events.CobblemonEvents.STARTER_CHOSEN
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.permission.PermissionValidator
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
@@ -56,9 +62,11 @@ import com.cobblemon.mod.common.api.storage.factory.FileBackedPokemonStoreFactor
import com.cobblemon.mod.common.api.storage.molang.NbtMoLangDataStoreFactory
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerDataStoreManager
import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFactory
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BagItems
@@ -69,10 +77,12 @@ import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.command.argument.DexArgumentType
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.FormArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
@@ -82,6 +92,7 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.permission.LaxPermissionValidator
@@ -104,8 +115,6 @@ import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
@@ -152,7 +161,7 @@ object Cobblemon {
val battleRegistry = BattleRegistry
var storage = PokemonStoreManager()
var molangData = NbtMoLangDataStoreFactory
lateinit var playerData: PlayerDataStoreManager
lateinit var playerDataManager: PlayerInstancedDataStoreManager
lateinit var starterConfig: StarterConfig
val dataProvider: DataProvider = CobblemonDataProvider
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
@@ -200,7 +209,7 @@ object Cobblemon {
DATA_SYNCHRONIZED.subscribe {
storage.onPlayerDataSync(it)
playerData.get(it).sendToPlayer(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel).sendToPlayer(it)
}
@@ -208,7 +217,7 @@ object Cobblemon {
PCLinkManager.removeLink(it.player.uuid)
BattleRegistry.onPlayerDisconnect(it.player)
storage.onPlayerDisconnect(it.player)
playerData.onPlayerDisconnect(it.player)
playerDataManager.onPlayerDisconnect(it.player)
TradeManager.onLogoff(it.player)
}
PlatformEvents.PLAYER_DEATH.subscribe {
@@ -233,9 +242,6 @@ object Cobblemon {
}
}
// Register the grow_tumblestone advancement
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { AdvancementHandler.onTumbleStonePlaced(it) }
PlatformEvents.CHANGE_DIMENSION.subscribe {
it.player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
}
@@ -251,6 +257,8 @@ object Cobblemon {
}
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
}
fun initialize() {
@@ -292,16 +300,21 @@ object Cobblemon {
}
PlatformEvents.SERVER_STARTING.subscribe { event ->
val server = event.server
playerData = PlayerDataStoreManager().also { it.setup(server) }
playerDataManager = PlayerInstancedDataStoreManager().also { it.setup(server) }
val mongoClient: MongoClient?
val pokemonStoreRoot = server.getWorldPath(LevelResource.ROOT).resolve("pokemon").toFile()
val storeAdapter = when (config.storageFormat) {
"nbt", "json" -> {
val jsonFactory = JsonPlayerDataStoreFactory()
jsonFactory.setup(server)
playerData.setFactory(jsonFactory)
val generalJsonFactory = CachedPlayerDataStoreFactory(PlayerDataJsonBackend())
generalJsonFactory.setup(server)
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
pokedexNbtFactory.setup(server)
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreType.GENERAL)
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreType.POKEDEX)
if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
@@ -314,7 +327,9 @@ object Cobblemon {
}
}
/*
"mongodb" -> {
/*
try {
Class.forName("com.mongodb.client.MongoClient")
@@ -323,14 +338,18 @@ object Cobblemon {
.build()
mongoClient = MongoClients.create(mongoClientSettings)
val mongoFactory = MongoPlayerDataStoreFactory(mongoClient, config.mongoDBDatabaseName)
playerData.setFactory(mongoFactory)
playerData.setFactory(mongoFactory, PlayerInstancedDataStoreType.GENERAL)
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
} catch (e: ClassNotFoundException) {
LOGGER.error("MongoDB driver not found.")
throw e
}
*/
}
*/
else -> throw IllegalArgumentException("Unsupported storageFormat: ${config.storageFormat}")
}
.with(ReforgedConversion(server.getWorldPath(LevelResource.ROOT))) as FileStoreAdapter<*>
@@ -347,21 +366,13 @@ object Cobblemon {
PlatformEvents.SERVER_STOPPED.subscribe {
storage.unregisterAll()
playerData.saveAll()
playerDataManager.saveAllStores()
}
PlatformEvents.SERVER_STARTED.subscribe {
bestSpawner.onServerStarted()
battleRegistry.onServerStarted()
}
PlatformEvents.SERVER_TICK_POST.subscribe { ServerTickHandler.onTick(it.server) }
POKEMON_CAPTURED.subscribe { AdvancementHandler.onCapture(it) }
// EGG_HATCH.subscribe { AdvancementHandler.onHatch(it) }
BATTLE_VICTORY.subscribe { AdvancementHandler.onWinBattle(it) }
EVOLUTION_COMPLETE.subscribe(Priority.LOWEST) { event ->
AdvancementHandler.onEvolve(event)
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
BagItems.observable.subscribe {
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
@@ -373,8 +384,12 @@ object Cobblemon {
).ifSuccessful { it.mute = true }
}
//To whomever is merging, this is moved out of Cobblemon and into the CobblemonImplementations
//CobblemonSherds.registerSherds()
registerEventHandlers()
}
fun registerEventHandlers() {
AdvancementHandler.registerListeners()
PokedexHandler.registerListeners()
}
fun getLevel(dimension: ResourceKey<Level>): Level? {
@@ -472,13 +487,15 @@ object Cobblemon {
}
private fun registerArgumentTypes() {
this.implementation.registerCommandArgument(cobblemonResource("pokemon"), PokemonArgumentType::class, SingletonArgumentInfo.contextFree(PokemonArgumentType::pokemon))
this.implementation.registerCommandArgument(cobblemonResource("pokemon"), SpeciesArgumentType::class, SingletonArgumentInfo.contextFree(SpeciesArgumentType::species))
this.implementation.registerCommandArgument(cobblemonResource("pokemon_properties"), PokemonPropertiesArgumentType::class, SingletonArgumentInfo.contextFree(PokemonPropertiesArgumentType::properties))
this.implementation.registerCommandArgument(cobblemonResource("spawn_bucket"), SpawnBucketArgumentType::class, SingletonArgumentInfo.contextFree(SpawnBucketArgumentType::spawnBucket))
this.implementation.registerCommandArgument(cobblemonResource("move"), MoveArgumentType::class, SingletonArgumentInfo.contextFree(MoveArgumentType::move))
this.implementation.registerCommandArgument(cobblemonResource("party_slot"), PartySlotArgumentType::class, SingletonArgumentInfo.contextFree(PartySlotArgumentType::partySlot))
this.implementation.registerCommandArgument(cobblemonResource("pokemon_store"), PokemonStoreArgumentType::class, SingletonArgumentInfo.contextFree(PokemonStoreArgumentType::pokemonStore))
this.implementation.registerCommandArgument(cobblemonResource("dialogue"), DialogueArgumentType::class, SingletonArgumentInfo.contextFree(DialogueArgumentType::dialogue))
this.implementation.registerCommandArgument(cobblemonResource("form"), FormArgumentType::class, SingletonArgumentInfo.contextFree(FormArgumentType::form))
this.implementation.registerCommandArgument(cobblemonResource("dex"), DexArgumentType::class, SingletonArgumentInfo.contextFree (DexArgumentType::dex))
}
}
@@ -36,6 +36,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@JvmField
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.block.*
import com.cobblemon.mod.common.block.LecternBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
@@ -263,6 +264,27 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
)
@JvmField
val SMOOTH_TUMBLESTONE = this.create("smooth_tumblestone",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
)
)
@JvmField
val SMOOTH_TUMBLESTONE_STAIRS = this.create("smooth_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(SMOOTH_TUMBLESTONE.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SMOOTH_TUMBLESTONE)))
@JvmField
val SMOOTH_TUMBLESTONE_SLAB = this.create("smooth_tumblestone_slab", SlabBlock(BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICKS = this.create("tumblestone_bricks", Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_ORANGE)
@@ -270,8 +292,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
)
)
))
@JvmField
val TUMBLESTONE_BRICK_STAIRS = this.create("tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(TUMBLESTONE_BRICKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(TUMBLESTONE_BRICKS)))
@JvmField
@@ -342,6 +363,27 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
)
@JvmField
val SMOOTH_SKY_TUMBLESTONE = this.create("smooth_sky_tumblestone",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_LIGHT_BLUE)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
)
)
@JvmField
val SMOOTH_SKY_TUMBLESTONE_STAIRS = this.create("smooth_sky_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(SMOOTH_SKY_TUMBLESTONE.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SMOOTH_SKY_TUMBLESTONE)))
@JvmField
val SMOOTH_SKY_TUMBLESTONE_SLAB = this.create("smooth_sky_tumblestone_slab", SlabBlock(BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_LIGHT_BLUE)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICKS = this.create("sky_tumblestone_bricks",
Block(
BlockBehaviour.Properties.of()
@@ -422,6 +464,28 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
.instrument(NoteBlockInstrument.BASEDRUM)
)
)
@JvmField
val SMOOTH_BLACK_TUMBLESTONE = this.create("smooth_black_tumblestone",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
)
)
@JvmField
val SMOOTH_BLACK_TUMBLESTONE_STAIRS = this.create("smooth_black_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(SMOOTH_BLACK_TUMBLESTONE.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SMOOTH_BLACK_TUMBLESTONE)))
@JvmField
val SMOOTH_BLACK_TUMBLESTONE_SLAB = this.create("smooth_black_tumblestone_slab", SlabBlock(BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICKS = this.create("black_tumblestone_bricks",
Block(
@@ -464,6 +528,117 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
)
@JvmField
val FIRE_STONE_BLOCK = this.create("fire_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.FIRE)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val WATER_STONE_BLOCK = this.create("water_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.WATER)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val THUNDER_STONE_BLOCK = this.create("thunder_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.GLOW_LICHEN)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val LEAF_STONE_BLOCK = this.create("leaf_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_GREEN)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val ICE_STONE_BLOCK = this.create("ice_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.ICE)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val SUN_STONE_BLOCK = this.create("sun_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val MOON_STONE_BLOCK = this.create("moon_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.DEEPSLATE)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val SHINY_STONE_BLOCK = this.create("shiny_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.QUARTZ)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val DAWN_STONE_BLOCK = this.create("dawn_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.DIAMOND)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val DUSK_STONE_BLOCK = this.create("dusk_stone_block",
Block(
BlockBehaviour.Properties.of()
.mapColor(MapColor.WARPED_HYPHAE)
.strength(1.0F)
.sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS)
.requiresCorrectToolForDrops()
.instrument(NoteBlockInstrument.BIT)
)
)
@JvmField
val BLACK_APRICORN = apricornBlock("black_apricorn", Apricorn.BLACK)
@JvmField
@@ -638,6 +813,19 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
)
@JvmField
val LECTERN = create(
"lectern",
LecternBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.WOOD)
.instrument(NoteBlockInstrument.BASS)
.sound(SoundType.WOOD)
.strength(2.5F)
.ignitedByLava()
)
)
@JvmField
val DISPLAY_CASE = create(
"display_case",
@@ -647,7 +835,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
.noOcclusion()
.pushReaction(PushReaction.BLOCK)
.mapColor(MapColor.STONE)
.strength(0.3f)
.strength(0.3F)
)
)
@@ -39,6 +39,7 @@ object CobblemonCommands {
PcCommand.register(dispatcher)
SpawnPokemonFromPool.register(dispatcher)
PokeboxCommand.register(dispatcher)
PokedexCommand.register(dispatcher)
TestStoreCommand.register(dispatcher)
QueryLearnsetCommand.register(dispatcher)
TestPcSlotCommand.register(dispatcher)
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexTypes
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -152,6 +153,21 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val ANCIENT_ORIGIN_BALL = pokeBallItem(PokeBalls.ANCIENT_ORIGIN_BALL)
@JvmField
val POKEDEX_BLACK = create("pokedex_black", PokedexItem(PokedexTypes.BLACK))
@JvmField
val POKEDEX_BLUE = create("pokedex_blue", PokedexItem(PokedexTypes.BLUE))
@JvmField
val POKEDEX_GREEN = create("pokedex_green", PokedexItem(PokedexTypes.GREEN))
@JvmField
val POKEDEX_PINK = create("pokedex_pink", PokedexItem(PokedexTypes.PINK))
@JvmField
val POKEDEX_RED = create("pokedex_red", PokedexItem(PokedexTypes.RED))
@JvmField
val POKEDEX_WHITE = create("pokedex_white", PokedexItem(PokedexTypes.WHITE))
@JvmField
val POKEDEX_YELLOW = create("pokedex_yellow", PokedexItem(PokedexTypes.YELLOW))
@JvmField
val VIVICHOKE = compostableItem("vivichoke")
@@ -256,6 +272,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@JvmField
val LECTERN = blockItem("lectern", CobblemonBlocks.LECTERN)
// Evolution items
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
@@ -962,6 +980,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val CHISELED_POLISHED_TUMBLESTONE = blockItem("chiseled_polished_tumblestone", CobblemonBlocks.CHISELED_POLISHED_TUMBLESTONE)
@JvmField
val SMOOTH_TUMBLESTONE = blockItem("smooth_tumblestone", CobblemonBlocks.SMOOTH_TUMBLESTONE)
@JvmField
val SMOOTH_TUMBLESTONE_STAIRS = blockItem("smooth_tumblestone_stairs", CobblemonBlocks.SMOOTH_TUMBLESTONE_STAIRS)
@JvmField
val SMOOTH_TUMBLESTONE_SLAB = blockItem("smooth_tumblestone_slab", CobblemonBlocks.SMOOTH_TUMBLESTONE_SLAB)
@JvmField
val TUMBLESTONE_BRICKS = blockItem("tumblestone_bricks", CobblemonBlocks.TUMBLESTONE_BRICKS)
@JvmField
val TUMBLESTONE_BRICK_STAIRS = blockItem("tumblestone_brick_stairs", CobblemonBlocks.TUMBLESTONE_BRICK_STAIRS)
@@ -982,6 +1006,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val CHISELED_POLISHED_SKY_TUMBLESTONE = blockItem("chiseled_polished_sky_tumblestone", CobblemonBlocks.CHISELED_POLISHED_SKY_TUMBLESTONE)
@JvmField
val SMOOTH_SKY_TUMBLESTONE = blockItem("smooth_sky_tumblestone", CobblemonBlocks.SMOOTH_SKY_TUMBLESTONE)
@JvmField
val SMOOTH_SKY_TUMBLESTONE_STAIRS = blockItem("smooth_sky_tumblestone_stairs", CobblemonBlocks.SMOOTH_SKY_TUMBLESTONE_STAIRS)
@JvmField
val SMOOTH_SKY_TUMBLESTONE_SLAB = blockItem("smooth_sky_tumblestone_slab", CobblemonBlocks.SMOOTH_SKY_TUMBLESTONE_SLAB)
@JvmField
val SKY_TUMBLESTONE_BRICKS = blockItem("sky_tumblestone_bricks", CobblemonBlocks.SKY_TUMBLESTONE_BRICKS)
@JvmField
val SKY_TUMBLESTONE_BRICK_STAIRS = blockItem("sky_tumblestone_brick_stairs", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_STAIRS)
@@ -1002,6 +1032,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val CHISELED_POLISHED_BLACK_TUMBLESTONE = blockItem("chiseled_polished_black_tumblestone", CobblemonBlocks.CHISELED_POLISHED_BLACK_TUMBLESTONE)
@JvmField
val SMOOTH_BLACK_TUMBLESTONE = blockItem("smooth_black_tumblestone", CobblemonBlocks.SMOOTH_BLACK_TUMBLESTONE)
@JvmField
val SMOOTH_BLACK_TUMBLESTONE_STAIRS = blockItem("smooth_black_tumblestone_stairs", CobblemonBlocks.SMOOTH_BLACK_TUMBLESTONE_STAIRS)
@JvmField
val SMOOTH_BLACK_TUMBLESTONE_SLAB = blockItem("smooth_black_tumblestone_slab", CobblemonBlocks.SMOOTH_BLACK_TUMBLESTONE_SLAB)
@JvmField
val BLACK_TUMBLESTONE_BRICKS = blockItem("black_tumblestone_bricks", CobblemonBlocks.BLACK_TUMBLESTONE_BRICKS)
@JvmField
val BLACK_TUMBLESTONE_BRICK_STAIRS = blockItem("black_tumblestone_brick_stairs", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_STAIRS)
@@ -1012,6 +1048,27 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = blockItem("chiseled_black_tumblestone_bricks", CobblemonBlocks.CHISELED_BLACK_TUMBLESTONE_BRICKS)
@JvmField
val FIRE_STONE_BLOCK = blockItem("fire_stone_block", CobblemonBlocks.FIRE_STONE_BLOCK)
@JvmField
val WATER_STONE_BLOCK = blockItem("water_stone_block", CobblemonBlocks.WATER_STONE_BLOCK)
@JvmField
val THUNDER_STONE_BLOCK = blockItem("thunder_stone_block", CobblemonBlocks.THUNDER_STONE_BLOCK)
@JvmField
val LEAF_STONE_BLOCK = blockItem("leaf_stone_block", CobblemonBlocks.LEAF_STONE_BLOCK)
@JvmField
val ICE_STONE_BLOCK = blockItem("ice_stone_block", CobblemonBlocks.ICE_STONE_BLOCK)
@JvmField
val SUN_STONE_BLOCK = blockItem("sun_stone_block", CobblemonBlocks.SUN_STONE_BLOCK)
@JvmField
val MOON_STONE_BLOCK = blockItem("moon_stone_block", CobblemonBlocks.MOON_STONE_BLOCK)
@JvmField
val SHINY_STONE_BLOCK = blockItem("shiny_stone_block", CobblemonBlocks.SHINY_STONE_BLOCK)
@JvmField
val DAWN_STONE_BLOCK = blockItem("dawn_stone_block", CobblemonBlocks.DAWN_STONE_BLOCK)
@JvmField
val DUSK_STONE_BLOCK = blockItem("dusk_stone_block", CobblemonBlocks.DUSK_STONE_BLOCK)
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
@@ -24,6 +24,7 @@ import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
@@ -31,6 +32,7 @@ import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedScanHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
@@ -88,19 +90,14 @@ import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPac
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.SwapClientPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePartyPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
@@ -110,6 +107,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotification
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
@@ -127,6 +125,7 @@ import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
@@ -185,6 +184,10 @@ import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedScanPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -264,6 +267,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SummaryUIPacket.ID, SummaryUIPacket::decode, SummaryUIPacketHandler))
list.add(PacketRegisterInfo(InteractPokemonUIPacket.ID, InteractPokemonUIPacket::decode, InteractPokemonUIPacketHandler))
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
// Starter packets
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
@@ -317,6 +321,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(NPCRegistrySyncPacket.ID, NPCRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PokeRodRegistrySyncPacket.ID, PokeRodRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(ScriptRegistrySyncPacket.ID, ScriptRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
@@ -366,6 +371,8 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(CloseNPCEditorPacket.ID, CloseNPCEditorPacket::decode, CloseNPCEditorHandler))
list.add(PacketRegisterInfo(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler))
//Pokedex scanning
list.add(PacketRegisterInfo(ServerConfirmedScanPacket.ID, ServerConfirmedScanPacket::decode, ServerConfirmedScanHandler))
return list
}
@@ -421,6 +428,10 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(OfferTradePacket.ID, OfferTradePacket::decode, OfferTradeHandler))
list.add(PacketRegisterInfo(UpdateTradeOfferPacket.ID, UpdateTradeOfferPacket::decode, UpdateTradeOfferHandler))
// Pokedex scanning
list.add(PacketRegisterInfo(StartScanningPacket.ID, StartScanningPacket::decode, StartScanningHandler))
list.add(PacketRegisterInfo(FinishScanningPacket.ID, FinishScanningPacket::decode, FinishScanningHandler))
// Pasture
list.add(PacketRegisterInfo(PasturePokemonPacket.ID, PasturePokemonPacket::decode, PasturePokemonHandler))
list.add(PacketRegisterInfo(UnpasturePokemonPacket.ID, UnpasturePokemonPacket::decode, UnpasturePokemonHandler))
@@ -61,14 +61,43 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val POKE_BALL_TRAIL = this.create("poke_ball.trail")
@JvmField
val POKEDEX_OPEN = this.create("item.pokedex.open")
@JvmField
val POKEDEX_CLOSE = this.create("item.pokedex.close")
@JvmField
val POKEDEX_SCAN_OPEN = this.create("item.pokedex.scan_open")
@JvmField
val POKEDEX_SCAN_CLOSE = this.create("item.pokedex.scan_close")
@JvmField
val POKEDEX_SCAN = this.create("item.pokedex.scan")
@JvmField
val POKEDEX_SCAN_LOOP = this.create("item.pokedex.scan_loop")
@JvmField
val POKEDEX_SCAN_DETAIL = this.create("item.pokedex.scan_detail")
@JvmField
val POKEDEX_CLICK = this.create("item.pokedex.click")
@JvmField
val POKEDEX_CLICK_SHORT = this.create("item.pokedex.click_short")
@JvmField
val POKEDEX_SNAP_PICTURE = this.create("item.pokedex.snap")
@JvmField
val POKEDEX_ZOOM_IN = this.create("item.pokedex.zoom_in")
@JvmField
val POKEDEX_ZOOM_OUT = this.create("item.pokedex.zoom_out")
@JvmField
val POKEDEX_ZOOM_INCREMENT = this.create("item.pokedex.zoom_increment")
@JvmField
val ITEM_USE = this.create("item.use")
@JvmField
val CAN_EVOLVE = this.create("pokemon.can_evolve")
val EVOLUTION_NOTIFICATION = this.create("evolution.notification")
@JvmField
val EVOLVING = this.create("pokemon.evolving")
val EVOLUTION_UI = this.create("evolution.ui")
@JvmField
val EVOLVE = this.create("evolution.evolve")
val EVOLUTION = this.create("evolution")
@JvmField
val PVN_BATTLE = this.create("battle.pvn.default")
@@ -161,6 +190,15 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("tumblestone.step")
@JvmField
val EVOLUTION_STONE_BLOCK_BREAK = this.create("evolution_stone_block.break")
@JvmField
val EVOLUTION_STONE_BLOCK_HIT = this.create("evolution_stone_block.hit")
@JvmField
val EVOLUTION_STONE_BLOCK_PLACE = this.create("evolution_stone_block.place")
@JvmField
val EVOLUTION_STONE_BLOCK_STEP = this.create("evolution_stone_block.step")
@JvmField
val GIMMIGHOUL_GIVE_ITEM_SMALL = this.create("gimmighoul.give_item_small")
@JvmField
@@ -210,7 +248,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val RELIC_COIN_SACK_SOUNDS = SoundType(1f, 1f,
val RELIC_COIN_SACK_SOUNDS = SoundType(1f, 1.1f,
RELIC_COIN_SACK_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_SACK_PLACE,
@@ -218,7 +256,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
RELIC_COIN_SACK_STEP
)
@JvmField
val RELIC_COIN_POUCH_SOUNDS = SoundType(1f, 1f,
val RELIC_COIN_POUCH_SOUNDS = SoundType(1f, 1.1f,
RELIC_COIN_POUCH_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_POUCH_PLACE,
@@ -264,7 +302,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val IMPACT_WATER = this.create("impact.water")
@JvmField
val TUMBLESTONE_SOUNDS = SoundType(1f, 1f,
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
TUMBLESTONE_BREAK,
TUMBLESTONE_STEP,
TUMBLESTONE_PLACE,
@@ -273,16 +311,25 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val TUMBLESTONE_BLOCK_SOUNDS = SoundType(1f, 1f,
val TUMBLESTONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
TUMBLESTONE_BLOCK_BREAK,
TUMBLESTONE_STEP,
TUMBLESTONE_PLACE,
TUMBLESTONE_HIT,
TUMBLESTONE_STEP
)
@JvmField
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
EVOLUTION_STONE_BLOCK_BREAK,
EVOLUTION_STONE_BLOCK_STEP,
EVOLUTION_STONE_BLOCK_PLACE,
EVOLUTION_STONE_BLOCK_HIT,
EVOLUTION_STONE_BLOCK_STEP
)
@JvmField
val BERRY_BUSH_SOUNDS = SoundType(1f, 1f,
val BERRY_BUSH_SOUNDS = SoundType(0.8f, 1.1f,
BERRY_BUSH_BREAK,
SoundEvents.GRASS_STEP,
BERRY_BUSH_PLACE,
@@ -291,25 +338,25 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val BIG_ROOT_SOUNDS = SoundType(1f, 1f,
val BIG_ROOT_SOUNDS = SoundType(0.8f, 1.1f,
BIG_ROOT_BREAK,
SoundEvents.ROOTS_STEP,
SoundEvents.ROOTS_PLACE,
SoundEvents.ROOTS_HIT,
SoundEvents.ROOTS_FALL
)
@JvmField
val ENERGY_ROOT_SOUNDS = SoundType(1f, 1f,
SoundEvents.ROOTS_BREAK,
SoundEvents.ROOTS_STEP,
ENERGY_ROOT_PLACE,
SoundEvents.ROOTS_HIT,
SoundEvents.ROOTS_FALL
)
@JvmField
val MEDICINAL_LEEK_SOUNDS = SoundType(1f, 1f,
val ENERGY_ROOT_SOUNDS = SoundType(0.8f, 1.1f,
BIG_ROOT_BREAK,
SoundEvents.ROOTS_STEP,
ENERGY_ROOT_PLACE,
SoundEvents.ROOTS_HIT,
SoundEvents.ROOTS_FALL
)
@JvmField
val MEDICINAL_LEEK_SOUNDS = SoundType(1f, 1.1f,
MEDICINAL_LEEK_BREAK,
SoundEvents.GRASS_STEP,
MEDICINAL_LEEK_PLACE,
@@ -318,7 +365,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val VIVICHOKE_SOUNDS = SoundType(1f, 1f,
val VIVICHOKE_SOUNDS = SoundType(0.6f, 1.1f,
VIVICHOKE_BREAK,
SoundEvents.GRASS_STEP,
VIVICHOKE_PLACE,
@@ -327,7 +374,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val MINT_SOUNDS = SoundType(1f, 1f,
val MINT_SOUNDS = SoundType(0.6f, 1.1f,
MINT_BREAK,
SoundEvents.GRASS_STEP,
MINT_PLACE,
@@ -336,7 +383,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val REVIVAL_HERB_SOUNDS = SoundType(1f, 1f,
val REVIVAL_HERB_SOUNDS = SoundType(0.6f, 1.1f,
REVIVAL_HERB_BREAK,
SoundEvents.GRASS_STEP,
REVIVAL_HERB_PLACE,
@@ -345,7 +392,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val GILDED_CHEST_SOUNDS = SoundType(1f, 1f,
val GILDED_CHEST_SOUNDS = SoundType(1f, 1.1f,
GILDED_CHEST_BREAK,
GILDED_CHEST_STEP,
GILDED_CHEST_PLACE,
@@ -59,7 +59,7 @@ class AspectCriterion(
// pokemon = json.get("pokemon").asString.asIdentifierDefaultingNamespace()
// }
//
// override fun matches(player: ServerPlayerEntity, context: MutableMap<Identifier, MutableSet<String>>): Boolean {
// override fun matches(player: ServerPlayer, context: MutableMap<Identifier, MutableSet<String>>): Boolean {
// val caughtAspects = context.getOrDefault(pokemon, mutableSetOf())
// return this.aspects.all { it in caughtAspects }
// }
@@ -34,8 +34,7 @@ class BattleCountableCriterion(
override fun matches(player: ServerPlayer, context: BattleCountableContext): Boolean {
var typeCheck = false
val advancementData = Cobblemon.playerData.get(player).advancementData
val advancementData = Cobblemon.playerDataManager.getGenericData(player).advancementData
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
typeCheck = true
}
@@ -87,7 +86,7 @@ class BattleCountableCriterion(
// json.add("battle_types", battleTypes.toJsonArray())
// }
//
// override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
// override fun matches(player: ServerPlayer, context: BattleCountableContext): Boolean {
// var typeCheck = false
// val advancementData = Cobblemon.playerData.get(player).advancementData
// if (battleTypes.isEmpty() || battleTypes.contains("any")) {
@@ -54,7 +54,7 @@ class CaughtPokemonCriterion(
// type = json.get("type")?.asString ?: "any"
// }
//
// override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
// override fun matches(player: ServerPlayer, context: CountablePokemonTypeContext): Boolean {
// return super.matches(player, context) && (context.type == type || type == "any")
// }
//}
@@ -50,7 +50,7 @@ open class CountableCriterion<T : CountableContext>(
// json.addProperty("count", count)
// }
//
// override fun matches(player: ServerPlayerEntity, context: T) = context.times >= count
// override fun matches(player: ServerPlayer, context: T) = context.times >= count
//}
//
///**
@@ -64,7 +64,7 @@ open class CountableCriterion<T : CountableContext>(
// * @since November 4th, 2022
// */
//class SimpleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountableContext>(id, predicate)
//fun SimpleCriterionTrigger<CountableContext, SimpleCountableCriterionCondition>.trigger(player: ServerPlayerEntity, times: Int) = trigger(player, CountableContext(times))
//fun SimpleCriterionTrigger<CountableContext, SimpleCountableCriterionCondition>.trigger(player: ServerPlayer, times: Int) = trigger(player, CountableContext(times))
//
///**
// * Some type of context that has a count associated with it, representing how many times the trigger has occurred.
@@ -55,7 +55,7 @@ class EvolvePokemonCriterion(
// evolution = json.get("evolution")?.asString ?: "any"
// }
//
// override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
// override fun matches(player: ServerPlayer, context: EvolvePokemonContext): Boolean {
// return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
// (context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
// }
@@ -57,7 +57,7 @@ class LevelUpCriterion(
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
override fun matches(player: ServerPlayer, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
@@ -79,7 +79,7 @@ class PartyCheckCriterion(
}
}
override fun matches(player: ServerPlayerEntity, context: PartyCheckContext): Boolean {
override fun matches(player: ServerPlayer, context: PartyCheckContext): Boolean {
val playerParty = player.party()
val matches = mutableListOf<Identifier>()
party.forEach {
@@ -57,7 +57,7 @@ class PlantTumblestoneCriterion(
// * @param context The context of the criterion
// * @return True if the tumblestone block can grow at the given position, false otherwise
// */
// override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
// }
//}
@@ -80,7 +80,7 @@ class TradePokemonCriterion(
receivedHeldItem = json.get("received_held_item")?.asString ?: "minecraft:air"
}
override fun matches(player: ServerPlayerEntity, context: TradePokemonContext): Boolean {
override fun matches(player: ServerPlayer, context: TradePokemonContext): Boolean {
val heldItem1 = context.traded.heldItem().item.registryEntry.ResourceKey().value
val heldItem2 = context.received.heldItem().item.registryEntry.ResourceKey().value
return (context.traded.species.resourceIdentifier == traded.asIdentifierDefaultingNamespace() || traded == "any") &&
@@ -33,6 +33,7 @@ import java.io.PrintWriter
* @since April 1st, 2022.
*/
object JsonManifestWalker {
// im walkin ere
/**
* Builds a manifest at [manifestPath] using the files inside the same folder.
*
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
@@ -114,7 +115,7 @@ open class PokemonBattle(
var ended = false
// TEMP battle showcase stuff
var announcingRules = false
var turn: Int = 1
var turn: Int = 0
private set
private var ticks: Int = 0
@@ -141,7 +141,7 @@ abstract class BattleActor(
* NPC and player actors expect to append their [getName] while wild Pokémon append nothing.
*
* @param name The name of an object being appended, typically a Pokémon nickname received from showdown.
* @return A [MutableText] of the [name] append with owner prefix.
* @return A [MutableComponent] of the [name] append with owner prefix.
*/
abstract fun nameOwned(name: String): MutableComponent
@@ -27,6 +27,7 @@ import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
import com.cobblemon.mod.common.api.events.pokedex.scanning.PokemonScannedEvent
import com.cobblemon.mod.common.api.events.pokemon.*
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
@@ -40,6 +41,7 @@ import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.api.reactive.EventObservable
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.Observable.Companion.filter
import com.cobblemon.mod.common.api.reactive.Observable.Companion.map
import com.cobblemon.mod.common.api.reactive.SimpleObservable
@@ -67,8 +69,6 @@ object CobblemonEvents {
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
@JvmField
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
@JvmField
val HELD_ITEM_UPDATED = CancelableObservable<HeldItemUpdatedEvent>()
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@@ -147,6 +147,9 @@ object CobblemonEvents {
@JvmField
val STARTER_CHOSEN = CancelableObservable<StarterChosenEvent>()
@JvmField
val POKEMON_SCANNED = EventObservable<PokemonScannedEvent>()
@JvmField
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
// Berries
@@ -166,4 +169,9 @@ object CobblemonEvents {
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
}
@JvmField
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@JvmField
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
}
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokedex.scanning
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
data class PokemonScannedEvent(val player: ServerPlayer, val scannedEntity: PokemonEntity) {
val pokemon: Pokemon
get() = scannedEntity.pokemon
val isOwned: Boolean
get() = pokemon.getOwnerPlayer() == player
}
@@ -10,5 +10,5 @@ package com.cobblemon.mod.common.api.events.pokemon
//data class HatchEggEvent (
// val pokemon: Pokemon,
// val player: ServerPlayerEntity
// val player: ServerPlayer
//)
@@ -6,12 +6,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.player.adapter
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.storage.player.PlayerData
import java.util.*
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.UUID
interface PlayerDataStoreAdapter {
fun load(uuid: UUID): PlayerData
fun save(playerData: PlayerData)
data class PokemonGainedEvent(
val playerId: UUID,
val pokemon: Pokemon
) {
}
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import java.util.UUID
data class PokemonSeenEvent(
val playerId: UUID,
val pokemon: Pokemon
) {
}
@@ -29,7 +29,7 @@ data class PokeRod(
val pokeBallId: ResourceLocation,
//Hex string of color
val lineColor: String,
var name: ResourceLocation// ?// TODO (techdaan) Why was this nullable?
var name: ResourceLocation
) {
internal fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(name)
@@ -10,10 +10,14 @@ package com.cobblemon.mod.common.api.gui
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.SpriteType
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.toHex
import com.mojang.authlib.minecraft.client.MinecraftClient
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.platform.Lighting
import com.mojang.blaze3d.systems.RenderSystem
@@ -94,8 +98,6 @@ fun drawRectangle(
bufferbuilder.addVertex(matrix, endX, endY, blitOffset).setUv(maxU, maxV)
bufferbuilder.addVertex(matrix, endX, y, blitOffset).setUv(maxU, minV)
bufferbuilder.addVertex(matrix, x, y, blitOffset).setUv(minU, minV)
// TODO: Figure out if this is correct replacement.
// OLD: BufferRenderer.draw(bufferbuilder)
BufferUploader.drawWithShader(bufferbuilder.buildOrThrow())
}
@@ -114,21 +116,6 @@ fun drawCenteredText(
context.drawString(textRenderer, comp, x.toInt() - textRenderer.width(comp) / 2, y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawTextJustifiedRight(
context: GuiGraphics,
font: ResourceLocation? = null,
text: MutableComponent,
x: Number,
y: Number,
colour: Int,
shadow: Boolean = true
) {
val comp = text.let { if (font != null) it.font(font) else it }
val font = Minecraft.getInstance().font
context.drawString(font, comp, x.toInt() - font.width(comp), y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawText(
context: GuiGraphics,
@@ -161,6 +148,21 @@ fun drawText(
return isHovered
}
@JvmOverloads
fun drawTextJustifiedRight(
context: GuiGraphics,
font: ResourceLocation? = null,
text: MutableComponent,
x: Number,
y: Number,
colour: Int,
shadow: Boolean = true
) {
val comp = text.let { if (font != null) it.font(font) else it }
val font = Minecraft.getInstance().font
context.drawString(font, comp, x.toInt() - font.width(comp), y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawText(
context: GuiGraphics,
@@ -214,60 +216,77 @@ fun drawPosablePortrait(
limbSwingAmount: Float = 0F,
ageInTicks: Float = 0F,
headYaw: Float = 0F,
headPitch: Float = 0F
headPitch: Float = 0F,
r: Float = 1F,
g: Float = 1F,
b: Float = 1F,
a: Float = 1F
) {
val model = repository.getPoser(identifier, aspects)
state.currentAspects = aspects
state.currentModel = model
val texture = repository.getTexture(identifier, aspects, state.animationSeconds)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(identifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, contextScale)
context.put(RenderContext.SPECIES, identifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.POSABLE_STATE, state)
val renderType = RenderType.entityCutout(texture)
RenderSystem.applyModelViewMatrix()
val quaternion1 = Axis.YP.rotationDegrees(-32F * if (reversed) -1F else 1F)
val quaternion2 = Axis.XP.rotationDegrees(5F)
val originalPose = state.currentPose
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
originalPose?.let { state.setPose(it) }
matrixStack.pushPose()
matrixStack.translate(0.0, PORTRAIT_DIAMETER.toDouble() + 2.0, 0.0)
matrixStack.scale(scale, scale, -scale)
matrixStack.translate(0.0, -PORTRAIT_DIAMETER / 18.0, 0.0)
matrixStack.translate(model.portraitTranslation.x * if (reversed) -1F else 1F, model.portraitTranslation.y, model.portraitTranslation.z - 4)
matrixStack.scale(model.portraitScale, model.portraitScale, 1 / model.portraitScale)
matrixStack.mulPose(quaternion1) // TODO (techdaan): correct?
matrixStack.mulPose(quaternion2)
val light1 = Vector3f(0.2F, 1.0F, -1.0F)
val light2 = Vector3f(0.1F, 0.0F, 8.0F)
RenderSystem.setShaderLights(light1, light2)
quaternion1.conjugate()
val sprite = repository.getSprite(identifier, aspects, SpriteType.PORTRAIT);
val immediate = Minecraft.getInstance().renderBuffers().bufferSource()
val buffer = immediate.getBuffer(renderType)
val packedLight = LightTexture.pack(11, 7)
if (sprite == null) {
val model = repository.getPoser(identifier, aspects)
state.currentAspects = aspects
state.currentModel = model
val texture = repository.getTexture(identifier, aspects, state.animationSeconds)
model.withLayerContext(immediate, state, repository.getLayers(identifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
immediate.endBatch()
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(identifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, contextScale)
context.put(RenderContext.SPECIES, identifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.POSABLE_STATE, state)
val renderType = RenderType.entityCutout(texture)
val quaternion1 = Axis.YP.rotationDegrees(-32F * if (reversed) -1F else 1F)
val quaternion2 = Axis.XP.rotationDegrees(5F)
val originalPose = state.currentPose
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
originalPose?.let { state.setPose(it) }
matrixStack.translate(
model.portraitTranslation.x * if (reversed) -1F else 1F,
model.portraitTranslation.y,
model.portraitTranslation.z - 4
)
matrixStack.scale(model.portraitScale, model.portraitScale, 1 / model.portraitScale)
matrixStack.mulPose(quaternion1)
matrixStack.mulPose(quaternion2)
val light1 = Vector3f(0.2F, 1.0F, -1.0F)
val light2 = Vector3f(0.1F, 0.0F, 8.0F)
RenderSystem.setShaderLights(light1, light2)
quaternion1.conjugate()
val immediate = Minecraft.getInstance().renderBuffers().bufferSource()
val buffer = immediate.getBuffer(renderType)
val packedLight = LightTexture.pack(11, 7)
val colour = toHex(r, g, b, a)
model.withLayerContext(immediate, state, repository.getLayers(identifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, colour)
immediate.endBatch()
}
model.setDefault()
Lighting.setupFor3DItems()
} else {
renderSprite(matrixStack, sprite)
}
matrixStack.popPose()
model.setDefault()
Lighting.setupFor3DItems()
}
fun drawProfile(
@@ -279,50 +298,75 @@ fun drawProfile(
partialTicks: Float,
scale: Float = 20F
) {
val model = repository.getPoser(resourceIdentifier, aspects)
val texture = repository.getTexture(resourceIdentifier, aspects, state.animationSeconds)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(resourceIdentifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, 1F)
context.put(RenderContext.SPECIES, resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.POSABLE_STATE, state)
state.currentAspects = aspects
state.currentModel = model
val renderType = RenderType.entityCutout(texture)//model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
matrixStack.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrixStack.scale(model.profileScale, model.profileScale, 1 / model.profileScale)
val sprite = repository.getSprite(resourceIdentifier, aspects, SpriteType.PROFILE);
if(sprite == null) {
val model = repository.getPoser(resourceIdentifier, aspects)
val texture = repository.getTexture(resourceIdentifier, aspects, state.animationSeconds)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(resourceIdentifier, aspects, 0f)
.let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, 1F)
context.put(RenderContext.SPECIES, resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
context.put(RenderContext.POSABLE_STATE, state)
state.currentAspects = aspects
state.currentModel = model
val renderType = RenderType.entityCutout(texture)//model.getLayer(texture)
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
matrixStack.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrixStack.scale(model.profileScale, model.profileScale, 1 / model.profileScale)
// matrixStack.multiply(rotation)
val quaternion1 = Axis.YP.rotationDegrees(-32F * if (false) -1F else 1F)
val quaternion2 = Axis.XP.rotationDegrees(5F)
matrixStack.mulPose(quaternion1)
matrixStack.mulPose(quaternion2)
Lighting.setupForEntityInInventory() // TODO (techdaan): Does this map correctly?
val entityRenderDispatcher = Minecraft.getInstance().entityRenderDispatcher
entityRenderDispatcher.setRenderShadow(true)
val quaternion1 = Axis.YP.rotationDegrees(-32F * if (false) -1F else 1F)
val quaternion2 = Axis.XP.rotationDegrees(5F)
matrixStack.mulPose(quaternion1)
matrixStack.mulPose(quaternion2)
Lighting.setupForEntityInInventory()
val entityRenderDispatcher = Minecraft.getInstance().entityRenderDispatcher
entityRenderDispatcher.setRenderShadow(true)
val bufferSource = Minecraft.getInstance().renderBuffers().bufferSource()
val buffer = bufferSource.getBuffer(renderType)
val light1 = Vector3f(-1F, 1F, 1.0F)
val light2 = Vector3f(1.3F, -1F, 1.0F)
RenderSystem.setShaderLights(light1, light2)
val packedLight = LightTexture.pack(11, 7)
val bufferSource = Minecraft.getInstance().renderBuffers().bufferSource()
val buffer = bufferSource.getBuffer(renderType)
val light1 = Vector3f(-1F, 1F, 1.0F)
val light2 = Vector3f(1.3F, -1F, 1.0F)
RenderSystem.setShaderLights(light1, light2)
val packedLight = LightTexture.pack(11, 7)
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
bufferSource.endBatch()
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
bufferSource.endBatch()
}
model.setDefault()
entityRenderDispatcher.setRenderShadow(true)
Lighting.setupFor3DItems()
} else {
renderSprite(matrixStack, sprite)
}
model.setDefault()
entityRenderDispatcher.setRenderShadow(true)
Lighting.setupFor3DItems()
}
fun renderSprite(matrixStack: PoseStack, sprite: ResourceLocation) {
val matrix: PoseStack.Pose = matrixStack.last()
matrix.pose().translate(-1f, 0f, 0f)
RenderSystem.setShaderTexture(0, sprite);
RenderSystem.setShader(GameRenderer::getPositionTexShader)
var buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX)
buffer.addVertex(matrix, 2f, 0f, 0.0f).setUv(1f, 0f)
buffer.addVertex(matrix, 0f, 0f, 0.0f).setUv(0f, 0f)
buffer.addVertex(matrix, 0f, 2f, 0.0f).setUv(0f, 1f)
buffer.addVertex(matrix, 2f, 2f, 0.0f).setUv(1f, 1f)
BufferUploader.drawWithShader(buffer.buildOrThrow())
}
@@ -41,20 +41,27 @@ class MultiLineLabelK(
fun renderLeftAligned(
context: GuiGraphics,
x: Number, y: Number,
YStartOffset: Number = 0,
ySpacing: Number,
colour: Int,
scale: Float = 1F,
shadow: Boolean = true
) {
context.pose().pushPose()
context.pose().scale(scale, scale, 1F)
comps.forEachIndexed { index, textWithWidth ->
val yOffset = if (index == 0) YStartOffset else 0
drawString(
context = context,
x = x, y = y.toFloat() + ySpacing.toFloat() * index,
x = x.toFloat() / scale,
y = (y.toFloat() + yOffset.toFloat() + ySpacing.toFloat() * index) / scale,
colour = colour,
shadow = shadow,
text = textWithWidth.text.string,
font = font
)
}
context.pose().popPose()
}
class TextWithWidth internal constructor(val text: FormattedText, val width: Int)
@@ -99,7 +99,7 @@ interface PokemonSelectingItem {
stack.shrink(1)
battlePokemon.actor.itemsUsed.add(bagItem)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.entity!!.pokemon.species.resourceIdentifier, BuiltInRegistries.ITEM.getKey(stack.item)))
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.effectedPokemon.species.resourceIdentifier, BuiltInRegistries.ITEM.getKey(stack.item)))
}
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.util.asExpressionLike
import com.mojang.serialization.Codec
/**
* An object that can be given a [MoLangRuntime] to produce a single [MoValue]. This abstracts
@@ -31,4 +33,11 @@ interface ExpressionLike {
fun resolveBoolean(runtime: MoLangRuntime) = resolveDouble(runtime) == 1.0
fun resolveObject(runtime: MoLangRuntime) = resolve(runtime) as ObjectValue<*>
companion object {
val CODEC: Codec<ExpressionLike> = Codec.STRING.xmap({
it.asExpressionLike()
}) {
it.getString()
}
}
}
@@ -35,10 +35,8 @@ class MoveSet : Iterable<Move> {
/**
* Gets all Moves from the Pokémon but skips null Moves
*/
fun getMoves(): List<Move> {
return moves.filterNotNull()
}
fun getMoves() = moves.filterNotNull()
fun getMovesWithNulls() = moves.toList()
fun hasSpace() = moves.any { it == null }
/**
@@ -125,6 +123,7 @@ class MoveSet : Iterable<Move> {
for (i in 0 until MOVE_COUNT) {
if (moves[i] == null) {
moves[i] = move
move.observable.subscribe { this.update() }
update()
return
}
@@ -90,7 +90,7 @@ interface EntityProvider {
}
//class MoveAnimationKeyframe(
// val sound: Identifier? = null,
// val sound: ResourceLocation? = null,
// val animation: List<AnimationWithMoments> = listOf(),
// val effects: List<ActionEffect> = listOf()
//)
@@ -13,7 +13,7 @@ import java.util.concurrent.CompletableFuture
class CannotInterruptActionEffectKeyframe : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
context.canBeInterrupted = true
context.canBeInterrupted = false
return CompletableFuture.completedFuture(Unit)
}
}
@@ -71,6 +71,7 @@ object CobblemonPermissions {
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val POKEDEX = this.create("${COMMAND_PREFIX}pokedex", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -163,7 +163,6 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("safari_ball", CatchRateModifiers.SAFARI)
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
createDefault("level_ball", CatchRateModifiers.LEVEL)
// ToDo we will need fishing context here once fishing is implemented for a multiplier
createDefault("lure_ball", CatchRateModifiers.LURE)
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
createDefault("love_ball", CatchRateModifiers.LOVE)
@@ -0,0 +1,152 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.resources.ResourceLocation
abstract class AbstractPokedexManager {
open val speciesRecords: MutableMap<ResourceLocation, SpeciesDexRecord> = mutableMapOf()
private val dexCalculatedValues = mutableMapOf<ResourceLocation, MutableMap<PokedexValueCalculator<*>, Any>>()
private val globalCalculatedValues = mutableMapOf<GlobalPokedexValueCalculator<*>, Any>()
@Transient
val struct = QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("get_species_record") { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
}
fun deleteSpeciesRecord(speciesId: ResourceLocation) {
speciesRecords.remove(speciesId)
markDirty()
}
fun getSpeciesRecord(speciesId: ResourceLocation): SpeciesDexRecord? {
return speciesRecords[speciesId]
}
fun getOrCreateSpeciesRecord(speciesId: ResourceLocation): SpeciesDexRecord {
return speciesRecords.getOrPut(speciesId) {
val record = SpeciesDexRecord()
record.initialize(this, speciesId)
onSpeciesRecordUpdated(record)
record
}
}
fun getHighestKnowledgeFor(entry: PokedexEntry): PokedexEntryProgress {
val speciesRecord = getSpeciesRecord(entry.speciesId) ?: return PokedexEntryProgress.NONE
val hasAllAspects = entry.conditionAspects.all(speciesRecord::hasAspect)
if (!hasAllAspects) {
return PokedexEntryProgress.NONE
}
// For each distinct form in this entry, get the highest knowledge level for the unlock forms, then take the highest of those.
return entry.forms.maxOfOrNull { form ->
form.unlockForms.maxOfOrNull {
speciesRecord.getFormRecord(it)?.knowledge ?: PokedexEntryProgress.NONE
} ?: PokedexEntryProgress.NONE
} ?: PokedexEntryProgress.NONE
}
fun getEncounteredForms(entry: PokedexEntry) : List<PokedexForm> {
return getFormsWithKnowledge(entry, PokedexEntryProgress.ENCOUNTERED)
}
fun getCaughtForms(entry: PokedexEntry) : List<PokedexForm> {
return getFormsWithKnowledge(entry, PokedexEntryProgress.CAUGHT)
}
fun getFormsWithKnowledge(entry: PokedexEntry, knowledge: PokedexEntryProgress) : List<PokedexForm> {
val speciesRecord = getSpeciesRecord(entry.speciesId) ?: return emptyList()
val hasAllAspects = entry.conditionAspects.all(speciesRecord::hasAspect)
if (!hasAllAspects) {
return emptyList()
}
// For each distinct form in this entry, get the highest knowledge level for the unlock forms, then take the highest of those.
return entry.forms.filter { form ->
form.unlockForms.any { (speciesRecord.getFormRecord(it)?.knowledge ?: PokedexEntryProgress.NONE) >= knowledge }
}
}
fun getSeenShinyStates(entry: PokedexEntry, form: PokedexForm): Set<String> {
return getSpeciesRecord(entry.speciesId)?.getFormRecord(form.displayForm)?.getSeenShinyStates() ?: return emptySet()
}
fun getSeenGenders(entry: PokedexEntry, form: PokedexForm): Set<Gender> {
return getSpeciesRecord(entry.speciesId)?.getFormRecord(form.displayForm)?.getGenders() ?: emptySet()
}
fun getSeenAspects(entry: PokedexEntry): Set<String> {
return getSpeciesRecord(entry.speciesId)?.getAspects() ?: emptySet()
}
fun getKnowledgeForSpecies(speciesId: ResourceLocation): PokedexEntryProgress {
return speciesRecords[speciesId]?.getKnowledge() ?: PokedexEntryProgress.NONE
}
open fun onSpeciesRecordUpdated(speciesDexRecord: SpeciesDexRecord) {
// Save stuff and packet updates
dexCalculatedValues.clear()
globalCalculatedValues.clear()
}
fun <T : Any> getDexCalculatedValue(dex: ResourceLocation, calculatedPokedexValue: PokedexValueCalculator<T>): T {
val existingValue = dexCalculatedValues[dex]?.get(calculatedPokedexValue) as? T
if (existingValue != null) {
return existingValue
} else {
val vals = dexCalculatedValues.getOrPut(dex) { mutableMapOf() }
val newValue = calculatedPokedexValue.calculate(this, Dexes.dexEntryMap[dex]!!.getEntries().associateBy { it.id })
vals[calculatedPokedexValue] = newValue
return newValue
}
}
fun <T : Any> getGlobalCalculatedValue(calculatedPokedexValue: GlobalPokedexValueCalculator<T>): T {
val existingValue = globalCalculatedValues[calculatedPokedexValue] as? T
if (existingValue != null) {
return existingValue
} else {
val newValue = calculatedPokedexValue.calculate(this)
globalCalculatedValues[calculatedPokedexValue] = newValue
return newValue
}
}
open fun markDirty() {
// Save stuff
}
companion object {
const val NUM_CAUGHT_KEY = "cobblemon.pokedex.entries.caught"
const val NUM_SEEN_KEY = "cobblemon.pokedex.entries.seen"
fun getKeyForSpeciesBase(speciesId: ResourceLocation): String {
return "cobblemon.pokedex.${speciesId.path}"
}
fun getKnowledgeKeyForSpecies(speciesId: ResourceLocation): String {
return "${getKeyForSpeciesBase(speciesId)}.knowledge"
}
fun getKnowledgeKeyForForm(speciesId: ResourceLocation, formName: String): String {
return "${getKnowledgeKeyForSpecies(speciesId)}.${formName.lowercase()}"
}
fun getCaptureMethodKeyForSpecies(speciesId: ResourceLocation): String {
return "${getKeyForSpeciesBase(speciesId)}.capturemethod"
}
}
}
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokedex.DexAdditions.DexAddition
import com.cobblemon.mod.common.api.pokedex.def.AggregatePokedexDef
import com.cobblemon.mod.common.api.pokedex.def.SimplePokedexDef
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object DexAdditions : JsonDataRegistry<DexAddition> {
override val id = cobblemonResource("dex_additions")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<DexAdditions>()
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<DexAddition> = TypeToken.get(DexAddition::class.java)
override val resourcePath = "dex_additions"
override fun reload(data: Map<ResourceLocation, DexAddition>) {
data.entries.forEach { (key, value) ->
Dexes.dexEntryMap[value.dexId]?.let { dexDef ->
if (dexDef is SimplePokedexDef) {
var invalid = false
value.entries.forEach {
if (!DexEntries.entries.containsKey(it)) {
invalid = true
Cobblemon.LOGGER.error("Unable to apply dex addition {} as the entry {} does not exist", key, it)
}
}
if (invalid) {
return@forEach
}
dexDef.appendEntries(value.entries)
} else if (dexDef is AggregatePokedexDef) {
var invalid = false
value.entries.forEach {
if (!Dexes.dexEntryMap.containsKey(it)) {
invalid = true
Cobblemon.LOGGER.error("Unable to apply dex addition {} as the sub-dex {} does not exist", key, it)
}
}
dexDef.appendSubDexIds(value.entries)
}
} ?: Cobblemon.LOGGER.error("Unable to apply dex addition {} as the dex {} does not exist", key, value.dexId)
}
observable.emit(this)
}
override fun sync(player: ServerPlayer) {} // Will be synced as part of the dexes
class DexAddition {
val dexId: ResourceLocation = ResourceLocation.parse("cobblemon:national")
/** Could be PokedexEntry locations or PokedexDef locations depending on what type dex the dexId points to. */
val entries = mutableListOf<ResourceLocation>()
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokedexDexSyncPacket
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object Dexes : JsonDataRegistry<PokedexDef> {
override val id = cobblemonResource("dexes")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<Dexes>()
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokedexDef::class.java, CodecBackedAdapter(PokedexDef.CODEC))
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<PokedexDef> = TypeToken.get(PokedexDef::class.java)
override val resourcePath = "dexes"
//Maps a dex id to its PokedexDef
val dexEntryMap = linkedMapOf<ResourceLocation, PokedexDef>()
override fun reload(data: Map<ResourceLocation, PokedexDef>) {
dexEntryMap.clear()
data.entries.sortedBy { it.value.sortOrder }.forEach { (id, def) -> dexEntryMap[id] = def }
observable.emit(this)
}
override fun sync(player: ServerPlayer) {
PokedexDexSyncPacket(dexEntryMap.values)
}
}
@@ -0,0 +1,133 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.readEnumConstant
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeEnumConstant
import com.cobblemon.mod.common.util.writeString
import com.google.common.collect.Sets
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* A record of a form in the Pokédex. There are a number of tracked properties regarding the form and what
* the dex has seen, and adding to it can be done through [encountered] and [caught].
*
* @author Hiroku
* @since August 23rd, 2024
*/
class FormDexRecord {
companion object {
val CODEC: Codec<FormDexRecord> = RecordCodecBuilder.create { instance ->
instance.group(
ListCodec(Codec.STRING, 0, 3).fieldOf("genders").forGetter { it.genders.map { it.name } },
ListCodec(Codec.STRING, 0, 2).fieldOf("seenShinyStates").forGetter { it.seenShinyStates.toList() },
Codec.STRING.fieldOf("knowledge").forGetter { it.knowledge.name }
).apply(instance) { genders, seenShinyStates, knowledge ->
FormDexRecord().also {
it.genders.addAll(genders.map(Gender::valueOf))
it.seenShinyStates.addAll(seenShinyStates)
it.knowledge = PokedexEntryProgress.valueOf(knowledge)
}
}
}
}
/** The genders that the dex is aware of. */
private val genders: MutableSet<Gender> = mutableSetOf()
/** Shiny states could be shiny or non-shiny - on the off chance they only saw the shiny, they shouldn't know what the normal looks like. */
private val seenShinyStates = mutableSetOf<String>() // consider: radiants in the future (radiants should just be a resource pack tbh)
/** The current awareness of the form that the dex has. */
var knowledge = PokedexEntryProgress.NONE
private set
private val data = VariableStruct() // Could use this for various other properties maybe, consider this a draft
@Transient
lateinit var speciesDexRecord: SpeciesDexRecord
@Transient
lateinit var struct: QueryStruct
fun initialize(speciesDexRecord: SpeciesDexRecord) {
this.speciesDexRecord = speciesDexRecord
struct = QueryStruct(hashMapOf())
.addFunction("data") { data }
.addFunction("knowledge") { StringValue(knowledge.name) }
.addFunction("has_seen_gender") { params -> genders.contains(Gender.valueOf(params.getString(0).uppercase())) }
}
fun encountered(pokemon: Pokemon) {
if (wouldBeDifferent(pokemon, PokedexEntryProgress.ENCOUNTERED)) {
addInformation(pokemon, PokedexEntryProgress.ENCOUNTERED)
}
}
fun caught(pokemon: Pokemon) {
if (wouldBeDifferent(pokemon, PokedexEntryProgress.CAUGHT)) {
addInformation(pokemon, PokedexEntryProgress.CAUGHT)
}
}
fun getGenders(): Set<Gender> = genders
fun hasSeenShinyState(shiny: Boolean): Boolean = seenShinyStates.contains(if (shiny) "shiny" else "normal")
fun getSeenShinyStates(): Set<String> = seenShinyStates
//Used when granting all entries in dex, should figure out better way
fun addAllShinyStatesAndGenders() {
genders.addAll(listOf(Gender.MALE, Gender.FEMALE))
seenShinyStates.addAll(listOf("shiny", "normal"))
}
fun setKnowledgeProgress(newKnowledge: PokedexEntryProgress) {
knowledge = newKnowledge
speciesDexRecord.onFormRecordUpdated(this)
}
private fun addInformation(pokemon: Pokemon, knowledge: PokedexEntryProgress) {
genders.add(pokemon.gender)
seenShinyStates.add(if (pokemon.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokemon, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
}
/** Returns whether the given [Pokemon] and [knowledge] would add new information to the Pokédex.*/
fun wouldBeDifferent(pokemon: Pokemon, knowledge: PokedexEntryProgress): Boolean {
return pokemon.gender !in genders
|| (pokemon.shiny && "shiny" !in seenShinyStates)
|| (!pokemon.shiny && "normal" !in seenShinyStates)
|| knowledge.ordinal > this.knowledge.ordinal
|| speciesDexRecord.wouldBeDifferent(pokemon)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(genders) { _, it -> buffer.writeEnumConstant(it) }
buffer.writeCollection(seenShinyStates) { _, it -> buffer.writeString(it) }
buffer.writeEnumConstant(knowledge)
}
fun decode(buffer: RegistryFriendlyByteBuf) {
genders.clear()
seenShinyStates.clear()
genders.addAll(buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readEnumConstant(Gender::class.java) })
seenShinyStates.addAll(buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() })
knowledge = buffer.readEnumConstant(PokedexEntryProgress::class.java)
}
}
@@ -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.pokedex
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.StringRepresentable
/**
* Contains stats about a specific pokemon for putting in the pokedex
*
* @author JPAK, Apion
* @since February 21, 2024
*/
enum class PokedexEntryProgress : StringRepresentable, Comparable<PokedexEntryProgress> {
NONE,
ENCOUNTERED,
CAUGHT;
override fun getSerializedName() = this.name
companion object {
val CODEC: Codec<PokedexEntryProgress> = RecordCodecBuilder.create { instance ->
instance.map ({
PokedexEntryProgress.valueOf(it)
}, PrimitiveCodec.STRING.fieldOf("name").forGetter { it.name })
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.getPlayer
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.UUID
import net.minecraft.resources.ResourceLocation
class PokedexManager(
override val uuid: UUID,
override val speciesRecords: MutableMap<ResourceLocation, SpeciesDexRecord>
) : AbstractPokedexManager(), InstancedPlayerData {
fun encounter(pokemon: Pokemon) {
val speciesId = pokemon.species.resourceIdentifier
val formName = pokemon.form.name
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).encountered(pokemon)
}
fun catch(pokemon: Pokemon) {
val speciesId = pokemon.species.resourceIdentifier
val formName = pokemon.form.name
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).caught(pokemon)
}
override fun markDirty() {
}
override fun initialize() {
speciesRecords.entries.forEach { (key, value) -> value.initialize(this, key) }
}
override fun onSpeciesRecordUpdated(speciesDexRecord: SpeciesDexRecord) {
uuid.getPlayer()?.sendPacket(
SetClientPlayerDataPacket(
type = PlayerInstancedDataStoreType.POKEDEX,
playerData = ClientPokedexManager(mutableMapOf(speciesDexRecord.id to speciesDexRecord)),
isIncremental = true
)
)
}
companion object {
val CODEC = RecordCodecBuilder.create<PokedexManager> { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("uuid").forGetter { it.uuid.toString() },
Codec.unboundedMap(ResourceLocation.CODEC, SpeciesDexRecord.CODEC).fieldOf("speciesRecords").forGetter { it.speciesRecords }
).apply(instance) { uuid, map ->
//Codec stuff seems to deserialize to an immutable map, so we have to convert it to mutable explicitly
PokedexManager(UUID.fromString(uuid), map.toMutableMap()).also { it.initialize() }
}
}
}
override fun toClientData() = ClientPokedexManager(speciesRecords)
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import net.minecraft.resources.ResourceLocation
interface PokedexValueCalculator<T> {
fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): T
}
interface GlobalPokedexValueCalculator<T> {
fun calculate(dexManager: AbstractPokedexManager): T
}
object CaughtCount : PokedexValueCalculator<Int> {
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Int {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }
}
}
object SeenCount : PokedexValueCalculator<Int> {
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Int {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }
}
}
@@ -0,0 +1,145 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import com.google.common.collect.Sets
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* Dex recorded information about a particular Pokémon species. This includes all forms and their information.
* Across all forms there is a single list of known aspects which is generally used to track cosmetic variation
* though technically things like gender and form-motivating aspects will get collected inadvertently.
*
* @author Hiroku
* @since August 23rd, 2024
*/
class SpeciesDexRecord {
companion object {
val CODEC: Codec<SpeciesDexRecord> = RecordCodecBuilder.create { instance ->
instance.group(
ListCodec(PrimitiveCodec.STRING, 0, 512).fieldOf("aspects").forGetter { it.aspects.toList() },
Codec.unboundedMap(Codec.STRING, FormDexRecord.CODEC).fieldOf("formRecords").forGetter { it.formRecords }
).apply(instance) { aspects, formRecords ->
SpeciesDexRecord().also {
it.aspects.addAll(aspects)
it.formRecords.putAll(formRecords)
}
}
}
}
@Transient
lateinit var id: ResourceLocation
private val aspects: MutableSet<String> = mutableSetOf()
private val formRecords: MutableMap<String, FormDexRecord> = mutableMapOf()
@Transient
val struct = QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("get_form_record") { params ->
val formName = params.getString(0)
formRecords[formName]?.struct ?: QueryStruct(hashMapOf())
}
.addFunction("get_or_create_form_record") { params ->
getOrCreateFormRecord(params.getString(0)).struct
}
.addFunction("add_aspect") { params ->
val aspect = params.getString(0)
aspects.add(aspect)
}
.addFunction("has_knowledge") { params ->
val knowledge = params.getString(0)
DoubleValue(formRecords.values.any { it.knowledge.name.equals(knowledge.toString(), ignoreCase = true) })
}
.addFunction("is_caught") { params ->
DoubleValue(formRecords.values.any { it.knowledge.name.equals("CAUGHT", ignoreCase = true) })
}
// more, also worth moving to something like MoLangFunctions.kt so it's easier to API in more values
@Transient
lateinit var pokedexManager: AbstractPokedexManager
fun initialize(pokedexManager: AbstractPokedexManager, id: ResourceLocation) {
this.id = id
this.pokedexManager = pokedexManager
this.formRecords.forEach { it.value.initialize(this) }
}
fun onFormRecordUpdated(formDexRecord: FormDexRecord) {
pokedexManager.onSpeciesRecordUpdated(this)
}
fun addInformation(pokemon: Pokemon, knowledge: PokedexEntryProgress) {
aspects.addAll(pokemon.aspects)
}
fun addAspects(addedAspects: Set<String>) {
aspects.addAll(addedAspects)
}
/** Returns true if the given Pokémon contains new information. Internal because it's only to be called from [FormDexRecord.wouldBeDifferent]. */
internal fun wouldBeDifferent(pokemon: Pokemon) = pokemon.aspects.any { it !in aspects }
fun getOrCreateFormRecord(formName: String): FormDexRecord {
return formRecords.getOrPut(formName.lowercase()) {
val record = FormDexRecord()
record.initialize(this)
onFormRecordUpdated(record)
// Some more stuff eventually
record
}
}
fun getFormRecord(formName: String): FormDexRecord? {
return formRecords[formName.lowercase()]
}
fun deleteFormRecord(formName: String) {
formRecords.remove(formName)
}
fun getAspects(): Set<String> = this.aspects
fun hasAspect(aspect: String) = aspect in aspects
fun getKnowledge() = formRecords.values.maxOfOrNull { it.knowledge } ?: PokedexEntryProgress.NONE
fun hasAtLeast(knowledge: PokedexEntryProgress) = getKnowledge().ordinal >= knowledge.ordinal
fun hasSeenForm(formName: String) = formRecords.entries.any { it.key.equals(formName, ignoreCase = true) && it.value.knowledge != PokedexEntryProgress.NONE }
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(aspects) { _, it -> buffer.writeString(it) }
buffer.writeInt(formRecords.size)
for ((formName, formRecord) in formRecords) {
buffer.writeString(formName)
formRecord.encode(buffer)
}
}
fun decode(buffer: RegistryFriendlyByteBuf) {
aspects.clear()
aspects.addAll(buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() })
formRecords.clear()
val numForms = buffer.readInt()
for (i in 0 until numForms) {
val formName = buffer.readString()
val formRecord = FormDexRecord()
formRecord.decode(buffer)
formRecords[formName] = formRecord
}
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.def
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.common.collect.Lists
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import io.netty.buffer.ByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
class AggregatePokedexDef(
override val id: ResourceLocation,
) : PokedexDef() {
override val typeId = ID
private val subDexIds = mutableListOf<ResourceLocation>()
var squash = true
private set
fun appendSubDexIds(subDexIds: List<ResourceLocation>) {
this.subDexIds.addAll(subDexIds)
}
override fun getEntries(): List<PokedexEntry> {
if (!squash) {
return Dexes.dexEntryMap.values.filter { it.id in subDexIds }.flatMap { it.getEntries() }
} else {
val speciesToEntry = linkedMapOf<ResourceLocation, PokedexEntry>()
subDexIds.forEach { dexId ->
Dexes.dexEntryMap[dexId]?.getEntries()?.forEach { entry ->
val preExisting = speciesToEntry[entry.speciesId]
if (preExisting != null) {
speciesToEntry[preExisting.speciesId] = preExisting.combinedWith(entry)
} else {
speciesToEntry[entry.speciesId] = entry
}
}
}
return speciesToEntry.values.toList()
}
}
override fun shouldSynchronize(other: PokedexDef) = true
override fun decode(buffer: RegistryFriendlyByteBuf) {
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
val size = buffer.readInt()
for (i in 0 until size) {
subDexIds.add(buffer.readIdentifier())
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
buffer.writeInt(subDexIds.size)
subDexIds.forEach {
buffer.writeIdentifier(it)
}
}
companion object {
val ID = cobblemonResource("aggregate_pokedex_def")
val CODEC: MapCodec<AggregatePokedexDef> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("id").forGetter { it.id },
PrimitiveCodec.INT.fieldOf("sortOrder").forGetter { it.sortOrder },
ResourceLocation.CODEC.listOf().fieldOf("subDexIds").forGetter { it.subDexIds },
PrimitiveCodec.BOOL.fieldOf("squash").forGetter { it.squash }
).apply(instance) { id, sortOrder, entries, squash ->
val result = AggregatePokedexDef(id)
result.sortOrder = sortOrder
result.subDexIds.addAll(entries)
result.squash = squash
result
}
}
val PACKET_CODEC: StreamCodec<ByteBuf, AggregatePokedexDef> = StreamCodec.composite(
ResourceLocation.STREAM_CODEC, AggregatePokedexDef::id,
ByteBufCodecs.INT, AggregatePokedexDef::sortOrder,
ByteBufCodecs.collection(Lists::newArrayListWithCapacity, ResourceLocation.STREAM_CODEC), AggregatePokedexDef::subDexIds
) { id, sortOrder, entries ->
val result = AggregatePokedexDef(id)
result.sortOrder = sortOrder
//result.subDexIds.addAll(entries)
result
}
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.def
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import io.netty.buffer.ByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
/**
* A class that defines what entries are in a Pokedex
*
* @since August 24, 2024
* @author Apion
*/
abstract class PokedexDef: ClientDataSynchronizer<PokedexDef> {
//The ID used to find the codecs for this "type" of PokedexDef
abstract val typeId: ResourceLocation
//The ID of this dex in the Dexes registry
abstract val id: ResourceLocation
// The sort order for the dex
var sortOrder: Int = 0
abstract fun getEntries(): List<PokedexEntry>
companion object {
val CODEC: Codec<PokedexDef> = ResourceLocation.CODEC.dispatch("type", PokedexDef::typeId, PokedexDef::getCodecById)
val PACKET_CODEC: StreamCodec<ByteBuf, PokedexDef> = ResourceLocation.STREAM_CODEC.dispatch({ it.typeId }, ::getPacketCodecById)
fun getPacketCodecById(id: ResourceLocation): StreamCodec<ByteBuf, out PokedexDef> {
return when (id) {
SimplePokedexDef.ID -> SimplePokedexDef.PACKET_CODEC
AggregatePokedexDef.ID -> AggregatePokedexDef.PACKET_CODEC
else -> throw NotImplementedError("Missing pokedex def packet codec for id: $id")
}
}
fun getCodecById(id: ResourceLocation): MapCodec<out PokedexDef> {
return when (id) {
SimplePokedexDef.ID -> SimplePokedexDef.CODEC
AggregatePokedexDef.ID -> AggregatePokedexDef.CODEC
else -> throw NotImplementedError("Missing pokedex def codec for id: $id")
}
}
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.def
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.common.collect.Lists
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import io.netty.buffer.ByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
/**
* A [PokedexDef] that is just a list of [PokedexEntry]s
*
* @since August 24, 2024
* @author Apion
*/
class SimplePokedexDef(
override val id: ResourceLocation
) : PokedexDef() {
override val typeId = ID
private val entries = mutableListOf<ResourceLocation>()
fun appendEntries(entries: List<ResourceLocation>) {
this.entries.addAll(entries)
}
override fun getEntries() = entries.mapNotNull { DexEntries.entries[it] }
override fun shouldSynchronize(other: PokedexDef) = true
override fun decode(buffer: RegistryFriendlyByteBuf) {
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
val size = buffer.readInt()
for (i in 0 until size) {
entries.add(buffer.readIdentifier())
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
buffer.writeInt(entries.size)
entries.forEach {
buffer.writeIdentifier(it)
}
}
companion object {
val ID = cobblemonResource("simple_pokedex_def")
val CODEC: MapCodec<SimplePokedexDef> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("id").forGetter { it.id },
PrimitiveCodec.INT.fieldOf("sortOrder").forGetter { it.sortOrder },
ResourceLocation.CODEC.listOf().fieldOf("entries").forGetter { it.entries }
).apply(instance) { id, sortOrder, entries ->
val result = SimplePokedexDef(id)
result.sortOrder = sortOrder
result.entries.addAll(entries)
result
}
}
val PACKET_CODEC: StreamCodec<ByteBuf, SimplePokedexDef> = StreamCodec.composite(
ResourceLocation.STREAM_CODEC, SimplePokedexDef::id,
ByteBufCodecs.INT, SimplePokedexDef::sortOrder,
ByteBufCodecs.collection(Lists::newArrayListWithCapacity, ResourceLocation.STREAM_CODEC), SimplePokedexDef::entries
) { id, sortOrder, entries ->
val result = SimplePokedexDef(id)
result.sortOrder = sortOrder
result.entries.addAll(entries)
result
}
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.DexEntrySyncPacket
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object DexEntries : JsonDataRegistry<PokedexEntry> {
override val id = cobblemonResource("dex_entries")
override val type = PackType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<PokedexEntry> = TypeToken.get(PokedexEntry::class.java)
override val resourcePath = "dex_entries"
val entries = mutableMapOf<ResourceLocation, PokedexEntry>()
override fun reload(data: Map<ResourceLocation, PokedexEntry>) {
data.forEach { _, entry ->
entries[entry.id] = entry
if (entry.forms.isEmpty()) {
entry.forms.add(PokedexForm())
}
entry.forms.forEach {
if (it.unlockForms.isEmpty()) {
it.unlockForms = mutableSetOf(it.displayForm)
}
}
}
observable.emit(this)
}
override val observable = SimpleObservable<DexEntries>()
override fun sync(player: ServerPlayer) {
DexEntrySyncPacket(entries.values)
}
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object DexEntryAdditions : JsonDataRegistry<DexEntryAdditions.DexEntryAddition> {
override val id = cobblemonResource("dex_entry_additions")
override val type = PackType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<DexEntryAddition> = TypeToken.get(DexEntryAddition::class.java)
override val resourcePath = "dex_entry_additions"
override val observable = SimpleObservable<DexEntryAdditions>()
val entries = mutableListOf<PokedexEntry>()
override fun reload(data: Map<ResourceLocation, DexEntryAddition>) {
data.entries.forEach { (key, addition) ->
DexEntries.entries[addition.entryId]?.add(addition)
?: return@forEach Cobblemon.LOGGER.error("Unable to find dex entry {} to add to from dex entry addition {}", addition.entryId, key) // Skip if the entry doesn't exist
}
observable.emit(this)
}
override fun sync(player: ServerPlayer) {} // It'd be synced as part of the DexEntries
class DexEntryAddition {
val entryId: ResourceLocation = cobblemonResource("some_addition")
val forms: List<PokedexForm> = emptyList()
val variations: List<PokedexCosmeticVariation> = emptyList()
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* Controls a toggle in the Pokédex GUI that iterates through some set of aspects that
* presumably match a species feature or other aspect provider on the server side. This is
* used to let the player check out the different variations of a Pokémon.
*
* @author Hiroku
* @since August 25th, 2025
*/
class PokedexCosmeticVariation {
var displayName: String = "cobblemon.pokedex.variation.cosmetic"
var icon: ResourceLocation = cobblemonResource("textures/gui/pokedex/variation/cosmetic.png")
var aspects: Set<String> = emptySet()
fun clone() = PokedexCosmeticVariation().also {
it.displayName = displayName
it.icon = icon
it.aspects = aspects.toSet()
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(displayName)
buffer.writeResourceLocation(icon)
buffer.writeCollection(aspects) { _, aspect -> buffer.writeString(aspect) }
}
fun decode(buffer: RegistryFriendlyByteBuf) {
displayName = buffer.readString()
icon = buffer.readResourceLocation()
aspects = buffer.readList { buffer.readString() }.toSet()
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class PokedexEntry(
val id: ResourceLocation,
val speciesId: ResourceLocation,
val displayAspects: MutableSet<String> = mutableSetOf(),
val conditionAspects: MutableSet<String> = mutableSetOf(),
val forms: MutableList<PokedexForm> = mutableListOf(),
val variations: MutableList<PokedexCosmeticVariation> = mutableListOf()
) {
fun clone() = PokedexEntry(
id,
speciesId,
displayAspects,
conditionAspects,
forms.map { it.clone() }.toMutableList(),
variations.map { it.clone() }.toMutableList()
)
fun combinedWith(other: PokedexEntry): PokedexEntry {
val copy = clone()
other.forms.forEach { form ->
if (form.displayForm !in copy.forms.map { it.displayForm }) {
copy.forms.add(form.clone())
} else {
copy.forms.find { it.displayForm == form.displayForm }!!.unlockForms.addAll(form.unlockForms)
}
}
other.variations.forEach { variation ->
if (variation.displayName !in copy.variations.map { it.displayName }) {
copy.variations.add(variation.clone())
}
}
return copy
}
fun add(addition: DexEntryAdditions.DexEntryAddition) {
addition.forms.forEach { form ->
if (form.displayForm !in forms.map { it.displayForm }) {
forms.add(form)
} else {
forms.find { it.displayForm == form.displayForm }!!.unlockForms.addAll(form.unlockForms)
}
}
addition.variations.forEach { variation ->
if (variation.displayName !in variations.map { it.displayName }) {
variations.add(variation)
}
}
}
fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeIdentifier(id)
buf.writeIdentifier(speciesId)
buf.writeCollection(displayAspects) { _, aspect -> buf.writeString(aspect) }
buf.writeCollection(conditionAspects) { _, aspect -> buf.writeString(aspect) }
buf.writeCollection(forms) { _, form ->
buf.writeString(form.displayForm)
buf.writeCollection(form.unlockForms) { _, unlockForm -> buf.writeString(unlockForm) }
}
buf.writeCollection(variations) { _, variation -> variation.encode(buf) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): PokedexEntry {
val id = buffer.readIdentifier()
val entryId = buffer.readIdentifier()
val displayAspects = buffer.readList { buffer.readString() }.toMutableSet()
val conditionAspects = buffer.readList { buffer.readString() }.toMutableSet()
val forms = buffer.readList {
val form = PokedexForm()
form.displayForm = buffer.readString()
form.unlockForms.addAll(buffer.readList { buffer.readString() })
form
}
val variations = buffer.readList { PokedexCosmeticVariation().also { it.decode(buffer) } }
return PokedexEntry(id, entryId, displayAspects, conditionAspects, forms, variations)
}
}
}
class PokedexForm {
var displayForm: String = "Normal"
var unlockForms: MutableSet<String> = mutableSetOf()
fun clone() = PokedexForm().also {
it.displayForm = displayForm
it.unlockForms.addAll(unlockForms)
}
}
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokedex.filter
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
abstract class EntryFilter {
abstract fun test(entry: PokedexEntry): Boolean
}
@@ -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.pokedex.filter
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
/**
* A Pokedex [EntryFilter] that filters out entries that do not contain the current search.
*
* @author whatsy
* @since September 4th, 2024
* @param searchString The string to use when checking.
*/
class SearchFilter(val searchString: String) : EntryFilter() {
override fun test(entry: PokedexEntry): Boolean {
if (searchString == "") return true
val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
return species.translatedName.string.contains(searchString.trim(), true)
}
}
@@ -164,6 +164,20 @@ object PokemonSpecies : JsonDataRegistry<Species> {
*/
fun count() = this.speciesByIdentifier.size
/**
* Gets a map of dex numbers to species.
*
* @return The dex numbers map to species.
*/
fun getSpeciesInNamespace(namespace: String = Cobblemon.MODID): MutableMap<Int, Species> = speciesByDex.row(namespace)
/**
* Get a list of loaded namespaces.
*
* @return The list of loaded namespaces.
*/
fun getNamespaces() = speciesByDex.rowKeySet().toList()
/**
* Picks a random [Species].
*
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.Cobblemon.playerData
import com.cobblemon.mod.common.Cobblemon.playerDataManager
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
@@ -20,18 +20,17 @@ import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
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.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.ItemStack
/**
@@ -122,7 +121,7 @@ interface Evolution : EvolutionLike {
if (pokeballStack == null) return false
}
// If the player has at least one empty spot in their party.
val emptySlot = owner.party().getFirstAvailablePosition() ?: return false
owner.party().getFirstAvailablePosition() ?: return false
// Add shed Pokemon to player's party
val shedPokemon = pokemon.clone()
@@ -130,7 +129,7 @@ interface Evolution : EvolutionLike {
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(shedPokemon)
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerData.get(owner).advancementData.totalEvolvedCount))
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerDataManager.getGenericData(owner).advancementData.totalEvolvedCount))
// Consume one of the balls
pokeballStack?.shrink(1)
@@ -145,7 +144,7 @@ interface Evolution : EvolutionLike {
*/
fun forceEvolve(pokemon: Pokemon) {
// This is a switch to enable/disable the evolution effect while we get particles improved
val useEvolutionEffect = false
val useEvolutionEffect = true
if (pokemon.state is ShoulderedState) {
pokemon.tryRecallWithAnimation()
@@ -155,34 +154,31 @@ interface Evolution : EvolutionLike {
if (pokemonEntity == null || !useEvolutionEffect) {
evolutionMethod(pokemon)
} else {
pokemonEntity.evolutionEntity = pokemon.getOwnerPlayer()?.let { GenericBedrockEntity(world = it.level()) }
val evolutionEntity = pokemon.entity!!.evolutionEntity
evolutionEntity?.apply {
category = cobblemonResource("evolution")
colliderHeight = pokemonEntity.bbHeight
colliderWidth = pokemonEntity.bbWidth
scale = pokemonEntity.ageScale
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
setPos(pokemonEntity.x, pokemonEntity.y, pokemonEntity.z)
pokemonEntity.busyLocks.add("evolving")
pokemonEntity.navigation.stop()
pokemonEntity.after(1F) {
evolutionAnimation(pokemonEntity)
}
pokemon.getOwnerPlayer()?.level()?.addFreshEntity(evolutionEntity)
afterOnServer(seconds = 9F) {
if (!pokemonEntity.isRemoved) {
evolutionMethod(pokemon)
afterOnServer(seconds = 1.5F) { pokemonEntity.cry() }
afterOnServer(seconds = 3F) {
if (evolutionEntity != null) {
evolutionEntity.kill()
if (!pokemonEntity.isRemoved) {
pokemonEntity.evolutionEntity = null
}
}
}
}
pokemonEntity.after(11.2F) {
evolutionMethod(pokemon)
}
pokemonEntity.after( seconds = 12F ) {
cryAnimation(pokemonEntity)
pokemonEntity.busyLocks.remove("evolving")
}
}
}
private fun evolutionAnimation(pokemon: Entity) {
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("q.bedrock_stateful('evolution', 'evolution', 'endures_primary_animations');"), emptySet())
playPoseableAnimationPacket.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 128.0, pokemon.level().dimension())
}
private fun cryAnimation(pokemon: Entity) {
val playPoseableAnimationPacket = PlayPosableAnimationPacket(pokemon.id, setOf("cry"), emptySet())
playPoseableAnimationPacket.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 128.0, pokemon.level().dimension())
}
fun evolutionMethod(pokemon: Pokemon) {
this.result.apply(pokemon)
this.learnableMoves.forEach { move ->
@@ -194,8 +190,9 @@ interface Evolution : EvolutionLike {
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, 1F, 1F)
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLUTION_UI, 1F, 1F)
this.shed(pokemon)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
}
@@ -0,0 +1,36 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.CollectionSubscription
interface MutableObservableCollection<T, C : Collection<T>> :
ObservableCollection<T, C>,
MutableCollection<T> {
fun subscribe(
priority: Priority = Priority.NORMAL,
anyChangeHandler: (Pair<C, T>) -> Unit,
additionHandler: ((Pair<C, T>) -> Unit)? = null,
removalHandler: ((Pair<C, T>) -> Unit)? = null,
): CollectionSubscription<T, C>
override fun add(element: T): Boolean
fun addIf(element: T, predicate: (C) -> Boolean): Boolean
override fun remove(element: T): Boolean
fun removeIf(predicate: (T) -> Boolean): Boolean
override fun addAll(elements: Collection<T>): Boolean
override fun retainAll(elements: Collection<T>): Boolean
override fun removeAll(elements: Collection<T>): Boolean
override fun iterator(): MutableIterator<T>
override fun clear()
fun mutableCopy(): MutableObservableCollection<T, C>
}
@@ -0,0 +1,8 @@
package com.cobblemon.mod.common.api.reactive.collections
interface MutableObservableCollectionIterator<T, C : Collection<T>, I : Iterator<T>> :
ObservableCollectionIterator<T, I>,
MutableIterator<T> {
override fun remove()
}
@@ -0,0 +1,32 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
interface ObservableCollection<T, C : Collection<T>> :
Collection<T>,
Observable<Pair<C, T>> {
val elements: C
override val size: Int
override fun subscribe(
priority: Priority,
handler: (Pair<C, T>) -> Unit,
): ObservableSubscription<Pair<C, T>>
override fun unsubscribe(subscription: ObservableSubscription<Pair<C, T>>)
override fun isEmpty(): Boolean
fun isNotEmpty(): Boolean
override fun contains(element: T): Boolean
override fun containsAll(elements: Collection<T>): Boolean
override operator fun iterator(): Iterator<T>
fun copy(): ObservableCollection<T, C>
}
@@ -0,0 +1,7 @@
package com.cobblemon.mod.common.api.reactive.collections
interface ObservableCollectionIterator<T, I : Iterator<T>> : Iterator<T> {
override fun hasNext(): Boolean
override fun next(): T
}
@@ -0,0 +1,35 @@
package com.cobblemon.mod.common.api.reactive.collections
import com.cobblemon.mod.common.api.reactive.Observable
/**
* This attempts to cast [this] value to [Observable].
*
* @return An observable set with [this] as an [Observable] or an empty set.
*/
fun <T> T.getElementObservables(): Set<Observable<*>> {
this.tryGetObservable()?.let { return setOf(it) }
return emptySet()
}
/**
* This attempts to cast [this] value to [Observable].
*
* @return This value as an [Observable] or null.
*/
fun <T> T.tryGetObservable(): Observable<*>? = if (this is Observable<*>) this else null
fun <T> MutableIterator<T>.removeIf(predicate: (T) -> Boolean): Boolean {
var removed = false
this.forEach { element ->
if (predicate(element)) {
this.remove()
if (!removed) {
removed = true
}
}
}
return removed
}
@@ -0,0 +1,242 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.IndexedListSubscription
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.ListSubscription
import com.cobblemon.mod.common.api.reactive.collections.removeIf
open class MutableObservableList<T>(
list: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) : MutableObservableCollection<T, List<T>>,
ObservableList<T>(list, elementHandler),
MutableList<T> {
private val additionObservable = SimpleObservable<Pair<List<T>, T>>()
private val removalObservable = SimpleObservable<Pair<List<T>, T>>()
private val setObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
private val swapObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
private val additionObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
private val removalObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
private val setObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Int>>()
private val swapObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Pair<Int, Int>>>()
override fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
}
fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
@Suppress("DuplicatedCode")
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
set = setHandler?.let { setObservable.subscribe(priority, it) },
swap = swapHandler?.let { swapObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler, setHandler, swapHandler)
}
fun subscribeIndexed(
priority: Priority,
additionHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
removalHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
setHandler: ((Triple<List<T>, Pair<T, T>, Int>) -> Unit)?,
swapHandler: ((Triple<List<T>, Pair<T, T>, Pair<Int, Int>>) -> Unit)?,
): IndexedListSubscription<T> {
return IndexedListSubscription(
addition = additionHandler?.let { additionObservableIndexed.subscribe(priority, it) },
removal = removalHandler?.let { removalObservableIndexed.subscribe(priority, it) },
set = setHandler?.let { setObservableIndexed.subscribe(priority, it) },
swap = swapHandler?.let { swapObservableIndexed.subscribe(priority, it) },
)
}
/**
* @return `true` after handling [register] & all subscriptions to [additionObservable],
* [additionObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitAddition(element: T, index: Int): Boolean {
register(element)
additionObservable.emit(list to element)
additionObservableIndexed.emit(Triple(list, element, index))
return emitAnyChange(element)
}
/**
* @return `true` after handling [unregister] & all subscriptions to [removalObservable],
* [removalObservableIndexed] & [ObservableList.emitAnyChange].
*/
private fun emitRemoval(element: T, index: Int): Boolean {
unregister(element)
removalObservable.emit(list to element)
removalObservableIndexed.emit(Triple(list, element, index))
return emitAnyChange(element)
}
/**
* @return `true` after handling [register], [unregister] & all subscriptions to [setObservable],
* [setObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitSet(elements: Pair<T, T>, index: Int): Boolean {
register(elements.first)
unregister(elements.second)
setObservable.emit(list to elements)
setObservableIndexed.emit(Triple(list, elements, index))
emitAnyChange(elements.first)
emitAnyChange(elements.second)
return true
}
/**
* @return `true` after handling all subscriptions to [swapObservable],
* [swapObservableIndexed], & [ObservableList.emitAnyChange].
*/
private fun emitSwap(elements: Pair<T, T>, indices: Pair<Int, Int>): Boolean {
swapObservable.emit(list to elements)
swapObservableIndexed.emit(Triple(list, elements, indices))
emitAnyChange(elements.first)
emitAnyChange(elements.second)
return true
}
override operator fun iterator() = MutableObservableListIterator(this)
override fun listIterator() = MutableObservableListIterator(this)
override fun listIterator(index: Int) = MutableObservableListIterator(this, index)
override fun subList(fromIndex: Int, toIndex: Int): MutableObservableSubList<T> {
return MutableObservableSubList(this, fromIndex, toIndex, elementHandler)
}
override fun addIf(element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(list)) add(element) else false
}
override fun add(element: T): Boolean {
list.add(element)
return emitAddition(element, lastIndex)
}
fun addAtIf(index: Int, element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(list)) {
add(index, element)
true
} else {
false
}
}
override fun add(index: Int, element: T) {
list.add(index, element)
emitAddition(element, index)
}
fun setIf(index: Int, element: T, predicate: (T) -> Boolean): T? {
return if (predicate(list[index])) set(index, element) else null
}
override operator fun set(index: Int, element: T): T {
return list.set(index, element).also { emitSet(element to it, index) }
}
fun swapIf(indexOne: Int, indexTwo: Int, predicate: () -> Boolean): Boolean {
return predicate().also { if (it) swap(indexOne, indexTwo) }
}
fun swap(indexOne: Int, indexTwo: Int) {
val one = list[indexOne]
val two = list.set(indexTwo, one)
list[indexOne] = two
emitSwap(one to two, indexOne to indexTwo)
}
override fun removeIf(predicate: (T) -> Boolean): Boolean = iterator().removeIf(predicate)
override fun remove(element: T): Boolean {
val indexOf = list.indexOfFirst { it == element }
if (indexOf == -1) {
return false
}
list.removeAt(indexOf)
return emitRemoval(element, indexOf)
}
override fun removeAt(index: Int): T {
val element = list.removeAt(index)
emitRemoval(element, index)
return element
}
fun removeAtIf(index: Int, predicate: (T) -> Boolean): T? {
val element = list[index]
if (predicate(element)) {
list.removeAt(index)
emitRemoval(element, index)
}
return element
}
override fun addAll(elements: Collection<T>): Boolean {
elements.forEach { add(it) }
return true
}
override fun addAll(index: Int, elements: Collection<T>): Boolean {
var currentIndex = index
elements.forEach { add(currentIndex++, it) }
return true
}
override fun removeAll(elements: Collection<T>):Boolean = removeIf { elements.contains(it) }
override fun retainAll(elements: Collection<T>):Boolean = removeIf { !elements.contains(it) }
override fun clear() {
while (isNotEmpty()) {
removeAt(0)
}
}
}
@@ -0,0 +1,60 @@
package com.cobblemon.mod.common.api.reactive.collections.list
// the name is not quite long enough yet...
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
open class MutableObservableListIterator<T>(
observableList: MutableObservableList<T>,
index: Int = 0,
) : MutableObservableCollectionIterator<T, Set<T>, ListIterator<T>>,
ObservableListIterator<T>(observableList, index),
MutableListIterator<T> {
private val mutableList: MutableObservableList<T> = observableList
private var illegalSetOrRemoveState: Boolean? = null
set (value) { if (field != value) field = value }
override fun next(): T {
illegalSetOrRemoveState = false
return super.next()
}
override fun previous(): T {
illegalSetOrRemoveState = false
return super.previous()
}
override fun remove() {
throwIfIllegalState("remove")
mutableList.removeAt(--index)
illegalSetOrRemoveState = true
}
override fun set(element: T) {
throwIfIllegalState("set")
mutableList[index -1] = element
}
override fun add(element: T) {
when {
mutableList.isEmpty() -> mutableList.add(element).run { index = 0 }
index < mutableList.size -> mutableList.add(index++, element)
else -> mutableList.add(element).run { index++ }
}
illegalSetOrRemoveState = true
}
private fun throwIfIllegalState(function: String) {
if (illegalSetOrRemoveState == null) {
throw IllegalStateException(
"Observable List iterator '$function' was called before 'next' or 'previous'."
)
} else if (illegalSetOrRemoveState == true) {
throw IllegalStateException(
"Observable List iterator '$function' was called, " +
"but 'remove' or 'add' was already called this iteration."
)
}
}
}
@@ -0,0 +1,317 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.ListSubscription
import com.cobblemon.mod.common.api.reactive.collections.removeIf
class MutableObservableSubList<T>(
private val parentList: MutableObservableList<T>,
private val fromIndex: Int,
private val toIndex: Int,
elementHandler: (T) -> Set<Observable<*>>,
) : MutableObservableCollection<T, List<T>>,
ObservableSubList<T>(parentList, fromIndex, toIndex, elementHandler),
MutableList<T> {
private val additionObservable = SimpleObservable<Pair<List<T>, T>>()
private val removalObservable = SimpleObservable<Pair<List<T>, T>>()
private val setObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
private val swapObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
override val elements: List<T>
get() = when {
isEmpty() -> emptyList()
parentList.size < maxSize -> parentList.elements.subList(fromIndex, parentList.size)
else -> parentList.elements.subList(fromIndex, currentToIndex)
}
private val maxLastIndex: Int = toIndex - 1
private val maxSize: Int = toIndex - fromIndex
override val size: Int get() = when {
isEmpty() -> 0
parentList.size < toIndex -> parentList.lastIndex - fromIndex
else -> maxLastIndex
}
/** This is the last index in the sublist window, or -1 if the window is empty. */
val currentLastIndex: Int get() = size - 1
/** This is either [fromIndex] or -1 if the sublist window is empty. */
val currentFromIndex: Int get() = if (isEmpty()) -1 else fromIndex
/**
* If [parentList] size > [toIndex] this is the toIndex,
* if the parentList size is > [fromIndex] this is the parentList size,
* or this is the [currentFromIndex].
*/
private val currentToIndex: Int
get() = when {
toIndex < parentList.size -> toIndex
fromIndex < parentList.size -> parentList.size
else -> currentFromIndex
}
private val currentLastIndexInParent: Int
get() = when {
isEmpty() -> fromIndex
parentList.lastIndex < toIndex -> parentList.lastIndex
else -> maxLastIndex
}
init {
parentList.subscribeIndexed(
priority = Priority.NORMAL,
additionHandler = { (_, element, index) -> emitAddition(element, index) },
removalHandler = { (_, element, index) -> emitRemoval(element, index) },
setHandler = { (_, elements, index) -> emitSet(elements, index) },
swapHandler = { (_, element, index) -> emitSwap(element, index) },
)
}
override fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
}
fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
@Suppress("DuplicatedCode")
return ListSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
set = setHandler?.let { setObservable.subscribe(priority, it) },
swap = swapHandler?.let { swapObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
): ListSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler, setHandler, swapHandler)
}
private fun emitAddition(element: T, index: Int) {
if (index >= toIndex) {
return
}
val elements = this.elements
if (index >= fromIndex) {
handleAddition(elements, element)
} else if(parentList.lastIndex >= fromIndex) {
handleAddition(elements, parentList[fromIndex])
}
if (parentList.size > toIndex) {
handleRemoval(elements, parentList[toIndex])
}
}
private fun emitRemoval(element: T, index: Int) {
if (index >= toIndex) {
return
}
val elements = this.elements
if (index >= fromIndex) {
handleRemoval(elements, element)
} else if(parentList.lastIndex >= fromIndex - 1) {
handleRemoval(elements, parentList[fromIndex - 1])
}
if (currentLastIndexInParent >= toIndex - 1) {
handleAddition(elements, parentList[toIndex - 1])
}
}
private fun emitSet(elementPair: Pair<T, T>, index: Int) {
if (!index.isInParentRelativeWindow()) {
return
}
register(elementPair.first)
unregister(elementPair.second)
setObservable.emit(elements to elementPair)
emitAnyChange(elementPair.first)
emitAnyChange(elementPair.second)
}
private fun emitSwap(elementPair: Pair<T, T>, indices: Pair<Int, Int>) {
val firstInWindow = indices.first.isInParentRelativeWindow()
val secondInWindow = indices.second.isInParentRelativeWindow()
when {
firstInWindow && !secondInWindow -> {
val elements = this.elements
handleRemoval(elements, elementPair.first)
handleAddition(elements, elementPair.second)
}
!firstInWindow && secondInWindow -> {
val elements = this.elements
handleAddition(elements, elementPair.first)
handleRemoval(elements, elementPair.second)
}
firstInWindow && secondInWindow -> {
swapObservable.emit(elements to elementPair)
emitAnyChange(elementPair.first)
elementPair.second?.let { emitAnyChange(it) }
}
}
}
private fun handleAddition(elements: List<T>, element: T) {
register(element)
additionObservable.emit(elements to element)
emitAnyChange(element)
}
private fun handleRemoval(elements: List<T>, element: T) {
unregister(element)
removalObservable.emit(elements to element)
emitAnyChange(element)
}
override operator fun iterator() = MutableObservableSubListIterator(this)
override fun listIterator() = MutableObservableSubListIterator(this)
override fun listIterator(index: Int) = MutableObservableSubListIterator(this, index)
override fun subList(fromIndex: Int, toIndex: Int): MutableObservableSubList<T> {
return parentList.subList(parentIndex(fromIndex), parentIndex(toIndex))
}
fun swapIf(indexOne: Int, indexTwo: Int, predicate: () -> Boolean): Boolean {
return predicate().also { if (it) swap(indexOne, indexTwo) }
}
fun swap(indexOne: Int, indexTwo: Int) {
val parentIndexOne = parentIndex(indexOne)
val one = parentList[parentIndexOne]
val two = parentList.set(parentIndex(indexTwo), one)
parentList[parentIndexOne] = two
}
override fun addIf(element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(this)) add(element) else false
}
override fun add(element: T): Boolean {
return if (parentList.lastIndex < toIndex) {
parentList.add(element)
} else {
parentList.add(toIndex, element)
true
}
}
fun addAtIf(index: Int, element: T, predicate: (List<T>) -> Boolean): Boolean {
return if (predicate(this)) {
add(index, element)
true
} else {
false
}
}
override fun add(index: Int, element: T) {
parentList.add(parentIndex(index), element)
}
override operator fun set(index: Int, element: T): T {
return parentList.set(parentIndex(index), element)
}
override fun removeIf(predicate: (T) -> Boolean): Boolean = iterator().removeIf(predicate)
override fun remove(element: T): Boolean {
for (i in fromIndex until parentList.size) {
val current = elements[i]
if (current == element) {
parentList.removeAt(i)
return true
}
}
return false
}
override fun removeAt(index: Int): T = parentList.removeAt(parentIndex(index))
override fun addAll(elements: Collection<T>): Boolean {
val parentIndex = if (currentLastIndex > -1) currentLastIndex + fromIndex else fromIndex
return if (parentIndex > parentList.lastIndex) {
parentList.addAll(elements)
} else {
parentList.addAll(parentIndex, elements)
}
}
override fun addAll(index: Int, elements: Collection<T>): Boolean {
return parentList.addAll(parentIndex(index), elements)
}
override fun removeAll(elements: Collection<T>): Boolean {
return iterator().removeIf { elements.contains(it) }
}
override fun retainAll(elements: Collection<T>): Boolean {
return iterator().removeIf { !elements.contains(it) }
}
/**
* This implementation will remove all elements from the window dynamically. If an element
* is removed & an element shifts into the sublist window that meets the predicate, the
* new element will also be removed.
*
* Note: The other iterators including the ObservableSubListIterator will only operate on
* the window provided before the iteration, even when the iterator is mutable.
*/
private fun removeAllIf(predicate: (T) -> Boolean): Boolean {
if (isEmpty()) {
return false
}
var mutated = false
val outOfParentBounds: (Int) -> Boolean = { it == parentList.size }
for (i in fromIndex until currentToIndex) {
if (outOfParentBounds(i)) {
return mutated
}
while (predicate(parentList[i])) {
parentList.removeAt(i).also { if (!mutated) mutated = true }
if (outOfParentBounds(i)) {
return true
}
}
}
return mutated
}
override fun clear() {
if (isNotEmpty()) {
iterator().removeIf { true }
}
}
}
@@ -0,0 +1,63 @@
package com.cobblemon.mod.common.api.reactive.collections.list
// the name is not quite long enough yet...
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
open class MutableObservableSubListIterator<T>(
subList: MutableObservableSubList<T>,
index: Int = 0,
) : MutableObservableCollectionIterator<T, List<T>, ListIterator<T>>,
ObservableSubListIterator<T>(subList, index),
MutableListIterator<T> {
private val mutableSubList: MutableObservableSubList<T> = subList
private var illegalSetOrRemoveState: Boolean? = null
set (value) { if (field != value) field = value }
override fun next(): T {
illegalSetOrRemoveState = false
return super.next()
}
override fun previous(): T {
illegalSetOrRemoveState = false
return super.previous()
}
override fun remove() {
throwIfIllegalState("remove")
mutableSubList.removeAt(--index)
illegalSetOrRemoveState = true
}
override fun set(element: T) {
throwIfIllegalState("set")
mutableSubList[index -1] = element
}
override fun add(element: T) {
when {
subList.isEmpty() -> {
mutableSubList.add(element)
index = mutableSubList.currentFromIndex
}
index < mutableSubList.currentLastIndex -> mutableSubList.add(index++, element)
else -> mutableSubList.add(element).run { index++ }
}
illegalSetOrRemoveState = true
}
private fun throwIfIllegalState(function: String) {
if (illegalSetOrRemoveState == null) {
throw IllegalStateException(
"Observable Sub List iterator '$function' was called before 'next' or 'previous'."
)
} else if (illegalSetOrRemoveState == true) {
throw IllegalStateException(
"Observable Sub List iterator '$function' was called, " +
"but 'remove' or 'add' was already called this iteration."
)
}
}
}
@@ -0,0 +1,93 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.PrioritizedList
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
open class ObservableList<T>(
list: Collection<T>,
protected val elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) : ObservableCollection<T, List<T>>,
List<T>,
Observable<Pair<List<T>, T>> {
protected val list: MutableList<T> = list.toMutableList()
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<List<T>, T>>>()
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
override val elements: List<T> get() = list
override val size: Int get() = list.size
val lastIndex: Int get() = list.lastIndex
init {
list.forEach { register(it) }
}
override fun subscribe(
priority: Priority,
handler: (Pair<List<T>, T>) -> Unit,
): ObservableSubscription<Pair<List<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
return subscription
}
fun subscribeAndHandle(
priority: Priority,
handler: (Pair<List<T>, T>) -> Unit
): ObservableSubscription<Pair<List<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
list.forEach { handler(this to it) }
return subscription
}
override fun unsubscribe(subscription: ObservableSubscription<Pair<List<T>, T>>) {
subscriptions.remove(subscription)
}
protected fun register(element: T) {
elementHandler(element).forEach{ observable ->
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
}
}
protected fun unregister(element: T) {
elementHandler(element).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
}
/** @return true after handling [subscriptions]. */
protected fun emitAnyChange(element: T): Boolean {
subscriptions.forEach { it.handle(list to element) }
return true
}
override fun isEmpty() = list.isEmpty()
override fun isNotEmpty() = list.isNotEmpty()
override fun contains(element: T) = list.contains(element)
override fun containsAll(elements: Collection<T>) = list.containsAll(elements)
override operator fun get(index: Int): T = list[index]
override fun indexOf(element: T) = list.indexOf(element)
override operator fun iterator(): Iterator<T> = list.iterator()
override fun lastIndexOf(element: T) = list.lastIndexOf(element)
override fun listIterator(): ListIterator<T> = list.listIterator()
override fun listIterator(index: Int): ListIterator<T> = list.listIterator(index = index)
override fun subList(fromIndex: Int, toIndex: Int): List<T> = list.subList(fromIndex, toIndex)
override fun copy() = ObservableList(this.list, elementHandler)
open fun mutableCopy() = MutableObservableList(this.list, elementHandler)
}
@@ -0,0 +1,33 @@
package com.cobblemon.mod.common.api.reactive.collections.list
open class ObservableListIterator<T>(
protected var list: ObservableList<T>,
protected var index: Int = 0,
) : ListIterator<T> {
override fun hasNext(): Boolean = index < list.size
override fun hasPrevious(): Boolean = list.isNotEmpty() && index > 0
override fun next(): T {
if (!hasNext()) {
throw NoSuchElementException(
"Observable List iterator does not have next element from index $index."
)
}
return list[index++]
}
override fun previous(): T {
if (!hasPrevious()) {
throw NoSuchElementException(
"Observable List iterator does not have a previous element at index ${index -1}."
)
}
return list[--index]
}
override fun nextIndex(): Int = index
override fun previousIndex(): Int = index -1
}
@@ -0,0 +1,46 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
import net.minecraft.nbt.NbtCompound
fun <T> List<T>.saveToNbt(
elementKey: String = "element",
elementHandler: (T) -> NbtCompound,
): NbtCompound {
val nbt = NbtCompound()
var size = 0
this.iterator().forEach { nbt.put(elementKey + size++, elementHandler(it)) }
nbt.putInt("size", size)
return nbt
}
fun <T> NbtCompound.loadObservableListOf(
elementKey: String = "element",
loadElementHandler: (NbtCompound) -> T,
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = ObservableList(this.loadList(elementKey, loadElementHandler), elementObservableHandler)
fun <T> NbtCompound.loadMutableObservableListOf(
elementKey: String = "element",
loadElementHandler: (NbtCompound) -> T,
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = MutableObservableList(this.loadList(elementKey, loadElementHandler), elementObservableHandler)
private fun <T> NbtCompound.loadList(
elementKey: String = "element",
elementHandler: (NbtCompound) -> T
): List<T> {
val newList = mutableListOf<T>()
if (this.contains("size")) {
val size = this.getInt("size")
for (i in 0 until size) {
newList.add(elementHandler(this.getCompound(elementKey + i)))
}
}
return newList
}
@@ -0,0 +1,38 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
fun <T> Collection<T>.toObservableList(
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = ObservableList(this, elementHandler)
fun <T> Collection<T>.toMutableObservableList(
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = MutableObservableList(this, elementHandler)
fun <T> observableListOf(
vararg elements: T,
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): ObservableList<T> = ObservableList(elements.toList(), elementHandler)
fun <T> mutableObservableListOf(
vararg elements: T,
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): MutableObservableList<T> = MutableObservableList(elements.toList(), elementHandler)
fun <T> observableListOf(
elements: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): ObservableList<T> = ObservableList(elements, elementHandler)
fun <T> mutableObservableListOf(
elements: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): MutableObservableList<T> = MutableObservableList(elements, elementHandler)
@@ -0,0 +1,142 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.PrioritizedList
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
open class ObservableSubList<T>(
private val parentList: ObservableList<T>,
private val fromIndex: Int,
private val toIndex: Int,
private val elementHandler: (T) -> Set<Observable<*>>,
) : ObservableCollection<T, List<T>>,
List<T>,
Observable<Pair<List<T>, T>> {
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<List<T>, T>>>()
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
override val elements: List<T> = parentList.elements.subList(fromIndex, toIndex)
override val size: Int = toIndex - fromIndex
init {
for (i in fromIndex until toIndex) {
register(parentList[i])
}
}
override fun subscribe(
priority: Priority,
handler: (Pair<List<T>, T>) -> Unit,
): ObservableSubscription<Pair<List<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
return subscription
}
fun subscribeAndHandle(
priority: Priority,
handler: (Pair<List<T>, T>) -> Unit
): ObservableSubscription<Pair<List<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
elements.forEach { handler(this to it) }
return subscription
}
override fun unsubscribe(subscription: ObservableSubscription<Pair<List<T>, T>>) {
subscriptions.remove(subscription)
}
protected fun register(element: T) {
elementHandler(element).forEach { observable ->
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
}
}
protected fun unregister(element: T) {
elementHandler(element).forEach { observable ->
subscriptionMap.remove(observable)?.unsubscribe()
}
}
protected fun emitAnyChange(element: T): Boolean {
subscriptions.forEach { it.handle(elements to element) }
return true
}
protected fun Int.isInParentRelativeWindow(): Boolean = this in fromIndex until toIndex
override fun isEmpty() = parentList.lastIndex < fromIndex
override fun isNotEmpty() = parentList.size > fromIndex
override fun contains(element: T) = elements.contains(element)
override fun containsAll(elements: Collection<T>) = this.elements.containsAll(elements)
/** @throws IndexOutOfBoundsException if sublist does not contain [subListIndex]. */
protected fun parentIndex(subListIndex: Int) = throwIfOutOfBounds(subListIndex) + fromIndex
/**
* @throws IndexOutOfBoundsException [index] is above [currentLastIndex] or below [fromIndex]
* @return [index]
*/
private fun throwIfOutOfBounds(index: Int): Int {
return if (index in 0 until size) {
index
} else {
throw IndexOutOfBoundsException("Index $index is outside the sub list window.")
}
}
override operator fun get(index: Int): T {
return parentList[parentIndex(index)]
}
override fun indexOf(element: T): Int {
val endIndex = fromIndex + size
for (i in fromIndex until endIndex) {
if (elements[i] == element) {
return i
}
}
return -1
}
override fun lastIndexOf(element: T): Int {
val startIndex = fromIndex + size
for (i in startIndex downTo fromIndex) {
if (elements[i] == element) {
return i
}
}
return -1
}
override operator fun iterator() = ObservableSubListIterator(this)
override fun listIterator() = ObservableSubListIterator(this)
override fun listIterator(index: Int) = ObservableSubListIterator(this, index)
override fun subList(fromIndex: Int, toIndex: Int): List<T> {
return parentList.subList(parentIndex(fromIndex), parentIndex(toIndex))
}
/** @return a read-only copy that will NOT mutate if the backing list mutates. */
override fun copy() = ObservableList(elements, elementHandler)
/** @return a mutable copy that will NOT mutate if the backing list mutates. */
fun mutableCopy() = MutableObservableList(elements, elementHandler)
/** @return a read-only version of [parentList] that will NOT mutate if the parent list mutates */
fun getParentCopy(): ObservableList<T> = parentList.copy()
/** @return a read-only version of [parentList] that will NOT mutate if the parent list mutates */
fun getParentMutableCopy(): MutableObservableList<T> = parentList.mutableCopy()
/** @return a read-only version of [parentList] that WILL mutate if the backing list mutates */
fun getParent(): ObservableList<T> = parentList
}
@@ -0,0 +1,36 @@
package com.cobblemon.mod.common.api.reactive.collections.list
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollectionIterator
open class ObservableSubListIterator<T>(
protected val subList: ObservableSubList<T>,
protected var index: Int = 0,
) : ObservableCollectionIterator<T, ListIterator<T>>,
ListIterator<T> {
override fun hasNext(): Boolean = index < subList.size
override fun hasPrevious(): Boolean = subList.isNotEmpty() && index > 0
override fun next(): T {
if (!hasNext()) {
throw NoSuchElementException(
"Observable Sub List iterator does not have next element from index $index."
)
}
return subList[index++]
}
override fun previous(): T {
if (!hasPrevious()) {
throw NoSuchElementException(
"Observable Sub List iterator does not have previous element from index ${index - 1}."
)
}
return subList[--index]
}
override fun nextIndex(): Int = index
override fun previousIndex(): Int = index - 1
}
@@ -0,0 +1,101 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.MapSubscription
import kotlin.collections.Map.Entry
class MutableObservableMap<K, V>(
map: Map<K, V> = mapOf(),
entryHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
) : ObservableMap<K, V>(map = map, entryHandler = entryHandler),
MutableMap<K, V> {
private val additionObservable = SimpleObservable<Pair<Map<K, V>, Pair<K, V>>>()
private val removalObservable = SimpleObservable<Pair<Map<K, V>, Pair<K, V>>>()
fun subscribe(
priority: Priority = Priority.NORMAL,
anyChangeHandler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit,
additionHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
removalHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
): MapSubscription<K, V> {
return MapSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority = Priority.NORMAL,
anyChangeHandler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit,
additionHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
removalHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
): MapSubscription<K, V> {
entries.forEach { anyChangeHandler(this to it.pair()) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
}
/**
* @return `true` after handling [register] & all subscriptions to [additionObservable],
* & [ObservableMap.emitAnyChange].
*/
private fun emitAddition(key: K, value: V): Boolean {
register(key, value)
additionObservable.emit(map to (key to value))
return emitAnyChange(key, value)
}
/**
* @return `true` after handling [unregister] & all subscriptions to [removalObservable],
* & [ObservableMap.emitAnyChange].
*/
private fun emitRemoval(key: K, value: V): Boolean {
unregister(key, value)
removalObservable.emit(map to (key to value))
return emitAnyChange(key, value)
}
override operator fun iterator(): MutableObservableMapIterator<K, V> {
return MutableObservableMapIterator(this)
}
operator fun set(key: K, value: V): V? = put(key, value)
fun put(pair: Pair<K, V>): V? = put(pair.first, pair.second)
fun put(entry: Entry<K, V>): V? = put(entry.key, entry.value)
override fun put(key: K, value: V): V? {
val previous = map.put(key, value)
emitAddition(key, value)
if (previous != null) {
emitRemoval(key, previous)
}
return previous
}
fun remove(key: K, value: V): Boolean {
return if (map[key] == value) (remove(key) != null) else false
}
override fun remove(key: K): V? {
val value = map.remove(key) ?: return null
emitRemoval(key, value)
return value
}
override fun putAll(from: Map<out K, V>) = from.forEach { put(it.key, it.value) }
fun getOrDefault(key: K, defaultValue: V): V = map[key] ?: defaultValue
fun getOrPut(key: K, newValue: V) = map[key] ?: newValue.also { put(key, it) }
override fun clear() {
if (map.isNotEmpty()) {
keys.toSet().forEach { remove(it) }
}
}
}
@@ -0,0 +1,35 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import kotlin.collections.Map.Entry
open class MutableObservableMapIterator<K, V>(
private val observableMap: MutableObservableMap<K, V>,
) : ObservableMapIterator<K, V>(observableMap.entries.toSet().iterator()),
MutableIterator<Entry<K, V>> {
private lateinit var currentEntry: Entry<K, V>
private var removedThisIteration: Boolean? = null
set (value) { if (field != value) field = value }
override fun next(): Entry<K, V> {
currentEntry = super.next()
removedThisIteration = false
return currentEntry
}
override fun remove() {
if (removedThisIteration == null) {
throw IllegalStateException(
"Observable Map iterator 'remove' was called before 'next' or 'previous'."
)
} else if (removedThisIteration == true) {
throw IllegalStateException(
"Observable Map iterator 'remove' was called, " +
"but 'remove' was already called this iteration."
)
}
observableMap.remove(currentEntry.key)
removedThisIteration = true
}
}
@@ -0,0 +1,124 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import com.cobblemon.mod.common.api.PrioritizedList
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import kotlin.Comparator
import kotlin.collections.Map.Entry
open class ObservableMap<K, V>(
map: Map<K, V>,
protected val entryHandler: (K, V) -> Set<Observable<*>> =
{ k: K, v: V -> k.getEntryObservables(v) },
) : Map<K, V>,
Observable<Pair<Map<K, V>, Pair<K, V>>> {
protected val map: MutableMap<K, V> = map.toMutableMap()
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>>>()
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
override val size get() = map.size
override val keys get() = map.keys
override val values get() = map.values
override val entries get() = map.entries
init {
this.map.forEach { (key, value) ->
entryHandler(key, value).forEach{ observable ->
subscriptionMap[observable] = observable.subscribe {
emitAnyChange(key, value)
}
}
}
}
override fun subscribe(
priority: Priority,
handler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit
): ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
return subscription
}
fun subscribeAndHandle(
priority: Priority,
handler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit
): ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
map.forEach { handler(this to (it.pair())) }
return subscription
}
override fun unsubscribe(subscription: ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>>) {
subscriptions.remove(subscription)
}
protected open fun register(key: K, value: V) {
if (contains(key, value)) {
entryHandler(key, value).forEach{ observable ->
subscriptionMap[observable] = observable.subscribe { emitAnyChange(key, value) }
}
}
}
protected fun unregister(key: K, value: V) {
entryHandler(key, value).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
}
protected fun emitAnyChange(key: K, value: V): Boolean {
subscriptions.forEach { it.handle(map to (key to value)) }
return true
}
override fun isEmpty() = map.isEmpty()
fun isNotEmpty() = map.isNotEmpty()
override fun containsKey(key: K) = map.containsKey(key)
override fun containsValue(value: V) = map.containsValue(value)
fun contains(pair: Pair<K, V>): Boolean = contains(pair.first, pair.second)
fun contains(entry: Entry<K, V>): Boolean = contains(entry.key, entry.value)
fun contains(key: K, value: V) = map[key] == value
fun contains(predicate: (Entry<K, V>) -> Boolean) = map.any(predicate)
fun containsAll(other: Collection<Entry<K, V>>): Boolean {
return map.all { entry -> other.any { it.key == entry.key && it.value == entry.value } }
}
fun containsAll(other: Map<K, V>): Boolean {
return map.all { entry -> other.any { it.key == entry.key && it.value == entry.value } }
}
open operator fun iterator(): Iterator<Entry<K, V>> = map.iterator()
override operator fun get(key: K) = map[key]
fun toSortedMap(comparator: Comparator<in K>) = map.toMap().toSortedMap(comparator)
fun firstKeyOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): K? {
map.forEach { if (predicate(it)) return it.key }
return null
}
fun firstValueOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): V? {
map.forEach { if (predicate(it)) return it.value }
return null
}
fun firstEntryOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): Entry<K, V>? {
map.forEach { if (predicate(it)) return it }
return null
}
fun copy() = ObservableMap(this.map, entryHandler)
fun mutableCopy() = MutableObservableMap(this.map, entryHandler)
}
@@ -0,0 +1,11 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import kotlin.collections.Map.Entry
open class ObservableMapIterator<K, V>(
protected var iterator: Iterator<Entry<K, V>>
) : Iterator<Entry<K, V>> {
override fun hasNext():Boolean = iterator.hasNext()
override fun next(): Entry<K, V> = iterator.next()
}
@@ -0,0 +1,47 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import com.cobblemon.mod.common.api.reactive.Observable
import net.minecraft.nbt.NbtCompound
import kotlin.collections.Map.Entry
fun <K, V> Map<K, V>.saveToNbt(
entryKey: String = "entry",
entryHandler: (Entry<K, V>) -> NbtCompound,
): NbtCompound {
val nbt = NbtCompound()
var size = 0
this.iterator().forEach { nbt.put(entryKey + size++, entryHandler(it)) }
nbt.putInt("size", size)
return nbt
}
fun <K, V> NbtCompound.loadObservableMapOf(
entryKey: String = "entry",
loadEntryHandler: (NbtCompound) -> Pair<K, V>,
entryObservableHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
) = ObservableMap(this.loadMap(entryKey, loadEntryHandler), entryObservableHandler)
fun <K, V> NbtCompound.loadMutableObservableMapOf(
entryKey: String = "entry",
loadEntryHandler: (NbtCompound) -> Pair<K, V>,
entryObservableHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
) = MutableObservableMap(this.loadMap(entryKey, loadEntryHandler), entryObservableHandler)
private fun <K, V> NbtCompound.loadMap(
entryKey: String = "entry",
entryHandler: (NbtCompound) -> Pair<K, V>,
): MutableMap<K, V> {
val newMap: MutableMap<K, V> = mutableMapOf()
if (this.contains("size")) {
val size = this.getInt("size")
for (i in 0 until size) {
val (key, value) = entryHandler(this.getCompound(entryKey + i))
newMap[key] = value
}
}
return newMap
}
@@ -0,0 +1,113 @@
package com.cobblemon.mod.common.api.reactive.collections.map
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.collections.tryGetObservable
import kotlin.collections.Map.Entry
fun <K, V> Entry<K, V>.pair() = this.key to this.value
fun <K, V> Collection<Entry<K, V>>.pairs(): Set<Pair<K, V>> {
mutableSetOf<Pair<K, V>>().let { set ->
this.forEach { set.add(it.key to it.value) }
return set
}
}
fun <K, V> Map<K, V>.pairs(): Set<Pair<K, V>> {
mutableSetOf<Pair<K, V>>().let { set ->
this.forEach { set.add(it.key to it.value) }
return set
}
}
fun <K, V> Collection<Entry<K, V>>.toMap(): Map<K, V> = this.toMutableMap()
fun <K, V> Collection<Entry<K, V>>.toMutableMap(): MutableMap<K, V> {
return mutableMapOf<K, V>().putAll(this)
}
fun <K, V> MutableMap<K, V>.putAll(entries: Collection<Entry<K, V>>): MutableMap<K, V> {
return entries.forEach { this[it.key] = it.value }.let { this }
}
fun <K, V> defaultEntryHandler() = { k: K, v: V -> k.getEntryObservables(v) }
fun <K, V> K.getEntryObservables(value: V): Set<Observable<*>> {
val observables = mutableSetOf<Observable<*>>()
this.tryGetObservable()?.let { observables.add(it) }
value.tryGetObservable()?.let { observables.add(it) }
return observables
}
fun <K, V> Map<K, V>.toObservableMap(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(this, entryHandler)
fun <K, V> Map<K, V>.toMutableObservableMap(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(this, entryHandler)
fun <K, V> Collection<Pair<K, V>>.toObservableMap(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(this.toMap(), entryHandler)
fun <K, V> Collection<Pair<K, V>>.toMutableObservableMap(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(this.toMap(), entryHandler)
fun <K, V> observableMapOf(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(mapOf(), entryHandler)
fun <K, V> mutableObservableMapOf(
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(mapOf(), entryHandler)
fun <K, V> observableMapOf(
map: Map<K, V> = emptyMap(),
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(map, entryHandler)
fun <K, V> observableMapOf(
vararg pairs: Pair<K, V>,
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(pairs.toMap(), entryHandler)
fun <K, V> mutableObservableMapOf(
vararg pairs: Pair<K, V>,
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(pairs.toMap(), entryHandler)
fun <K, V> mutableObservableMapOf(
map: Map<K, V> = emptyMap(),
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(map, entryHandler)
fun <K, V> observableMapOf(
vararg entries: Entry<K, V>,
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(entries.toSet().toMap(), entryHandler)
fun <K, V> mutableObservableMapOf(
vararg entries: Entry<K, V>,
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(entries.toSet().toMap(), entryHandler)
fun <K, V> observableMapOf(
pairs: Collection<Pair<K, V>> = emptySet(),
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = ObservableMap(pairs.toMap(), entryHandler)
fun <K, V> mutableObservableMapOf(
pairs: Collection<Pair<K, V>> = emptySet(),
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
) = MutableObservableMap(pairs.toMap(), entryHandler)
@@ -0,0 +1,100 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.SetSubscription
import com.cobblemon.mod.common.api.reactive.collections.removeIf
class MutableObservableSet<T>(
set: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) : MutableObservableCollection<T, Set<T>>,
ObservableSet<T>(set, elementHandler),
MutableSet<T> {
private val additionObservable = SimpleObservable<Pair<Set<T>, T>>()
private val removalObservable = SimpleObservable<Pair<Set<T>, T>>()
override fun subscribe(
priority: Priority,
anyChangeHandler: (Pair<Set<T>, T>) -> Unit,
additionHandler: ((Pair<Set<T>, T>) -> Unit)?,
removalHandler: ((Pair<Set<T>, T>) -> Unit)?,
): SetSubscription<T> {
return SetSubscription(
anyChange = this.subscribe(priority, anyChangeHandler),
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
)
}
fun subscribeAndHandle(
priority: Priority,
anyChangeHandler: (Pair<Set<T>, T>) -> Unit,
additionHandler: ((Pair<Set<T>, T>) -> Unit)?,
removalHandler: ((Pair<Set<T>, T>) -> Unit)?,
): SetSubscription<T> {
elements.forEach { anyChangeHandler(this to it) }
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
}
/**
* @return `true` after handling [register] & all subscriptions to [additionObservable]
* & [ObservableSet.emitAnyChange].
*/
private fun emitAddition(element: T): Boolean {
register(element)
additionObservable.emit(set to element)
return emitAnyChange(element)
}
/**
* @return `true` after handling [unregister] & all subscriptions to [removalObservable]
* & [ObservableSet.emitAnyChange].
*/
private fun emitRemoval(element: T): Boolean {
unregister(element)
removalObservable.emit(set to element)
return emitAnyChange(element)
}
override operator fun iterator() = MutableObservableSetIterator(this)
override fun addIf(element: T, predicate: (Set<T>) -> Boolean): Boolean {
return if (predicate(set)) add(element) else false
}
override fun add(element: T): Boolean {
return if (set.add(element)) emitAddition(element) else false
}
override fun removeIf(predicate: (T) -> Boolean): Boolean {
return iterator().removeIf(predicate)
}
fun removeIf(element: T, predicate: (Set<T>) -> Boolean): Boolean {
return if (predicate(set)) remove(element) else false
}
override fun remove(element: T): Boolean {
return if (set.remove(element)) emitRemoval(element) else false
}
override fun addAll(elements: Collection<T>): Boolean {
var mutated = false
elements.forEach { if (add(it) && !mutated) mutated = true }
return mutated
}
override fun removeAll(elements: Collection<T>):Boolean = removeIf { elements.contains(it) }
override fun retainAll(elements: Collection<T>):Boolean = removeIf { !elements.contains(it) }
override fun clear() {
// just a simpler way to call the mutable iterator & emit with each removal
removeIf { true }
}
}
@@ -0,0 +1,33 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
open class MutableObservableSetIterator<T>(
private val observableSet: MutableObservableSet<T>,
) : MutableObservableCollectionIterator<T, Set<T>, Iterator<T>>,
ObservableSetIterator<T>(observableSet.elements.toSet().iterator()),
MutableIterator<T> {
private var currentElement: T? = null
set (value) { if (field != value) field = value }
private var removedThisIteration: Boolean = false
set (value) { if (field != value) field = value }
override fun next(): T {
currentElement = iterator.next()
removedThisIteration = false
return currentElement!!
}
override fun remove() {
if (currentElement == null) {
throw IllegalStateException("Observable Set iterator 'remove' was called before 'next' or 'previous'.")
} else if (removedThisIteration) {
throw IllegalStateException("Observable Set iterator 'remove' was called, but 'remove' was already called this iteration.")
}
observableSet.remove(currentElement)
currentElement = null
removedThisIteration = true
}
}
@@ -0,0 +1,86 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.PrioritizedList
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
open class ObservableSet<T>(
set: Collection<T>,
protected val elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) : ObservableCollection<T, Set<T>>,
Set<T>,
Observable<Pair<Set<T>, T>> {
protected val set: MutableSet<T> = set.toMutableSet()
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<Set<T>, T>>>()
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
// Keep as 'this.set' & not 'set,' b/c compiler doesn't like it & cries a little.
override val elements: Set<T> get() = this.set
override val size: Int get() = set.size
init {
set.forEach { register(it) }
}
override fun subscribe(
priority: Priority,
handler: (Pair<Set<T>, T>) -> Unit,
): ObservableSubscription<Pair<Set<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
return subscription
}
fun subscribeAndHandle(
priority: Priority,
handler: (Pair<Set<T>, T>) -> Unit
): ObservableSubscription<Pair<Set<T>, T>> {
val subscription = ObservableSubscription(this, handler)
subscriptions.add(priority, subscription)
set.forEach { handler(this to it) }
return subscription
}
/*
TODO: fix issue where passing subscription to addition or removal does not unsubscribe
Does this issue really matter?
Calling unsubscribe from the subscription directly works as intended
*/
override fun unsubscribe(subscription: ObservableSubscription<Pair<Set<T>, T>>) {
subscriptions.remove(subscription)
}
protected fun register(element: T) {
elementHandler(element).forEach{ observable ->
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
}
}
protected fun unregister(element: T) {
elementHandler(element).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
}
/** @return true after handling [subscriptions]. */
protected fun emitAnyChange(element: T): Boolean {
subscriptions.forEach { it.handle(set to element) }
return true
}
override fun isEmpty() = set.isEmpty()
override fun isNotEmpty() = set.isNotEmpty()
override fun contains(element: T) = set.contains(element)
override fun containsAll(elements: Collection<T>) = set.containsAll(elements)
override operator fun iterator(): Iterator<T> = set.iterator()
override fun copy() = ObservableSet(set, elementHandler)
fun mutableCopy() = MutableObservableSet(set, elementHandler)
}
@@ -0,0 +1,11 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollectionIterator
open class ObservableSetIterator<T>(
protected var iterator: Iterator<T>
) : ObservableCollectionIterator<T, Iterator<T>> {
override fun hasNext():Boolean = iterator.hasNext()
override fun next(): T = iterator.next()
}
@@ -0,0 +1,46 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
import net.minecraft.nbt.NbtCompound
fun <T> Set<T>.saveToNbt(
elementKey: String = "element",
elementHandler: (T) -> NbtCompound,
): NbtCompound {
val nbt = NbtCompound()
var size = 0
this.iterator().forEach { nbt.put(elementKey + size++, elementHandler(it)) }
nbt.putInt("size", size)
return nbt
}
fun <T> NbtCompound.loadObservableSetOf(
elementKey: String = "element",
loadElementHandler: (NbtCompound) -> T,
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = ObservableSet(this.loadSet(elementKey, loadElementHandler), elementObservableHandler)
fun <T> NbtCompound.loadMutableObservableSetOf(
elementKey: String = "element",
loadElementHandler: (NbtCompound) -> T,
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = MutableObservableSet(this.loadSet(elementKey, loadElementHandler), elementObservableHandler)
private fun <T> NbtCompound.loadSet(
elementKey: String = "element",
elementHandler: (NbtCompound) -> T,
): Set<T> {
val newSet = mutableSetOf<T>()
if (this.contains("size")) {
val size = this.getInt("size")
for (i in 0 until size) {
newSet.add(elementHandler(this.getCompound(elementKey + i)))
}
}
return newSet
}
@@ -0,0 +1,38 @@
package com.cobblemon.mod.common.api.reactive.collections.set
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
fun <T> Collection<T>.toObservableSet(
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = ObservableSet(this, elementHandler)
fun <T> Collection<T>.toMutableObservableSet(
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
) = MutableObservableSet(this, elementHandler)
fun <T> observableSetOf(
vararg elements: T,
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): ObservableSet<T> = ObservableSet(elements.toSet(), elementHandler)
fun <T> mutableObservableSetOf(
vararg elements: T,
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): MutableObservableSet<T> = MutableObservableSet(elements.toSet(), elementHandler)
fun <T> observableSetOf(
elements: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): ObservableSet<T> = ObservableSet(elements, elementHandler)
fun <T> mutableObservableSetOf(
elements: Collection<T> = setOf(),
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
): MutableObservableSet<T> = MutableObservableSet(elements, elementHandler)
@@ -0,0 +1,27 @@
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
/**
* Immutable observable collections implement an [anyChange] observable subscription directly.
*
* Mutable observable collections implement [anyChange], [addition], & [removal] subscriptions
* through a [CollectionSubscription].
*
* @property anyChange Emits with any mutation of the collection or the collection's observable elements.
*
* @property addition Emits when any collection mutation results from the addition of an element.
*
* @property removal Emits when any collection mutation results from the removal of an element.
*
* @see ListSubscription.set
* @see ListSubscription.swap
*/
interface CollectionSubscription<T, C : Collection<T>> {
val anyChange: ObservableSubscription<Pair<C, T>>
val addition: ObservableSubscription<Pair<C, T>>?
val removal: ObservableSubscription<Pair<C, T>>?
fun unsubscribe()
}
@@ -0,0 +1,18 @@
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
data class IndexedListSubscription<T>(
val addition: ObservableSubscription<Triple<List<T>, T, Int>>? = null,
val removal: ObservableSubscription<Triple<List<T>, T, Int>>? = null,
val set: ObservableSubscription<Triple<List<T>, Pair<T, T>, Int>>? = null,
val swap: ObservableSubscription<Triple<List<T>, Pair<T, T>, Pair<Int, Int>>>? = null,
) {
fun unsubscribe() {
addition?.unsubscribe()
removal?.unsubscribe()
set?.unsubscribe()
swap?.unsubscribe()
}
}
@@ -0,0 +1,20 @@
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
data class ListSubscription<T>(
override val anyChange: ObservableSubscription<Pair<List<T>, T>>,
override val addition: ObservableSubscription<Pair<List<T>, T>>? = null,
override val removal: ObservableSubscription<Pair<List<T>, T>>? = null,
val set: ObservableSubscription<Pair<List<T>, Pair<T, T>>>? = null,
val swap: ObservableSubscription<Pair<List<T>, Pair<T, T>>>? = null,
) : CollectionSubscription<T, List<T>> {
override fun unsubscribe() {
this.anyChange.unsubscribe()
addition?.unsubscribe()
removal?.unsubscribe()
set?.unsubscribe()
swap?.unsubscribe()
}
}
@@ -0,0 +1,31 @@
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.collections.map.ObservableMap
import com.cobblemon.mod.common.api.reactive.collections.map.MutableObservableMap
/**
* The immutable [ObservableMap] implements an [anyChange] observable subscription directly.
*
* The [MutableObservableMap] implements [anyChange], [addition], & [removal] subscriptions
* through this class.
*
* @property anyChange Emits with any mutation from removal, addition, or a mutation in place by
* an entry extending the observable interface.
*
* @property addition Emits when any mutation results from the addition of an element.
*
* @property removal Emits when any mutation results from the removal of an element.
*/
data class MapSubscription<K, T>(
val anyChange: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>,
val addition: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>? = null,
val removal: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>? = null,
) {
fun unsubscribe() {
anyChange.unsubscribe()
addition?.unsubscribe()
removal?.unsubscribe()
}
}
@@ -0,0 +1,16 @@
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
data class SetSubscription<T>(
override val anyChange: ObservableSubscription<Pair<Set<T>, T>>,
override val addition: ObservableSubscription<Pair<Set<T>, T>>? = null,
override val removal: ObservableSubscription<Pair<Set<T>, T>>? = null,
) : CollectionSubscription<T, Set<T>> {
override fun unsubscribe() {
this.anyChange.unsubscribe()
addition?.unsubscribe()
removal?.unsubscribe()
}
}
@@ -1,29 +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.reference
/**
* Reference data interface. Reference data is defined as information that applies to Cobblemon structurally,
* such as species, stat types, Pokémon types, etc. These do not change during runtime, and are instead used only
* referentially.
*
* A Cobblemon class or interface that implements this interface is capable of being added/modified/deleted
* on the server side and this information will reach the client on login.
*/
interface ReferenceData {
/**
* A unique identifier for this piece of reference data among this reference collection.
* Used to identify when two pieces of reference data are describing the same thing.
*/
fun id(): String
/** Generate a hash to be able to tell if the server and client have a differing version of this. */
override fun hashCode(): Int
// TODO write and read from byte buffer
}
Loaded 100 of 1951 files, more files were not shown because too many files have changed in this diff. Show more