Compare commits

..
Author SHA1 Message Date
Paul a7e6e638fe Added run_command molang function to general query structs and player structs 2024-05-20 09:10:04 +10:00
Paul 9ef3ea2932 Use context for flows because it's cooler, added more functions to pokemon related molang 2024-05-19 19:08:56 +10:00
Paul 38dcee3327 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into event-flows
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt
2024-05-19 11:27:18 +10:00
Hiroku f81e8ba766 Merge branch 'npcs' into 'sega_bass_pro_fishing_2'
NPC foundationwork that's not implemented but I need the tech for reasons

See merge request cable-mc/cobblemon!1024
2024-05-19 00:31:46 +00:00
Plastered_Crab 2f3ae9bc5e - correct particles now play at certain events when fishing 2024-05-16 15:37:11 -07:00
apion ccbe33bfe5 Fixed missing texture on fishing particle 2024-05-16 17:55:23 -04:00
apion 0989c90eb3 Fixed server sync issue on rod registry 2024-05-16 17:11:32 -04:00
apion ddcd0ffd37 Merge branch 'refs/heads/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
2024-05-16 16:57:47 -04:00
Plastered_Crab 1f5d15e59d - rods now use NBT to store baits on them 2024-05-16 13:21:32 -07:00
Plastered_Crab a3ff4b6b81 - more particle work and dynamic tooltip parsing 2024-05-15 22:22:46 -07:00
apion 90c5180712 Revert "update Forge to current RB, update Fabric deps to latest"
This reverts commit 7139d4708b.
2024-05-15 18:17:29 -04:00
Plastered_Crab d2c084e4af - more setup for the dynamic tooltips 2024-05-15 12:37:52 -07:00
Plastered_Crab 98eda52866 - adjustments to fishing particles 2024-05-15 12:13:07 -07:00
Plastered_Crab c9d3cbd848 - initial steps for dynamic tooltips for pokerods + test particles 2024-05-15 10:34:22 -07:00
Nick 158aa05f24 Merge branch 'fix/downgrade-loom' into 'main'
fix: resolve mapping issues by downgrading loom

See merge request cable-mc/cobblemon!1015
2024-05-15 15:38:09 +00:00
Apion 8924d0fe0f Merge branch 'navneetset-main-patch-27693' into 'main'
Fix PC store downsizing not working with .resize()

See merge request cable-mc/cobblemon!1016
2024-05-15 12:12:04 +00:00
PebblesandApion d510eaa462 Fix PC store downsizing not working with .resize() 2024-05-15 12:12:04 +00:00
Hiroku 8b63057d9d Merge branch 'we-ballin' into 'main'
Cherish, Dream, Beast, and Ancient Origin Send Out Particles

See merge request cable-mc/cobblemon!1018
2024-05-15 10:26:08 +00:00
Hiroku d572b3c505 Missed a bit [skip-ci] 2024-05-15 10:25:21 +00:00
Hiroku 8edc60224a Merge branch 'main' into 'we-ballin'
# Conflicts:
#   CHANGELOG.md
2024-05-15 10:24:54 +00:00
Hiroku a634c4f046 Merge branch 'update-libs' into 'main'
Update libs

See merge request cable-mc/cobblemon!1017
2024-05-15 10:23:14 +00:00
MoeBoy76 7139d4708b update Forge to current RB, update Fabric deps to latest 2024-05-15 11:11:02 +10:00
dhi_holo 50e99736a3 Partial Fix
Origin Ball now plays correctly in resource pack, but not in the mod itself, updated jsons to match, so all that's left is working out why the mod refuses to read the new jsons even though the new jsons are read in the files, in intellij, and in github desktop.
2024-05-15 10:43:09 +10:00
Plastered_Crab e9a2bed304 - fixed bobber splash sounds (again) 2024-05-14 17:42:40 -07:00
Plastered_Crab 6476f61d09 - fixed bobber splash sounds 2024-05-14 17:37:43 -07:00
MoeBoy76 68c4acf0eb Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2024-05-15 09:21:22 +10:00
Sterrezo 74ae10b342 Sound updates and fixes 2024-05-15 01:02:24 +02:00
Plastered_Crab 9e2b15e9ec - implementation of new Bobber landing sounds 2024-05-14 15:27:19 -07:00
broccoli 5b5e7109f5 fix: resolve mapping issues by downgrading loom
fixes regression in fd7b82dbe3
2024-05-14 23:17:16 +01:00
Plastered_Crab d401b987ce - Changed FishingSpawnContext to allow for min and max Lure Levels of rod enchants
- first implementation of new Bobber landing sounds
2024-05-14 14:53:09 -07:00
Hiroku 37596523db Merge branch 'tmetcalfe89/abstract-stat-ratio-requirement' into 'main'
abstracted stat ratio requirement

See merge request cable-mc/cobblemon!1011
2024-05-14 20:15:13 +00:00
Plastered_Crab b7ba3f62f7 - Made all berries work as bait as well as their effects
- Reworked IV bait logic to allow for stacking effects
- Fixed Glow Berries and Sweet Berries to work as bait
2024-05-14 12:35:43 -07:00
tmetcalfe89 eb80c20dbf updated readme 2024-05-14 09:11:16 -07:00
Apion 3813ec3c92 Merge branch 'sound' into 'main'
Renamed Relic Coin Sack sound assets. Added new sounds for Relic Coin Pouches.

See merge request cable-mc/cobblemon!1013
2024-05-14 14:12:24 +00:00
Sterrezo 9be212d2b8 Renamed Relic Coin Sack sound assets. Added new sounds for Relic Coin Pouches.
Renamed all sounds called "coin_pouch..." to "relic_coin_sack..." Added new sounds for breaking and placing Relic Coin Pouches with a new sound group for them.
2024-05-14 13:27:11 +02:00
tmetcalfe89 2559257b3a added licenses 2024-05-14 01:16:30 -07:00
Apion a1687e8304 Merge branch 'tmetcalfe89/ensure-defeat-count' into 'main'
Ensure update total defeat count on battle win

See merge request cable-mc/cobblemon!1010
2024-05-14 07:25:09 +00:00
tmetcalfe89 3aa95b9339 abstracted stat ratio requirement 2024-05-13 23:39:01 -07:00
tmetcalfe89 4e97f70801 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into tmetcalfe89/ensure-defeat-count 2024-05-13 22:39:56 -07:00
dhi_holo 404ca565f5 4 balls maybe working
Send out particles for Cherish, Dream, Beast, and Ancient Origin balls

Needs testing on one issue before MR
2024-05-14 15:35:32 +10:00
MoeBoy76 93db0e1cda Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2024-05-14 08:57:15 +10:00
Paul 78d34e0ae6 Mergo conflicto 2024-05-13 21:16:25 +10:00
Paul 4f7630eee0 Merge branch 'refs/heads/sega_bass_pro_fishing_2' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/ClientMoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/resources/cobblemon-common.accesswidener
#	gradle/libs.versions.yml
2024-05-13 20:55:16 +10:00
Hiroku 1bf9d36dbd Merge branch 'wheres-the-tag-drake' into 'main'
Updated fossils.json Item Tag

Closes #834

See merge request cable-mc/cobblemon!1009
2024-05-13 09:45:38 +00:00
dhi_holo b19c5ed125 Updated fossils.json Item Tag
Sorted the fossils tag to match the order on Bulbapedia (just to make sure all other fossils were there, and because it looks a little nicer), and added Fossilized Drake to the tag
2024-05-13 17:51:19 +10:00
Paul c1b5e58f8a Added Lure Ball functionality, improved spawning API's use of causes, added forcedAspects to Pokemon, added aspect/unaspect in PokemonProperties 2024-05-13 17:42:36 +10:00
Paul b1904ab3fd Sega Bas Pro Fishing 2™ is the 1.6.0 branch 2024-05-13 15:19:23 +10:00
Paul 949925ca9f Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2 2024-05-13 15:17:19 +10:00
Paul e3cca0d9cf SpawnAction#complete returns R instead of Boolean 2024-05-13 15:08:55 +10:00
tmetcalfe89 1aa55f8199 replaced entity ref with originalPokemon 2024-05-12 21:49:58 -07:00
Paul 4c16b2ce2e Track friendship on client entities, tweaks to dataTracker initializing, MoLang in pose & quirk conditions 2024-05-13 12:47:53 +10:00
Paul 7ebe0551e1 Fixed build issue caused by unnecessary AW 2024-05-11 18:54:29 +10:00
Paul 6e1d4e0c4d Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2 2024-05-11 18:32:10 +10:00
MoeBoy76 a0b263e392 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon 2024-05-03 10:45:11 +10:00
Paul 7aef56c610 license 2024-04-27 14:28:30 +10:00
Paul 27e1a34097 Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/resources/assets/cobblemon/textures/item/poke_balls/beast_ball.png
2024-04-27 12:43:42 +10:00
Paul b25dc6610e Fixed bobber spawning not working 2024-04-22 11:20:54 +10:00
Paul fa67bba631 Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/advancement/CobblemonCriteria.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/DisplayCaseRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/MagikarpModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/resources/assets/cobblemon/bedrock/pokemon/animations/0129_magikarp/magikarp.animation.json
2024-04-22 11:07:52 +10:00
Paul 9d49ce086a merge conflict bug resolution 2024-04-11 15:10:17 +10:00
Paul 5f8c1d5b6d Merge remote-tracking branch 'origin/main' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/FaintInstruction.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/JsonPokemonPoseableModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/PokemonPoseableModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonServerDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/particle/SnowstormParticleReader.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MiscUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/WorldExtensions.kt
2024-04-11 14:36:52 +10:00
Lily e6590bef08 moved most baits to the new system 2024-03-19 15:00:59 +00:00
Lily 60b0e70651 updated some baits to use the new system 2024-03-19 14:40:17 +00:00
Lily 0d9f8bbb36 refactored the fishing bait system to allow for multiple effects per item 2024-03-18 22:24:24 -04:00
Paul bd8d9fa9e3 Added a bunch of AI tasks to NPCs for combat (I dunno it's neat) 2024-03-16 11:16:58 +10:00
Sterrezo e75dcd63d1 Merge branch 'sega_bass_pro_fishing_2' of https://gitlab.com/cable-mc/cobblemon into sega_bass_pro_fishing_2 2024-03-04 17:46:49 +01:00
Sterrezo a4fc1442bf Updated fishing sounds 2024-03-04 17:46:06 +01:00
Plastered_Crab fb85ceacb0 - More adjustments to the reel-in trajectory 2024-03-01 12:40:14 -08:00
Paul 9da2e125ac More NPC AI, some molang context implementation 2024-03-01 10:07:25 +10:00
jeffw773 7d9644ba51 - tweak Gyarados spawn for testing 2024-02-29 08:58:05 -08:00
Plastered_Crab 39c856b69a - Added in all Ancient Rod predicates so they look right when casting 2024-02-28 15:49:56 -08:00
Plastered_Crab 4a4c375f66 - Added in new test sounds for fishing 2024-02-28 14:56:28 -08:00
Plastered_Crab d22df3e554 - tweaked reel in logic and trajectory 2024-02-27 16:36:09 -08:00
Plastered_Crab ed4dadc3f6 - cleaned up code 2024-02-26 16:21:44 -08:00
Plastered_Crab c2e5e39019 - Tweaked IV bait logic 2024-02-26 16:11:35 -08:00
Plastered_Crab f383e5a52f - Fixed some bait logic 2024-02-26 15:33:28 -08:00
Plastered_Crab 914b7c63c9 - Hooking entities no longer crashes the game :D 2024-02-26 14:37:57 -08:00
Plastered_Crab 5a17a5ed68 - Glow/Sweet Berries can be used as bait + fixes for particles for fishing trails 2024-02-26 12:04:54 -08:00
jeffw773 bb1a46950f - added comment to bobber entity 2024-02-26 06:27:39 -08:00
jeffw773 679d3eae58 - edit to vanilla bait list 2024-02-26 06:08:04 -08:00
jeffw773 a9cd80e496 - remove junk test fishing loot table. Finalize % chance to get Smithing Template odds 2024-02-26 05:38:50 -08:00
apion a96cc307bb Fixed loot table injector 2024-02-25 23:39:06 -05:00
jeffw773 f2b2a282e5 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-25 19:56:20 -08:00
jeffw773 5bd7695ce8 - players can define their own bait now 2024-02-25 19:56:05 -08:00
Lily b42387a23f Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-25 17:40:43 -05:00
Lily 6aa1e6f9ad advancement + tag changes 2024-02-25 17:39:55 -05:00
jeffw773 414fcf8278 - Removed additional crafting recipe 2024-02-24 21:27:26 -08:00
jeffw773 a3c26484a4 - Inejected Pokerod Smithing Template into Treasure Fishing table 2024-02-24 21:25:06 -08:00
Paul 53acf7a628 Various improvements to NPC AI 2024-02-24 15:17:48 +10:00
Lily fbc8883c96 advancement fixes, removed crafting table recipes, fixed fishing bait not registering 2024-02-23 23:57:50 -05:00
Paul 235b5d062d Merge remote-tracking branch 'origin/main' into npcs 2024-02-24 13:56:55 +10:00
Paul 12b92d3f35 Added flow datapackable thing 2024-02-24 11:58:57 +10:00
jeffw773 baba3f669d Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-23 17:45:38 -08:00
jeffw773 2fe098adac - added in recipes for ancient rods 2024-02-23 17:45:23 -08:00
Lily 1c770049b4 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/PokerodItem.kt
2024-02-23 20:21:53 -05:00
Lily 71b854f36a ancient rods + advancements 2024-02-23 20:21:26 -05:00
jeffw773 61b944a293 - reworked fishing baits to be datapackable (now you can fish with steak if you wanted) 2024-02-23 16:16:48 -08:00
Paul dacfb4f1ff Merge branch 'npcs' into event-flows
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt
2024-02-24 10:02:26 +10:00
Paul 4086ba73d3 Some structs for things 2024-02-24 09:59:27 +10:00
jeffw773 46ed6d0492 - more bait swapping logic 2024-02-23 08:53:46 -08:00
jeffw773 72058bb56f - Fixed some bait swapping logic 2024-02-22 23:57:05 -08:00
jeffw773 a7b28ed936 - Added some wip fishing sounds 2024-02-22 23:19:25 -08:00
jeffw773 96fce55688 - added some additional fishing spawn pool pokemon 2024-02-22 22:15:27 -08:00
Lily 85daf496d8 changes to pokerod item lang 2024-02-22 23:16:09 -05:00
Paul 7655ddb25b More AI work for NPCs, im lernings 2024-02-23 12:16:10 +10:00
Plastered_Crab 7b4b498877 - All but 6 berries are implemented for bait (save me) 2024-02-22 15:52:17 -08:00
Plastered_Crab 0ee32c7fd3 - Most of the baits work now (I am so tired) 2024-02-22 14:44:47 -08:00
Plastered_Crab 2c2c1b0cf4 - More groundwork for bait 2024-02-22 09:30:46 -08:00
jeffw773 218edf680b - Ground work for baits having effects on fishing 2024-02-21 21:35:21 -08:00
Plastered_Crab 7598a0dacc - Updated assets for rods 2024-02-21 18:33:25 -08:00
Plastered_Crab 7327a4ddb5 - Bait can be removed and swapped out on the bobber 2024-02-21 11:44:21 -08:00
Plastered_Crab f1d3e7b12b - Bait can now be applied to the fishing bobbers 2024-02-21 11:09:53 -08:00
jeffw773 410ea6be87 - Tweaked line colors and started on bait testing 2024-02-20 23:08:48 -08:00
Plastered_Crab 399dfc33c5 - Added Beast Rod (blue string variant) 2024-02-20 17:05:20 -08:00
Plastered_Crab 26e39bb37d - Altered the trajectory of reeled in pokemon 2024-02-20 15:17:11 -08:00
Plastered_Crab 4ff7df63c0 - Fixed colored fishing lines for cosmetic rods and fixed line not breaking when swapping to another rod 2024-02-20 14:45:39 -08:00
jeffw773 1d1f749d6d - Added cast predicates and JSON features to each remaining cosmetic rod 2024-02-20 01:36:14 -08:00
Plastered_Crab d6e4b9c1a6 - Added other rods to the CobblemonItems list 2024-02-19 17:19:56 -08:00
apion 15d3ee0d49 Data-packable fishing rods kind of 2024-02-19 19:56:22 -05:00
Plastered_Crab de0ed1ab41 - Added crafting table recipes for cosmetic rods 2024-02-19 15:12:47 -08:00
Lily 81d930afae poke rod sync stuff (not fully working atm) 2024-02-19 15:37:30 -05:00
Plastered_Crab b83d75f30f - More Attempts at setting ball used for bobber and line color for rod during registry 2024-02-19 11:42:04 -08:00
Plastered_Crab 5814c1a5d6 - Attempts to get each ball rendered correctly for each rod (still needs work) 2024-02-19 10:10:57 -08:00
jeffw773 a45fec50a4 - Added fancy cosmetic Rods for each pokeball type 2024-02-18 22:51:05 -08:00
jeffw773 576b6ea767 - implemented test Magikarp animations + added test hook to Bobber 2024-02-18 18:05:16 -08:00
Paul dd6271ceed Merge remote-tracking branch 'origin/main' into npcs 2024-02-19 11:35:40 +10:00
jeffw773 792287cb5a - added new Magikarp models/textures/animations 2024-02-18 14:44:11 -08:00
Lily 42ad084dbe pokerod assets 2024-02-18 16:24:20 -05:00
jeffw773 b9bd61f3dc - fishing line connects to bobber much better now 2024-02-18 01:04:41 -08:00
jeffw773 c6d6f98d03 - fishing line connects to bobber much better now 2024-02-18 01:03:28 -08:00
jeffw773 abbd17faf7 - Pokeball Bobber now casts with more controlled random pitch/yaw amounts and also rotates upright when it hits water 2024-02-18 00:29:21 -08:00
jeffw773 c159137a70 Merge remote-tracking branch 'origin/display_case' into sega_bass_pro_fishing_2 2024-02-17 22:54:08 -08:00
jeffw773 03628631f7 - Pokeball bobber now rotates upwards when bobbing in water 2024-02-17 11:50:37 -08:00
jeffw773 e5e61df4b2 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt
2024-02-17 10:31:26 -08:00
Lily f07c4bf5b1 refactored fishing bobber variable names to be more readable instead of the single-letter names that mc gives us 2024-02-17 11:31:38 -05:00
Lily 0e3fd520f0 fixed naming to be more consistent with vanilla textures 2024-02-17 11:23:08 -05:00
jeffw773 935f3100d2 - Pokeball Bobber now slows down fast in water (due to water friction) and has a random spin applied when casting 2024-02-16 22:12:07 -08:00
Lily 8b32764ce3 pokerod smithing template assets 2024-02-17 00:14:26 -05:00
Lily 3e5a57ea20 pokerod now slowly stops spinning 2024-02-16 23:35:34 -05:00
Plastered_Crab 6ee94aa55b - Pokeball Bobber now spins when cast and stops spinning on land, needs fixed for water surface 2024-02-16 17:01:07 -08:00
Paul 05e7e1448b Removed redundant function 2024-02-17 10:51:10 +10:00
Lily bd5185e69e Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-16 19:31:40 -05:00
Lily 5e8df4609b started bobber rendering 2024-02-16 19:31:21 -05:00
Plastered_Crab 289d1474a6 - Beeeeg Whiscash has been added to fishing spawn chances 2024-02-16 14:54:52 -08:00
Plastered_Crab e4604c0d13 - Wooper (Heart variant) is now fully implemented for fishing 2024-02-16 14:24:31 -08:00
Tyzillion d4f91fa945 Wooper Heart Fix. <3 2024-02-16 16:53:53 -05:00
Plastered_Crab 5c52a9a31a - Started Implemented Wooper (heart form) to be added to the fishing chance pool 2024-02-16 13:19:57 -08:00
Plastered_Crab db2efcb48e - added new spawn conditional for lureLevel. Variants of Karp can now only have a chance to appear at certain Lure levels 2024-02-16 10:32:55 -08:00
Plastered_Crab 3bcbe2fb45 - Incorporated Pokerod Treasure loot table and player will physically reel in pokemon under 200 lbs 2024-02-16 09:32:01 -08:00
Paul c7202cc869 Fixed magikarp jump pattern spawn file 2024-02-16 11:34:40 +10:00
Lily 269e18786b Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-15 19:40:14 -05:00
Lily bd79a4ecfb pokerod smithing template (needs some more assets but should be functional) 2024-02-15 19:39:49 -05:00
Plastered_Crab 3c3a8b1af9 - Pokerod now cannot stack and has same durability as vanilla fishing rod 2024-02-15 16:07:54 -08:00
Lily 11e2ed34e5 Merge branch 'main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/client/CobblemonFabricClient.kt
#	forge/src/main/kotlin/com/cobblemon/mod/forge/client/CobblemonForgeClient.kt
2024-02-15 18:54:42 -05:00
Plastered_Crab 1500d6cdf1 - All Magikarp Jump variants implemented and new item loot tables started 2024-02-15 14:59:18 -08:00
Plastered_Crab acbbe2a1ba - Start to JUMP variants spawning properly when fishing 2024-02-14 16:42:58 -08:00
Plastered_Crab 0fe6bc918c - Adding Magikarp JUMP addon variants into Base mod 2024-02-14 14:57:27 -08:00
Plastered_Crab d399b3e29a - Magikarps now get reeled in at good distances in front of player 2024-02-14 14:20:32 -08:00
Plastered_Crab acc6f119e1 - Magikarps getting reeled in now land in front of the player instead of behind the player (for battle reasons) 2024-02-13 16:45:22 -08:00
Plastered_Crab 932a2c375c - Begin different reel-in trajectories for fishing up Magikarps 2024-02-13 14:46:42 -08:00
Paul 1d3de659a8 Conflicty resolvo 2024-02-11 12:14:51 +10:00
Paul 9e5879edd3 Put molang back 2024-02-11 12:01:39 +10:00
Paul 4f71340ef8 Conflict stuff 2024-02-11 11:59:40 +10:00
Paul b5c4863e20 Setup saving for NPC edit UI 2024-02-11 11:38:28 +10:00
Paul a3eff4b195 Merge branch 'move-animations' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
2024-02-10 16:12:26 +10:00
Nick 142eebe9e6 Apply model predicates, starting with Pokerod 2024-02-09 22:07:43 -08:00
jeffw773 36f19c9180 - Xp is rewarded to player for fishing up Pokemon 2024-02-09 21:15:10 -08:00
Paul 168e034daf Removed some more dead code 2024-02-10 13:28:56 +10:00
Paul 827e651b22 Removed some dead code 2024-02-10 12:15:37 +10:00
Paul fc34312bbb Crucial all-important fix (may change the world as we know it) 2024-02-10 12:15:14 +10:00
Paul 53261b3274 Removed dead code 2024-02-10 11:40:35 +10:00
Paul 9c3a38657c Merge remote-tracking branch 'origin/main' into move-animations 2024-02-10 11:37:57 +10:00
Paul 656487c868 Oops don't remove that 2024-02-10 11:36:36 +10:00
Paul d491f16aff Merge remote-tracking branch 'origin/main' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/fossil/FossilModel.kt
2024-02-10 11:31:10 +10:00
Paul 29a5b4931d Changelog 2024-02-10 11:30:47 +10:00
Paul 42fdcc740e The default target locator would probably work as a default special_attack locator 2024-02-10 11:27:20 +10:00
Paul 3bfc3ca425 Approximate the target locator for pokemon without that locator in the model 2024-02-10 11:19:28 +10:00
Paul 82682c3710 Merge remote-tracking branch 'origin/main' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityState.kt
2024-02-10 10:37:12 +10:00
jeffw773 beb8fed6bd - Pokeball Bobber now renders correctly when cast out with Pokerod
- Luck of the Sea and Lure get read correctly from the rod and factor into the spawn bucket chosen
2024-02-09 14:17:13 -08:00
jeffw773 142284a17c - Pokemon rarity is altered by Luck of the Sea 2024-02-08 21:18:41 -08:00
Paul 261850cc9f Early work on NPC editor 2024-02-09 13:47:40 +10:00
jeffw773 f1ea86362a - Fixed fishing spawns being able to check if sky is actually visible.
- added isOcean to biomes to Magikarp
2024-02-07 18:28:42 -08:00
Paul d1772b0241 Merge branch 'amo/sendout-upgrade' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2024-02-04 14:46:04 +10:00
Paul ec3361ad91 Added basic fishing spawn detail for Magikarp and use the world pool for fishing because ehhh 2024-02-03 12:16:53 +10:00
Paul ca7c36ce15 Added trigger spawning with a fishing implementation 2024-02-03 12:13:38 +10:00
jeffw773 f7c73728d7 - Added some newer spawning stuff and added battle initiation after fished up mon 2024-02-02 16:38:15 -08:00
jeffw773 1992a23edc Merge branch 'main' into sega_bass_pro_fishing_2 2024-02-02 13:39:02 -08:00
Paul 4bb2564b80 Merge branch 'amo/sendout-upgrade' into move-animations 2024-02-02 14:26:44 +10:00
Paul 4ab72dd0d9 Added MoLang scrip framework 2024-02-02 11:31:42 +10:00
Paul 7f6eb452be Moved NPCs over to JSON posers and improved declaration of label exclusions in primary animations 2024-01-27 11:08:01 +10:00
Paul 0afe50c883 Some fixes to have NPCs use the send out animations 2024-01-26 12:59:08 +10:00
Paul a74499a14b Conflict issues 2024-01-26 11:07:23 +10:00
Paul e6ff24072f Merge branch 'move-animations' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/net/serializers/IdentifierDataSerializer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/scheduling/SchedulingFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.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/bedrock/animation/BedrockStatefulAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
2024-01-26 10:51:47 +10:00
Paul 7693d22e58 Why is there a .ignored folder 2024-01-25 19:31:25 +10:00
Paul f3544f9cdf Updated inbuilt posers for new animation system stuff 2024-01-25 19:30:44 +10:00
Paul 9f7800b995 Mountains of remaining work on move animations system 2024-01-25 19:10:41 +10:00
Paul 022c8ed499 Merge branch 'amo/sendout-upgrade' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.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/bedrock/animation/BedrockAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokeball/PokeBallModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/JsonPokemonPoseableModel.kt
2024-01-19 13:50:41 +10:00
jeffw773 dad28135cc - Brief changes to particle count based on rarity reeled in 2024-01-18 14:32:43 -08:00
jeffw773 e41d13dd53 - Pokerod now uses config values for rarity buckets and access biome spawn pools 2024-01-14 23:02:33 -08:00
jeffw773 64cae53d82 - Pokerod foundation laid out for having chance to spawn Pokemon instead of getting item as well as rarity chance started 2024-01-14 00:11:12 -08:00
dhi_holo c9bf0bd8d7 Merge branch 'sega_bass_pro_fishing_2' of https://gitlab.com/cable-mc/cobblemon into sega_bass_pro_fishing_2 2024-01-14 13:36:53 +10:00
dhi_holo 97d2047aee It's a Tool, It's a Rod
makes pokerod be held correctly, and adds second model file for the cast variation, when that is implemented.
2024-01-14 13:36:04 +10:00
jeffw773 811f8abed7 - Added override for model of pokerod when cast 2024-01-13 19:27:22 -08:00
jeffw773 ab02a54f28 - Finally the Pokerod is starting to work! (it is backwards tho) 2024-01-13 19:08:36 -08:00
jeffw773 6e0c1aefcc - More testing fishing code for pokerod 2024-01-13 13:55:02 -08:00
jeffw773 e887b6c43c - Some code for fishing testing again 2024-01-13 12:16:38 -08:00
jeffw773 25172c2bc9 - Some code for fishing testing 2024-01-13 11:23:38 -08:00
jeffw773 929b90173a - Some code for fishing testing 2024-01-12 11:58:02 -08:00
jeffw773 70a65f6ffe - Testing code 2024-01-12 07:39:21 -08:00
jeffw773 db7d3f01d8 - Starting implementation on fishing 2024-01-12 07:39:20 -08:00
Paul 08565a9823 Brought fossils into the new molang/animation changes 2024-01-12 10:01:02 +10:00
Paul e11245f609 Merge remote-tracking branch 'origin/main' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/ExpressionLike.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/SingleExpression.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/ClientMoLangFunctions.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/pokeball/PokeBallPoseableState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/Poseable.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MiscUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/ExpressionLikeAdapter.kt
2024-01-08 16:17:56 +10:00
Paul b3a6d8eb27 Tried fixing a wander path issue 2024-01-08 12:30:35 +10:00
Paul 093c3683c5 Merge remote-tracking branch 'origin/main' into npcs 2024-01-07 14:04:45 +10:00
Paul 2b4b9ca5dd Setup and foundation work on NPC entity AI 2024-01-07 12:32:25 +10:00
Paul ea70d27f87 Merge remote-tracking branch 'origin/main' into npcs 2024-01-06 10:37:21 +10:00
Paul 5487b81f10 Improved rendering of reference faces and NPCs in dialogue GUI 2024-01-06 10:36:37 +10:00
Paul 96b8aeb5b8 Added NPC face and dialogue scripty stuff woo 2024-01-05 16:00:03 +10:00
Paul bf1520b1b4 Fixed NPC recalling logic 2024-01-05 11:03:09 +10:00
Paul 0f22da7ff2 Merge remote-tracking branch 'origin/main' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.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/data/CobblemonDataProvider.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MiscUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/MoValueAdapter.kt
2024-01-05 10:20:35 +10:00
Paul 90e39874e0 Imports mainly 2023-12-27 11:12:58 +10:00
Paul 2d5796de71 Merge remote-tracking branch 'origin/improved-transformed-parts' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-12-26 13:32:48 +10:00
Paul 3b152eafac Conflict resolution artifact 2023-12-26 12:12:53 +10:00
Paul 06ed354ebe Merge branch 'improved-transformed-parts' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonServerDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt
2023-12-26 12:09:23 +10:00
Paul caad276cbf Moved NPC's to use modern MC AI 2023-12-25 20:16:41 +10:00
Paul 093ad6329d Merge remote-tracking branch 'origin/main' into npcs 2023-12-25 11:38:58 +10:00
Paul 94d216fd7a Corrections from merging in scheduling changes 2023-12-24 21:34:52 +10:00
Paul 04c63145e1 Merge branch 'improved-transformed-parts' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/battles/model/PokemonBattle.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/scheduling/SchedulingFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.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/client/render/models/blockbench/bedrock/animation/BedrockStatefulAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/bedrock/animation/BedrockStatelessAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonServerDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/client/CobblemonFabricClient.kt
#	forge/src/main/kotlin/com/cobblemon/mod/forge/client/CobblemonForgeClient.kt
2023-12-24 15:15:52 +10:00
Paul 80b8cebb19 Fully integrated smooth animation transitioning into move animations 2023-11-25 17:19:19 +10:00
Paul b339081128 Merge remote-tracking branch 'origin/improved-transformed-parts' into move-animations
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/scheduling/SchedulingFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.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/pokemon/gen1/CharizardModel.kt
2023-11-24 10:09:32 +10:00
Paul 506e685117 Added q.animation to poseable models molang runtime 2023-11-03 19:07:32 +10:00
Paul eceac5fa81 Substantially implemented system for action effects 2023-10-30 17:01:42 +10:00
Paul a6b5c0bfab Added support for arbitrary animation starting for poseable entities 2023-10-27 15:12:41 +10:00
Paul b1a0d4d5a7 Groundwork for effect animations 2023-10-26 18:47:32 +10:00
Paul 627d31d0cc Merge branch 'spawn-rules' into move-animations 2023-10-21 13:15:32 +10:00
Paul d9f1e3c15f Merge remote-tracking branch 'origin/main' into move-animations 2023-10-21 11:47:14 +10:00
Paul eca52623e8 Merge remote-tracking branch 'origin/main' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
2023-10-21 10:54:21 +10:00
Paul 10740ac1c8 Merge remote-tracking branch 'origin/main' into move-animations 2023-10-14 17:11:23 +10:00
Paul b9766486db Merge remote-tracking branch 'origin/main' into npcs 2023-10-13 18:07:42 +10:00
Paul bf6a923e2c Tracked data improvements for NPCs 2023-10-13 08:41:38 +10:00
Paul a073886bc5 Merge remote-tracking branch 'origin/main' into npcs 2023-10-09 17:04:40 +10:00
Paul 7c4749ecd1 Added variables listing to NPC class 2023-10-08 17:54:47 +10:00
Paul 76566669c6 Merge remote-tracking branch 'origin/spawn-rules' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonProperties.kt
2023-10-08 11:10:16 +10:00
Paul 5b8e673e53 Merge remote-tracking branch 'origin/main' into npcs 2023-10-07 21:29:53 +10:00
Paul f0a62d56a9 Merge remote-tracking branch 'origin/main' into move-animations 2023-10-07 14:56:49 +10:00
Paul 565a28de87 Merge remote-tracking branch 'origin/main' into move-animations 2023-10-02 20:16:34 +10:00
Paul de35a54f84 Fixes from merge from main 2023-09-26 18:02:32 +10:00
Paul 2c164685f4 Merge broken 2023-09-25 13:35:48 +10:00
Paul c3789cd620 Testcommand should spawn trainers not in the ground please thank you (also update trainer animations) 2023-08-27 11:02:03 +10:00
Paul 0905b16a48 Fixed instances of NPC Pokémon not being recalled or disappeared when the NPC dies 2023-08-27 11:01:14 +10:00
Paul 7881b14c4d Fixed NPC Pokémon persisting across restarts as if they were wild 2023-08-27 10:50:17 +10:00
Paul 49c8df2cef Polished timing of NPC sendout/recall animations, added win/loss NPC animations 2023-08-26 19:21:49 +10:00
Paul 165b51fde6 Many NPC battle improvements, including sendout/recall animations and scheduling, animation fixes 2023-08-25 22:07:09 +10:00
Paul b020f642f3 Merge branch 'main' into npcs 2023-08-22 19:56:15 +10:00
Paul 88e0bf57c8 Outlined NPC battle logic 2023-08-21 15:57:41 +10:00
Paul 2adb96c6bc Merge remote-tracking branch 'origin/main' into npcs 2023-08-20 19:30:24 +10:00
Paul 4094dfa6de Much more foundation for NPCs configuration 2023-08-19 12:53:25 +10:00
Paul 135ec86291 Basic AI for NPCs, implemented limb swing properties in molang to improve idle/walk for NPCs, fixes 2023-08-16 07:19:45 +10:00
Paul 910a8361ec Licenses 2023-08-13 21:51:15 +10:00
Paul 125f7d679f Much more NPC framework, rendering works, NPC class data registry configured 2023-08-13 21:50:45 +10:00
Paul 04abd73f35 Merge remote-tracking branch 'origin/main' into npcs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2023-08-13 20:40:34 +10:00
Paul f3ba33f7ba More outlining for action effects 2023-05-02 19:54:18 +10:00
Paul ed4dff66b6 Tentative move particle work 2023-04-27 22:45:58 +10:00
Paul 0f16290c34 Merge remote-tracking branch 'origin/1.19.3' into npcs 2023-04-24 21:16:23 +10:00
Paul 9725c46b30 Initial work on NPCs 2023-04-03 21:14:29 +10:00
755 changed files with 13643 additions and 812 deletions
+1
View File
@@ -36,6 +36,7 @@ common/src/main/resources/assets/cobblemon/showdown
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
common/.fabric
# MacOS
.DS_Store
+33 -3
View File
@@ -1,11 +1,41 @@
# Changelog
## [1.6.0 - The Bass Pro Fishing Update (Month Xth, 2024)](#1-6-0)
#### "Now that there's a fishing mechanic, the mod is actually good!"
### Additions
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
- Added aspect and unaspect PokemonProperty arguments (which also includes commands such as /pokemonedit, /spawnpokemon, and /givepokemon) to allow forcing or un-forcing an aspect on a Pokémon.
### 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.
## [1.5.1](#1-5-1)
### Fixes
-Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
### Additions
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
## [1.5.0 - The Ruins and Revival Update (May Xth, 2024)](#1-5-0)
### Changes
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
### Fixes
- Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
- Fixed Fossilized Drake being missing from the Fossils item tag.
- Fixed PCStore#resize not allowing PC boxes size reduction.
### Developer
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. More information this way.
### Data Pack & Resource Pack Creators
- Added support for MoLang conditions for quirks and poses.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual.
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
### Additions
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.entity.npc.NPCEntity;
import net.minecraft.entity.ai.brain.Brain;
import net.minecraft.entity.ai.brain.MemoryModuleType;
import net.minecraft.entity.ai.brain.sensor.Sensor;
import net.minecraft.entity.ai.brain.sensor.SensorType;
import java.util.Collection;
public class GenericsCheatClass {
// For some reason, doing this in the NPCEntity class in Kotlin gives confusing generic errors I could not resolve.
public static Brain.Profile<NPCEntity> createNPCBrain(Collection<MemoryModuleType<?>> memories, Collection<SensorType<? extends Sensor<? super NPCEntity>>> sensors) {
return Brain.createProfile(memories, sensors);
}
}
@@ -27,6 +27,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.IdentifierDataSerializer
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.UUIDSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.permission.PermissionValidator
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
@@ -49,8 +50,13 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.WorldSlice
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -87,6 +93,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.FlowHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
@@ -98,20 +105,16 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
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
@@ -126,6 +129,7 @@ import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@@ -254,6 +258,8 @@ object Cobblemon {
TrackedDataHandlerRegistry.register(StringSetDataSerializer)
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
TrackedDataHandlerRegistry.register(UUIDSetDataSerializer)
// Lowest priority because this applies after luxury ball bonus as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(Priority.LOWEST) { event ->
@@ -291,6 +297,10 @@ object Cobblemon {
CustomPokemonProperty.register(UncatchableProperty)
CustomPokemonProperty.register(PokemonFlagProperty)
CustomPokemonProperty.register(HiddenAbilityPropertyType)
CustomPokemonProperty.register(AspectPropertyType)
CustomPokemonProperty.register(UnaspectPropertyType)
FlowHandler.setup()
ifDedicatedServer {
isDedicatedServer = true
@@ -13,6 +13,7 @@ import net.minecraft.entity.ai.brain.Activity
object CobblemonActivities {
val activities = mutableListOf<Activity>()
val BATTLING_ACTIVITY = Activity("pokemon_battling")
val NPC_BATTLING = Activity("npc_battling")
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -234,7 +234,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.sounds(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
@@ -245,7 +245,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.sounds(CobblemonSounds.RELIC_COIN_SACK_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@@ -48,6 +48,7 @@ object CobblemonCommands {
PokemonRestartCommand.register(dispatcher)
BedrockParticleCommand.register(dispatcher)
OpenDialogueCommand.register(dispatcher)
NPCEditCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -66,6 +68,15 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(CHEST_BOAT_KEY.toString())
)
@JvmField
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
@JvmField
val POKE_BOBBER: EntityType<PokeRodFishingBobberEntity> = this.create(
POKE_BOBBER_KEY.path,
EntityType.Builder.create(::PokeRodFishingBobberEntity, SpawnGroup.MISC).setDimensions(0.5F, 0.5F).maxTrackingRange(10)
.build(POKE_BOBBER_KEY.toString())
)
@JvmField
val GENERIC_BEDROCK_ENTITY_KEY = cobblemonResource("generic_bedrock")
@JvmField
@@ -75,8 +86,16 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(GENERIC_BEDROCK_ENTITY_KEY.toString())
)
@JvmField
val NPC_KEY = cobblemonResource("npc")
@JvmField
val NPC: EntityType<NPCEntity> = create(
NPC_KEY.path,
EntityType.Builder.create({ _, world -> NPCEntity(world) }, SpawnGroup.CREATURE).build("$NPC_KEY")
)
fun registerAttributes(consumer: (EntityType<out LivingEntity>, DefaultAttributeContainer.Builder) -> Unit) {
consumer(POKEMON, PokemonEntity.createAttributes())
consumer(NPC, NPCEntity.createAttributes())
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.util.concurrent.ExecutionException
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
/**
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
* which can be cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
*
* @author Hiroku
* @since February 24th, 2024
*/
object CobblemonFlows : DataRegistry {
override val id = cobblemonResource("flows")
override val observable = SimpleObservable<CobblemonFlows>()
override val type = ResourceType.SERVER_DATA
override fun sync(player: ServerPlayerEntity) {}
val runtime by lazy { MoLangRuntime().setup() }
val clientFlows = hashMapOf<Identifier, MutableList<ExpressionLike>>()
val flows = hashMapOf<Identifier, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
manager.findResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
if (identifier.path.startsWith("flows/client/")) {
clientFlows.getOrPut(Identifier(identifier.namespace, event)) { mutableListOf() }.add(expression)
} else {
flows.getOrPut(Identifier(identifier.namespace, event)) { mutableListOf() }.add(expression)
}
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
}
}
}
}
Cobblemon.LOGGER.info("Loaded ${CobblemonScripts.scripts.size} flows and ${CobblemonScripts.clientScripts.size} client flows")
observable.emit(this)
}
fun run(eventIdentifier: Identifier, context: Map<String, MoValue>) {
flows[eventIdentifier]?.forEach { it.resolve(runtime, context) }
}
}
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.blue
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
@@ -45,6 +47,7 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.world.World
@@ -932,12 +935,121 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
Text.translatable("item.minecraft.fishing_rod").blue(),
Text.translatable("item.cobblemon.smithing_template.pokerod.ingredients").blue(),
Text.translatable("upgrade.cobblemon.pokerod").gray(),
Text.translatable("item.cobblemon.smithing_template.pokerod.base_slot_description"),
Text.translatable("item.cobblemon.smithing_template.pokerod.additions_slot_description"),
listOf(cobblemonResource("item/empty_slot_fishing_rod")),
listOf(cobblemonResource("item/empty_slot_pokeball"))
))
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
)
val pokeRods = mutableListOf<PokerodItem>()
@JvmField
val POKE_ROD = pokerodItem(cobblemonResource("poke_rod"))
@JvmField
val CITRINE_ROD = pokerodItem(cobblemonResource("citrine_rod"))
@JvmField
val VERDANT_ROD = pokerodItem(cobblemonResource("verdant_rod"))
@JvmField
val AZURE_ROD = pokerodItem(cobblemonResource("azure_rod"))
@JvmField
val ROSEATE_ROD = pokerodItem(cobblemonResource("roseate_rod"))
@JvmField
val SLATE_ROD = pokerodItem(cobblemonResource("slate_rod"))
@JvmField
val PREMIER_ROD = pokerodItem(cobblemonResource("premier_rod"))
@JvmField
val GREAT_ROD = pokerodItem(cobblemonResource("great_rod"))
@JvmField
val ULTRA_ROD = pokerodItem(cobblemonResource("ultra_rod"))
@JvmField
val SAFARI_ROD = pokerodItem(cobblemonResource("safari_rod"))
@JvmField
val FAST_ROD = pokerodItem(cobblemonResource("fast_rod"))
@JvmField
val LEVEL_ROD = pokerodItem(cobblemonResource("level_rod"))
@JvmField
val LURE_ROD = pokerodItem(cobblemonResource("lure_rod"))
@JvmField
val HEAVY_ROD = pokerodItem(cobblemonResource("heavy_rod"))
@JvmField
val LOVE_ROD = pokerodItem(cobblemonResource("love_rod"))
@JvmField
val FRIEND_ROD = pokerodItem(cobblemonResource("friend_rod"))
@JvmField
val MOON_ROD = pokerodItem(cobblemonResource("moon_rod"))
@JvmField
val SPORT_ROD = pokerodItem(cobblemonResource("sport_rod"))
@JvmField
val PARK_ROD = pokerodItem(cobblemonResource("park_rod"))
@JvmField
val NET_ROD = pokerodItem(cobblemonResource("net_rod"))
@JvmField
val DIVE_ROD = pokerodItem(cobblemonResource("dive_rod"))
@JvmField
val NEST_ROD = pokerodItem(cobblemonResource("nest_rod"))
@JvmField
val REPEAT_ROD = pokerodItem(cobblemonResource("repeat_rod"))
@JvmField
val TIMER_ROD = pokerodItem(cobblemonResource("timer_rod"))
@JvmField
val LUXURY_ROD = pokerodItem(cobblemonResource("luxury_rod"))
@JvmField
val DUSK_ROD = pokerodItem(cobblemonResource("dusk_rod"))
@JvmField
val HEAL_ROD = pokerodItem(cobblemonResource("heal_rod"))
@JvmField
val QUICK_ROD = pokerodItem(cobblemonResource("quick_rod"))
@JvmField
val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
@JvmField
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"))
@JvmField
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"))
@JvmField
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"))
@JvmField
val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
@JvmField
val ANCIENT_CITRINE_ROD = pokerodItem(cobblemonResource("ancient_citrine_rod"))
@JvmField
val ANCIENT_VERDANT_ROD = pokerodItem(cobblemonResource("ancient_verdant_rod"))
@JvmField
val ANCIENT_AZURE_ROD = pokerodItem(cobblemonResource("ancient_azure_rod"))
@JvmField
val ANCIENT_ROSEATE_ROD = pokerodItem(cobblemonResource("ancient_roseate_rod"))
@JvmField
val ANCIENT_SLATE_ROD = pokerodItem(cobblemonResource("ancient_slate_rod"))
@JvmField
val ANCIENT_IVORY_ROD = pokerodItem(cobblemonResource("ancient_ivory_rod"))
@JvmField
val ANCIENT_GREAT_ROD = pokerodItem(cobblemonResource("ancient_great_rod"))
@JvmField
val ANCIENT_ULTRA_ROD = pokerodItem(cobblemonResource("ancient_ultra_rod"))
@JvmField
val ANCIENT_FEATHER_ROD = pokerodItem(cobblemonResource("ancient_feather_rod"))
@JvmField
val ANCIENT_WING_ROD = pokerodItem(cobblemonResource("ancient_wing_rod"))
@JvmField
val ANCIENT_JET_ROD = pokerodItem(cobblemonResource("ancient_jet_rod"))
@JvmField
val ANCIENT_HEAVY_ROD = pokerodItem(cobblemonResource("ancient_heavy_rod"))
@JvmField
val ANCIENT_LEADEN_ROD = pokerodItem(cobblemonResource("ancient_leaden_rod"))
@JvmField
val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
@JvmField
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"))
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@@ -1003,6 +1115,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
private fun pokerodItem(pokeRodId: Identifier): PokerodItem {
val settings = Item.Settings().maxCount(1).maxDamage(Items.FISHING_ROD.maxDamage)
val item = create(pokeRodId.path, PokerodItem(pokeRodId, settings))
pokeRods.add(item)
return item
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Settings()).also {
@@ -10,11 +10,13 @@ package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import net.minecraft.resource.ResourceManager
@@ -29,6 +31,7 @@ object CobblemonMechanics : DataRegistry {
val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.create()
var remedies = RemediesMechanic()
@@ -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
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import java.util.Optional
import net.minecraft.entity.ai.brain.MemoryModuleType
import net.minecraft.util.Uuids
object CobblemonMemories {
val memories = mutableMapOf<String, MemoryModuleType<*>>()
val BATTLING_POKEMON = register("battling_pokemon", ListCodec(Uuids.INT_STREAM_CODEC))
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
memories[id] = memoryModule
return memoryModule
}
fun <U> register(id: String): MemoryModuleType<U> {
val memoryModule = MemoryModuleType<U>(Optional.empty())
memories[id] = memoryModule
return memoryModule
}
}
@@ -20,13 +20,15 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
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.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
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
@@ -64,13 +66,16 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.NPCRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.data.*
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
@@ -78,6 +83,8 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
@@ -115,7 +122,12 @@ 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.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
@@ -127,6 +139,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMove
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
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
@@ -163,6 +176,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokem
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
@@ -189,12 +203,12 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.util.server
import kotlin.reflect.KClass
import net.minecraft.network.PacketByteBuf
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* Registers Cobblemon network packets.
@@ -300,6 +314,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NPCRegistrySyncPacket.ID, NPCRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
@@ -307,6 +322,8 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PokeRodRegistrySyncPacket.ID, PokeRodRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -350,6 +367,10 @@ object CobblemonNetwork : NetworkManager {
// Dialogue packets
this.createClientBound(DialogueClosedPacket.ID, DialogueClosedPacket::decode, DialogueClosedHandler)
this.createClientBound(DialogueOpenedPacket.ID, DialogueOpenedPacket::decode, DialogueOpenedHandler)
// NPCs
this.createClientBound(CloseNPCEditorPacket.ID, CloseNPCEditorPacket::decode, CloseNPCEditorHandler)
this.createClientBound(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler)
}
override fun registerServerBound() {
@@ -419,6 +440,9 @@ object CobblemonNetwork : NetworkManager {
// Dialogue packets
this.createServerBound(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler)
this.createServerBound(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler)
// NPC packets
this.createServerBound(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler)
}
private inline fun <reified T : NetworkPacket<T>> createClientBound(identifier: Identifier, noinline decoder: (PacketByteBuf) -> T, handler: ClientNetworkPacketHandler<T>) {
@@ -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
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
import java.util.function.Supplier
import net.minecraft.entity.Entity
import net.minecraft.entity.ai.brain.sensor.Sensor
import net.minecraft.entity.ai.brain.sensor.SensorType
object CobblemonSensors {
val sensors = mutableMapOf<String, SensorType<*>>()
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
sensors[id] = sensor
return sensor
}
}
@@ -122,13 +122,26 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
@JvmField
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
val RELIC_COIN_SACK_BREAK = this.create("relic_coin_sack.break")
@JvmField
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
val RELIC_COIN_SACK_HIT = this.create("relic_coin_sack.hit")
@JvmField
val COIN_POUCH_STEP = this.create("coin_pouch.step")
val RELIC_COIN_SACK_STEP = this.create("relic_coin_sack.step")
@JvmField
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
val RELIC_COIN_SACK_PLACE = this.create("relic_coin_sack.place")
@JvmField
val RELIC_COIN_POUCH_BREAK = this.create("relic_coin_pouch.break")
@JvmField
val RELIC_COIN_POUCH_PLACE = this.create("relic_coin_pouch.place")
@JvmField
val FISHING_NOTIFICATION = this.create("fishing.notification")
@JvmField
val FISHING_SPLASH_BIG = this.create("fishing.splash_big")
@JvmField
val FISHING_SPLASH_SMALL = this.create("fishing.splash_small")
@JvmField
val FISHING_BOBBER_LAND = this.create("fishing.bobber_land")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -189,13 +202,22 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
val RELIC_COIN_SACK_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_SACK_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_SACK_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
)
@JvmField
val RELIC_COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_POUCH_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_POUCH_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -32,7 +32,14 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6))))
VillagerProfession.FARMER -> listOf(
VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
)
VillagerProfession.FISHERMAN -> listOf(
VillagerTradeOffer(VillagerProfession.FISHERMAN, 5, listOf(
TradeOffers.SellItemFactory(CobblemonItems.POKEROD_SMITHING_TEMPLATE, 12, 3, 30)
))
)
else -> emptyList()
}
@@ -113,6 +113,13 @@ object CobblemonCriteria {
)
)
val CAST_POKE_ROD = this.create(
SimpleCriterionTrigger(
cobblemonResource("cast_poke_rod"),
CastPokeRodCriterionCondition::class.java
)
)
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create(
SimpleCriterionTrigger(
@@ -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.advancement.criterion
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class CastPokeRodCriterionCondition(id: Identifier, predicate: LootContextPredicate): SimpleCriterionCondition<Boolean>(id, predicate) {
var hasBait: Boolean = false
override fun toJson(json: JsonObject) {
json.addProperty("hasBait", hasBait)
}
override fun fromJson(json: JsonObject) {
hasBait = json.get("hasBait").asBoolean
}
override fun matches(player: ServerPlayerEntity, context: Boolean): Boolean {
return hasBait == context
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.battles.model
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
@@ -20,6 +21,8 @@ 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.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -46,8 +49,10 @@ import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHit
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.withQueryValue
import java.io.File
import java.util.UUID
import java.util.concurrent.CompletableFuture
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.util.concurrent.ConcurrentLinkedDeque
@@ -66,6 +71,10 @@ open class PokemonBattle(
) {
/** Whether logging will be silenced for this battle. */
var mute = true
val struct = this.asMoLangValue()
val runtime = MoLangRuntime().also { it.environment.structs["query"] = struct }
val onEndHandlers: MutableList<(PokemonBattle) -> Unit> = mutableListOf()
init {
side1.battle = this
@@ -97,6 +106,8 @@ open class PokemonBattle(
val battleLog = mutableListOf<String>()
val chatLog = mutableListOf<Text>()
var started = false
var winners = listOf<BattleActor>()
var losers = listOf<BattleActor>()
var ended = false
// TEMP battle showcase stuff
var announcingRules = false
@@ -266,6 +277,7 @@ open class PokemonBattle(
}
sendUpdate(BattleEndPacket())
BattleRegistry.closeBattle(this)
onEndHandlers.forEach { it(this) }
}
fun finishCaptureAction(captureAction: BattleCaptureAction) {
@@ -353,6 +365,14 @@ open class PokemonBattle(
}
}
fun dispatchFuture(future: () -> CompletableFuture<*>) {
val dispatch = BattleDispatch {
val generatedFuture = future()
return@BattleDispatch DispatchResult { generatedFuture.isDone }
}
dispatches.add(dispatch)
}
fun dispatchInsert(dispatcher: () -> Iterable<BattleDispatch>) {
dispatch {
val newDispatches = dispatcher()
@@ -145,4 +145,7 @@ abstract class BattleActor(
}
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
open fun sendUpdate(packet: NetworkPacket<*>) {}
open fun win(otherWinners: List<BattleActor>, losers: List<BattleActor>) {}
open fun lose(winners: List<BattleActor>, otherLosers: List<BattleActor>) {}
}
@@ -27,10 +27,13 @@ object DialogueManager {
val activeDialogues = mutableMapOf<UUID, ActiveDialogue>()
fun startDialogue(playerEntity: ServerPlayerEntity, dialogue: Dialogue) {
val activeDialogue = ActiveDialogue(playerEntity, dialogue)
activeDialogues[playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, includeFaces = true)
playerEntity.sendPacket(packet)
startDialogue(ActiveDialogue(playerEntity, dialogue))
}
fun startDialogue(activeDialogue: ActiveDialogue) {
activeDialogues[activeDialogue.playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, true)
activeDialogue.playerEntity.sendPacket(packet)
}
fun stopDialogue(playerEntity: ServerPlayerEntity) {
@@ -12,6 +12,7 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
@@ -28,6 +29,10 @@ object EntityDimensionsAdapter : JsonSerializer<EntityDimensions>, JsonDeseriali
const val WIDTH = "width"
const val HEIGHT = "height"
val templates = mutableMapOf(
"player" to { EntityDimensions(0.6F, 1.8F, false) }
)
override fun serialize(dimensions: EntityDimensions, type: Type, ctx: JsonSerializationContext): JsonElement {
val json = JsonObject()
json.addProperty(WIDTH, dimensions.width)
@@ -36,6 +41,9 @@ object EntityDimensionsAdapter : JsonSerializer<EntityDimensions>, JsonDeseriali
}
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): EntityDimensions {
if (json is JsonPrimitive) {
return templates[json.asString]?.invoke() ?: throw IllegalStateException("Dimensions provided by template name $json but no such template is set.")
}
json as JsonObject
return EntityDimensions(json.get(WIDTH).asFloat, json.get(HEIGHT).asFloat, false)
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.entity
import com.bedrockk.molang.runtime.struct.QueryStruct
import net.minecraft.entity.Entity
import net.minecraft.entity.data.TrackedData
@@ -18,4 +19,5 @@ interface EntitySideDelegate<T : Entity> {
fun initialize(entity: T) {}
fun tick(entity: T) {}
fun onTrackedDataSet(data: TrackedData<*>) {}
fun addToStruct(struct: QueryStruct) {}
}
@@ -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.entity
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* A delegate for some actions that can be different for an NPC on client versus server.
*
* @author Hiroku
* @since August 25th, 2023
*/
interface NPCSideDelegate : EntitySideDelegate<NPCEntity>
@@ -0,0 +1,25 @@
/*
* 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.entity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import java.util.concurrent.CompletableFuture
/**
* Interface for custom entities that can send out and recall Pokémon. They get notified
* when either of those actions are occurring.
*
* @author Hiroku
* @since August 25th, 2023
*
*/
interface PokemonSender {
fun sendingOut(): CompletableFuture<Unit>
fun recalling(pokemonEntity: PokemonEntity): CompletableFuture<Unit>
}
@@ -8,12 +8,23 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.registry.RegistryKeys
import net.minecraft.server.network.ServerPlayerEntity
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity,
val pokeBallEntity: EmptyPokeBallEntity
)
) {
val context = mapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"player" to player.asMoLangValue(),
"poke_ball" to pokeBallEntity.struct,
"item" to player.world.itemRegistry.getEntry(pokeBallEntity.pokeBall.item).asMoLangValue(RegistryKeys.ITEM)
)
}
@@ -0,0 +1,84 @@
/*
* 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.fishing
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtString
data class FishingBait(
val item: Identifier,
val effects: List<Effect>,
) {
data class Effect(
val type: Identifier,
val subcategory: Identifier?,
val chance: Double = 0.0,
val value: Double = 0.0
) {
fun toNbt(): NbtCompound {
val nbt = NbtCompound()
nbt.putString("Type", type.toString())
subcategory?.let { nbt.putString("Subcategory", it.toString()) }
nbt.putDouble("Chance", chance)
nbt.putDouble("Value", value)
return nbt
}
companion object {
fun fromNbt(nbt: NbtCompound): Effect {
val type = Identifier(nbt.getString("Type"))
val subcategory = if (nbt.contains("Subcategory")) Identifier(nbt.getString("Subcategory")) else null
val chance = nbt.getDouble("Chance")
val value = nbt.getDouble("Value")
return Effect(type, subcategory, chance, value)
}
}
}
fun toNbt(): NbtCompound {
val nbt = NbtCompound()
nbt.putString("Item", item.toString())
val effectsList = NbtList()
effects.forEach { effectsList.add(it.toNbt()) }
nbt.put("Effects", effectsList)
return nbt
}
companion object {
fun fromNbt(nbt: NbtCompound): FishingBait {
val item = Identifier(nbt.getString("Item"))
val effectsList = nbt.getList("Effects", 10) // 10 is the type for NbtCompound
val effects = mutableListOf<Effect>()
for (i in 0 until effectsList.size) {
effects.add(Effect.fromNbt(effectsList.getCompound(i)))
}
return FishingBait(item, effects)
}
}
object Effects {
val NATURE = cobblemonResource("nature")
val IV = cobblemonResource("iv")
val EV = cobblemonResource("ev")
val BITE_TIME = cobblemonResource("bite_time")
val GENDER_CHANCE = cobblemonResource("gender_chance")
val LEVEL_RAISE = cobblemonResource("level_raise")
val TERA = cobblemonResource("tera")
val SHINY_REROLL = cobblemonResource("shiny_reroll")
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
val FRIENDSHIP = cobblemonResource("friendship")
val INERT = cobblemonResource("inert")
}
}
@@ -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.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fishing.FishingBaitRegistrySyncPacket
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.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object FishingBaits : JsonDataRegistry<FishingBait>{
override val id = cobblemonResource("fishing_baits")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<FishingBaits>()
override val typeToken: TypeToken<FishingBait> = TypeToken.get(FishingBait::class.java)
override val resourcePath = "fishing_baits"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
private val itemMap = mutableMapOf<Identifier, FishingBait>()
override fun sync(player: ServerPlayerEntity) {
FishingBaitRegistrySyncPacket(this.itemMap.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<Identifier, FishingBait>) {
itemMap.clear()
data.forEach { id, bait ->
itemMap[bait.item] = bait
}
}
fun getFromItemStack(stack: ItemStack): FishingBait? {
return itemMap[Registries.ITEM.getId(stack.item)]
}
fun isFishingBait(stack: ItemStack) = itemMap.containsKey(Registries.ITEM.getId(stack.item))
//
//
// fun getBaitSubcategory(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.subcategory
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.subcategory
// }
// return null
// }
//
// fun getBaitSuccessChance(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.chance
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.chance
// }
// return null
// }
//
// fun getBaitEffectValue(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.value
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.value
// }
// return null
// }
//
//
// fun getBaitNote(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.note
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.note
// }
// return null
// }
}
@@ -0,0 +1,48 @@
/*
* 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.fishing
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.pokeball.PokeBall
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
/**
* Base poke rod object
* It is intended that there is one poke rod object initialized for a given poke rod type.
*
* @property name the poke rod registry name
* @property pokeBallId The [Identifier] of the pokeball that is used as the bobber for this rod
* @property lineColor list of [RGB] values that apply to the fishing line of the Pokérod
*/
data class PokeRod(
val pokeBallId: Identifier,
//Hex string of color
val lineColor: String,
var name: Identifier?
) {
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(name)
buffer.writeIdentifier(pokeBallId)
buffer.writeString(lineColor)
}
companion object {
internal fun decode(buffer: PacketByteBuf): PokeRod {
val name = buffer.readIdentifier()
val pokeBallId = buffer.readIdentifier()
val lineColor = buffer.readString()
return PokeRod(pokeBallId, lineColor, name)
}
}
fun getPokeBall(): PokeBall? {
return PokeBalls.getPokeBall(pokeBallId)
}
}
@@ -0,0 +1,62 @@
/*
* 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.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokeRodRegistrySyncPacket
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.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
/**
* The data registry for [PokeRod]s.
* All the pokerod fields are guaranteed to exist
*/
object PokeRods : JsonDataRegistry<PokeRod> {
override val id = cobblemonResource("pokerods")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<PokeRods>()
// ToDo once datapack pokerod is implemented add required adapters here
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<PokeRod> = TypeToken.get(PokeRod::class.java)
override val resourcePath = "pokerods"
private val rods = mutableMapOf<Identifier, PokeRod>()
override fun reload(data: Map<Identifier, PokeRod>) {
data.forEach {
it.value.name = it.key
rods[it.key] = it.value
}
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
PokeRodRegistrySyncPacket(rods.values).sendToPlayer(player)
}
/**
* Gets a Pokerod from registry name.
* @return the pokerod object if found otherwise null.
*/
fun getPokeRod(name : Identifier): PokeRod? = rods[name]
}
@@ -10,12 +10,11 @@ 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.models.blockbench.PoseableEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
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.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Species
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
@@ -29,14 +28,17 @@ import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
import net.minecraft.text.MutableText
import net.minecraft.text.OrderedText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.RotationAxis
import org.joml.Matrix4f
import org.joml.Quaternionf
import org.joml.Vector3f
@JvmOverloads
fun blitk(
matrixStack: MatrixStack,
texture: Identifier? = null,
@@ -100,6 +102,7 @@ fun drawRectangle(
BufferRenderer.drawWithGlobalProgram(bufferbuilder.end())
}
@JvmOverloads
fun drawCenteredText(
context: DrawContext,
font: Identifier? = null,
@@ -114,6 +117,7 @@ fun drawCenteredText(
context.drawText(textRenderer, comp, x.toInt() - textRenderer.getWidth(comp) / 2, y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawText(
context: DrawContext,
font: Identifier? = null,
@@ -145,6 +149,7 @@ fun drawText(
return isHovered
}
@JvmOverloads
fun drawText(
context: DrawContext,
text: OrderedText,
@@ -163,6 +168,7 @@ fun drawText(
context.drawText(textRenderer, text, tweakedX.toInt(), y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawString(
context: DrawContext,
text: String,
@@ -181,22 +187,30 @@ fun drawString(
context.drawText(textRenderer, comp, x.toInt(), y.toInt(), colour, shadow)
}
fun drawPortraitPokemon(
species: Species,
@JvmOverloads
fun <T : Entity, M : PoseableEntityModel<T>> drawPoseablePortrait(
identifier: Identifier,
aspects: Set<String>,
matrixStack: MatrixStack,
scale: Float = 13F,
contextScale: Float = 1F,
reversed: Boolean = false,
state: PoseableEntityState<PokemonEntity>? = null,
partialTicks: Float
state: PoseableEntityState<T>? = null,
repository: VaryingModelRepository<T, M>,
partialTicks: Float,
limbSwing: Float = 0F,
limbSwingAmount: Float = 0F,
ageInTicks: Float = 0F,
headYaw: Float = 0F,
headPitch: Float = 0F
) {
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state?.animationSeconds ?: 0F)
val model = repository.getPoser(identifier, aspects)
val texture = repository.getTexture(identifier, aspects, state?.animationSeconds ?: 0F)
val context = RenderContext()
PokemonModelRepository.getTextureNoSubstitute(species.resourceIdentifier, aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
context.put(RenderContext.SPECIES, species.resourceIdentifier)
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)
val renderType = model.getLayer(texture)
@@ -212,7 +226,7 @@ fun drawPortraitPokemon(
model.getPose(PoseType.PORTRAIT)?.let { state.setPose(it.poseName) }
state.timeEnteredPose = 0F
state.updatePartialTicks(partialTicks)
model.setupAnimStateful(null, state, 0F, 0F, 0F, 0F, 0F)
model.setupAnimStateful(null, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
originalPose?.let { state.setPose(it) }
}
@@ -234,7 +248,7 @@ fun drawPortraitPokemon(
val buffer = immediate.getBuffer(renderType)
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.withLayerContext(immediate, state, repository.getLayers(identifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
immediate.draw()
}
@@ -242,5 +256,58 @@ fun drawPortraitPokemon(
matrixStack.pop()
model.setDefault()
DiffuseLighting.enableGuiDepthLighting()
}
fun <E : Entity, M : PoseableEntityModel<E>> drawProfile(
repository: VaryingModelRepository<E, M>,
resourceIdentifier: Identifier,
aspects: Set<String>,
matrixStack: MatrixStack,
state: PoseableEntityState<E>,
partialTicks: Float,
scale: Float = 20F
) {
val model = repository.getPoser(resourceIdentifier, aspects)
val texture = repository.getTexture(resourceIdentifier, aspects, state.animationSeconds)
val context = RenderContext()
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)
val renderType = model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
model.getPose(PoseType.PROFILE)?.let { state.setPose(it.poseName) }
state.timeEnteredPose = 0F
state.updatePartialTicks(partialTicks)
model.setupAnimStateful(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 = RotationAxis.POSITIVE_Y.rotationDegrees(-32F * if (false) -1F else 1F)
val quaternion2 = RotationAxis.POSITIVE_X.rotationDegrees(5F)
matrixStack.multiply(quaternion1)
matrixStack.multiply(quaternion2)
DiffuseLighting.method_34742()
val entityRenderDispatcher = MinecraftClient.getInstance().entityRenderDispatcher
entityRenderDispatcher.setRenderShadows(true)
val bufferSource = MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers
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 = LightmapTextureManager.pack(11, 7)
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
bufferSource.draw()
}
model.setDefault()
entityRenderDispatcher.setRenderShadows(true)
DiffuseLighting.enableGuiDepthLighting()
}
@@ -19,8 +19,10 @@ import com.bedrockk.molang.runtime.value.MoValue
* @since October 22nd, 2023
*/
interface ExpressionLike {
override fun toString(): String
/** Produces a [MoValue] for a [MoLangRuntime] to supply an environment. */
fun resolve(runtime: MoLangRuntime): MoValue
fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue> = emptyMap()): MoValue
fun getString(): String = toString()
fun resolveDouble(runtime: MoLangRuntime) = resolve(runtime).asDouble()
fun resolveFloat(runtime: MoLangRuntime) = resolveDouble(runtime).toFloat()
@@ -28,4 +30,5 @@ interface ExpressionLike {
fun resolveInt(runtime: MoLangRuntime) = resolveDouble(runtime).toInt()
fun resolveBoolean(runtime: MoLangRuntime) = resolveDouble(runtime) == 1.0
fun resolveObject(runtime: MoLangRuntime) = resolve(runtime) as ObjectValue<*>
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolve
/**
@@ -19,5 +21,6 @@ import com.cobblemon.mod.common.util.resolve
* @since October 22nd, 2023
*/
class ListExpression(val exprs: List<Expression>): ExpressionLike {
override fun resolve(runtime: MoLangRuntime) = exprs.resolve(runtime)
override fun toString() = exprs.joinToString("\n") { it.getString().let { if (it.endsWith(";")) it else "$it;" } }
override fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue>) = exprs.resolve(runtime, context)
}
@@ -18,12 +18,27 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.worldRegistry
import com.mojang.datafixers.util.Either
import net.minecraft.block.Block
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.damage.DamageSource
import net.minecraft.entity.damage.DamageTypes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtDouble
import net.minecraft.nbt.NbtElement
@@ -35,6 +50,7 @@ import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.registry.tag.TagKey
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.Identifier
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
@@ -48,6 +64,11 @@ import net.minecraft.world.dimension.DimensionType
*/
object MoLangFunctions {
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
"run_command" to java.util.function.Function { params ->
val command = params.getString(0)
val server = server() ?: return@Function DoubleValue(0)
server.commandManager.executeWithPrefix(server.commandSource, command)
},
"is_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
@@ -75,23 +96,134 @@ object MoLangFunctions {
val array = ArrayStruct(hashMapOf())
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
return@Function array
},
"run_script" to java.util.function.Function { params ->
val runtime = MoLangRuntime()
runtime.environment.structs.putAll(params.environment.structs)
val script = params.getString(0).asIdentifierDefaultingNamespace()
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
)
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val playerFunctions = mutableListOf<(ServerPlayerEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val playerFunctions = mutableListOf<(PlayerEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ player ->
hashMapOf(
"username" to java.util.function.Function { _ -> StringValue(player.gameProfile.name) },
"uuid" to java.util.function.Function { _ -> StringValue(player.gameProfile.id.toString()) },
"data" to java.util.function.Function { _ -> return@Function Cobblemon.molangData.load(player.uuid) },
"save_data" to java.util.function.Function { _ -> Cobblemon.molangData.save(player.uuid) },
"main_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.mainHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"off_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.offHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"face" to java.util.function.Function { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
)
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("username") { _ -> StringValue(player.gameProfile.name) }
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
map.put("data") { _ -> return@put if (player is ServerPlayerEntity) Cobblemon.molangData.load(player.uuid) else DoubleValue(0) }
map.put("save_data") { _ -> if (player is ServerPlayerEntity) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) }
map.put("main_held_item") { _ -> player.world.itemRegistry.getEntry(player.mainHandStack.item).asMoLangValue(RegistryKeys.ITEM) }
map.put("off_held_item") { _ -> player.world.itemRegistry.getEntry(player.offHandStack.item).asMoLangValue(RegistryKeys.ITEM) }
map.put("face") { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
map.put("swing_hand") { _ -> player.swingHand(player.activeHand) }
map.put("food_level") { _ -> DoubleValue(player.hungerManager.foodLevel) }
map.put("saturation_level") { _ -> DoubleValue(player.hungerManager.saturationLevel) }
map.put("tell") { params ->
val message = params.getString(0).text()
val overlay = params.getBooleanOrNull(1) ?: false
player.sendMessage(message, overlay)
}
map.put("teleport") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
player.teleport(x, y, z)
}
map.put("heal") { params ->
val amount = params.getDoubleOrNull(0) ?: player.maxHealth
player.heal(amount.toFloat())
}
map.put("environment") {
val environment = MoLangEnvironment()
environment.structs["query"] = player.asMoLangValue()
environment
}
map.put("run_command") { params ->
val command = params.getString(0)
val server = server() ?: return@put DoubleValue(0)
server.commandManager.executeWithPrefix(player.commandSource, command)
}
map
}
)
val entityFunctions = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("damage") { params ->
val amount = params.getDouble(0)
val source = DamageSource(entity.world.registryManager.get(RegistryKeys.DAMAGE_TYPE).getEntry(DamageTypes.GENERIC).get())
entity.damage(source, amount.toFloat())
}
map.put("is_sneaking") { _ -> DoubleValue(entity.isSneaking) }
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
map.put("is_in_water") { _ -> DoubleValue(entity.isSubmergedInWater) }
map.put("is_touching_water_or_rain") { _ -> DoubleValue(entity.isWet) }
map.put("is_touching_water") { _ -> DoubleValue(entity.isTouchingWater) }
map.put("is_in_lava") { _ -> DoubleValue(entity.isInLava) }
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
map.put("is_riding") { _ -> DoubleValue(entity.hasVehicle()) }
map.put("health") { _ -> DoubleValue(entity.health) }
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
map.put("name") { _ -> StringValue(entity.entityName) }
map.put("yaw") { _ -> DoubleValue(entity.yaw.toDouble()) }
map.put("pitch") { _ -> DoubleValue(entity.pitch.toDouble()) }
map.put("x") { _ -> DoubleValue(entity.x) }
map.put("y") { _ -> DoubleValue(entity.y) }
map.put("z") { _ -> DoubleValue(entity.z) }
map.put("velocity_x") { _ -> DoubleValue(entity.velocity.x) }
map.put("velocity_y") { _ -> DoubleValue(entity.velocity.y) }
map.put("velocity_z") { _ -> DoubleValue(entity.velocity.z) }
map.put("horizontal_velocity") { _ -> DoubleValue(entity.velocity.horizontalLength()) }
map.put("is_on_ground") { _ -> DoubleValue(entity.isOnGround) }
map.put("world") { _ -> entity.world.worldRegistry.getEntry(entity.world).asWorldMoLangValue() }
map.put("biome") { _ -> entity.world.getBiome(entity.blockPos).asBiomeMoLangValue() }
map
}
)
val npcFunctions = mutableListOf<(NPCEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ npc ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("class") { StringValue(npc.npc.resourceIdentifier.toString()) }
map.put("name") { StringValue(npc.name.string) }
map.put("face") { ObjectValue(ReferenceDialogueFaceProvider(npc.id)) }
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
map.put("run_script_on_client") { params ->
val world = npc.world
if (world is ServerWorld) {
val script = params.getString(0)
val packet = RunPosableMoLangPacket(npc.id, setOf("q.run_script('$script')"))
packet.sendToPlayers(world.players.toList())
}
Unit
}
map.put("run_script") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val environment = params.environment
val runtime = MoLangRuntime()
runtime.environment.structs.putAll(environment.structs)
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
map.put("environment") { _ -> npc.runtime.environment }
map
}
)
val battleFunctions = mutableListOf<(PokemonBattle) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ battle ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("battle_id") { StringValue(battle.battleId.toString()) }
map.put("is_pvn") { DoubleValue(battle.isPvN) }
map.put("is_pvp") { DoubleValue(battle.isPvP) }
map.put("is_pvw") { DoubleValue(battle.isPvW) }
map.put("battle_type") { StringValue(battle.format.toString()) }
map.put("environment") { battle.runtime.environment }
map
}
)
@@ -99,15 +231,35 @@ object MoLangFunctions {
fun RegistryEntry<World>.asWorldMoLangValue() = asMoLangValue(RegistryKeys.WORLD).addFunctions(worldFunctions)
fun RegistryEntry<Block>.asBlockMoLangValue() = asMoLangValue(RegistryKeys.BLOCK).addFunctions(blockFunctions)
fun RegistryEntry<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(RegistryKeys.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
fun ServerPlayerEntity.asMoLangValue(): ObjectValue<ServerPlayerEntity> {
fun PlayerEntity.asMoLangValue(): ObjectValue<PlayerEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.entityName }
)
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
value.addFunctions(playerFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.name.string }
)
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
value.addFunctions(npcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
val value = ObjectValue(
obj = this,
stringify = { it.battleId.toString() }
)
value.addFunctions(battleFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun <T> RegistryEntry<T>.asMoLangValue(key: RegistryKey<Registry<T>>): ObjectValue<RegistryEntry<T>> {
val value = ObjectValue(
obj = this,
@@ -134,11 +286,6 @@ object MoLangFunctions {
return this
}
fun <T : QueryStruct> T.addFunction(name: String, function: java.util.function.Function<MoParams, Any>): T {
this.functions[name] = function
return this
}
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()) } as QueryStruct
fun MoLangRuntime.setup(): MoLangRuntime {
@@ -193,4 +340,6 @@ object MoLangFunctions {
else -> null
} ?: throw IllegalArgumentException("Invalid NBT element type: ${nbt.type}")
}
}
}
fun Either<Identifier, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolve
/**
@@ -20,5 +22,6 @@ import com.cobblemon.mod.common.util.resolve
* @since October 22nd, 2023
*/
class SingleExpression(val expr: Expression) : ExpressionLike {
override fun resolve(runtime: MoLangRuntime) = runtime.resolve(expr)
override fun toString() = expr.getString()
override fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue>) = runtime.resolve(expr, context)
}
@@ -12,7 +12,6 @@ import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
@@ -11,9 +11,11 @@ package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.resolveBoolean
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.Entity
@@ -29,7 +31,7 @@ import net.minecraft.entity.Entity
*/
class ActionEffectTimeline(
val timeline: List<ActionEffectKeyframe> = mutableListOf(),
val condition: Expression = "true".asExpression()
val condition: ExpressionLike = "true".asExpressionLike()
) {
companion object {
val NONE = ActionEffectTimeline()
@@ -49,6 +49,6 @@ class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityC
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
}
}
@@ -46,6 +46,6 @@ class EntityMoLangActionEffectKeyframe : ConditionalActionEffectKeyframe(), Enti
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
}
}
@@ -51,6 +51,6 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
packet.sendToPlayers(players)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
}
}
@@ -45,6 +45,6 @@ class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), Entit
entity.playSound(soundEvent, 1f, 1f)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
}
}
@@ -25,6 +25,6 @@ class MoLangActionEffectKeyframe : ActionEffectKeyframe {
val delay: ExpressionLike = "0".asExpressionLike()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
expressions.resolve(context.runtime)
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
}
}
@@ -14,5 +14,5 @@ import com.cobblemon.mod.common.util.asExpressionLike
class PauseActionEffectKeyframe : ConditionalActionEffectKeyframe() {
val pause = "1".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime), serverThread = true)
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime))
}
@@ -24,7 +24,7 @@ class RemoveHoldsActionEffectKeyframe : ActionEffectKeyframe {
val delay = 0F
val holds = mutableSetOf<String>()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
return delayedFuture(seconds = delay, serverThread = true).thenApply {
return delayedFuture(seconds = delay).thenApply {
if (holds.isEmpty()) {
context.holds.clear()
} else {
@@ -56,7 +56,7 @@ class ReturnToPositionActionEffectKeyframe : ActionEffectKeyframe {
}
)
after(seconds = timeout.resolveFloat(context.runtime), serverThread = true) {
after(seconds = timeout.resolveFloat(context.runtime)) {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
@@ -29,5 +29,4 @@ object StringSetDataSerializer : TrackedDataHandler<Set<String>> {
}
override fun copy(set: Set<String>) = set.toSet()
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.net.serializers
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import java.util.UUID
import net.minecraft.entity.data.TrackedDataHandler
import net.minecraft.network.PacketByteBuf
object UUIDSetDataSerializer : TrackedDataHandler<Set<UUID>> {
override fun write(buffer: PacketByteBuf, set: Set<UUID>) {
buffer.writeSizedInt(IntSize.U_BYTE, set.size)
set.forEach(buffer::writeUuid)
}
override fun read(buffer: PacketByteBuf): Set<UUID> {
val set = mutableSetOf<UUID>()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
set.add(buffer.readUuid())
}
return set
}
override fun copy(set: Set<UUID>) = set.toSet()
}
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.NPCBattleConfiguration
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.readMapK
import com.cobblemon.mod.common.util.writeMapK
import com.mojang.datafixers.util.Either
import net.minecraft.entity.EntityDimensions
import net.minecraft.network.PacketByteBuf
import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
/**
* A class of NPC. This can contain a lot of preset information about the NPC's behaviour. Consider this the Pokémon
* species but for NPCs.
*
* @author Hiroku
* @since August 14th, 2023
*/
class NPCClass {
@Transient
lateinit var resourceIdentifier: Identifier
var names = mutableListOf<MutableText>(lang("npc.name.default"))
var hitbox = EntityDimensions(0.6F, 1.8F, false)
var battleConfiguration = NPCBattleConfiguration()
var interaction: Either<Identifier, ExpressionLike>? = null
var variables = mutableMapOf<String, MoValue>()
fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(names, PacketByteBuf::writeText)
buffer.writeFloat(this.hitbox.width)
buffer.writeFloat(this.hitbox.height)
buffer.writeBoolean(this.hitbox.fixed)
battleConfiguration.encode(buffer)
buffer.writeNullable(interaction) { _, value ->
buffer.writeBoolean(value.map({ true }, { false }))
buffer.writeString(value.map({ it.toString() }, { it.toString() }))
}
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
buffer.writeString(key)
buffer.writeString(value.asString())
}
}
fun decode(buffer: PacketByteBuf) {
names = buffer.readList { buffer.readText().copy() }.toMutableList()
hitbox = EntityDimensions(buffer.readFloat(), buffer.readFloat(), buffer.readBoolean())
battleConfiguration = NPCBattleConfiguration()
battleConfiguration.decode(buffer)
interaction = buffer.readNullable {
if (buffer.readBoolean()) {
Either.left(Identifier(buffer.readString()))
} else {
Either.right(buffer.readString().asExpressionLike())
}
}
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
val key = buffer.readString()
val value = buffer.readString()
if (value.toDoubleOrNull() != null) {
return@readMapK key to DoubleValue(value.toDouble())
} else {
return@readMapK key to StringValue(value)
}
}
}
}
@@ -0,0 +1,128 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.SleepDepth
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.net.messages.client.data.NPCRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.block.Block
import net.minecraft.entity.EntityDimensions
import net.minecraft.item.Item
import net.minecraft.nbt.NbtCompound
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.world.biome.Biome
object NPCClasses : JsonDataRegistry<NPCClass> {
override val id = cobblemonResource("npc")
override val type = ResourceType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(NbtCompound::class.java, NbtCompoundAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Either::class.java, Identifier::class.java, ExpressionLike::class.java).type, NPCScriptAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
override val typeToken: TypeToken<NPCClass> = TypeToken.get(NPCClass::class.java)
override val resourcePath = "npcs"
override val observable = SimpleObservable<NPCClasses>()
private val npcClassesByIdentifier = hashMapOf<Identifier, NPCClass>()
val classes: Collection<NPCClass>
get() = this.npcClassesByIdentifier.values
init {
// NPC additions would be useful
// SpeciesAdditions.observable.subscribe {
// this.species.forEach(Species::initialize)
// this.species.forEach(Species::resolveEvolutionMoves)
// }
}
/**
* Finds an NPC class by the pathname of their [Identifier].
* This method exists for the convenience of finding Cobble default NPC classes.
* This uses [getByIdentifier] using the [Cobblemon.MODID] as the namespace and the [name] as the path.
*
* @param name The path of the NPC class asset.
* @return The [NPCClass] if existing.
*/
fun getByName(name: String) = this.getByIdentifier(cobblemonResource(name))
/**
* Finds an [NPCClass] by its unique [Identifier].
*
* @param identifier The unique [NPCClass.resourceIdentifier] of the [NPCClass].
* @return The [NPCClass] if existing.
*/
fun getByIdentifier(identifier: Identifier) = this.npcClassesByIdentifier[identifier]
/**
* Counts the currently loaded NPC classes.
*
* @return The loaded NPC class amount.
*/
fun count() = this.npcClassesByIdentifier.size
/**
* Picks a random [NPCClass].
*
* @throws [NoSuchElementException] if there are no NPC classes loaded.
*
* @return A randomly selected [Species].
*/
fun random(): NPCClass = this.npcClassesByIdentifier.values.random()
override fun reload(data: Map<Identifier, NPCClass>) {
this.npcClassesByIdentifier.clear()
data.forEach { (identifier, species) ->
species.resourceIdentifier = identifier
this.npcClassesByIdentifier[identifier] = species
}
}
override fun sync(player: ServerPlayerEntity) {
NPCRegistrySyncPacket(npcClassesByIdentifier.values.toList()).sendToPlayer(player)
}
}
@@ -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.api.npc
import com.cobblemon.mod.common.api.npc.configuration.NPCBattleConfiguration
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* An NPC configuration is like a snapshot of an NPC setup. It can be taken from an existing NPC and loaded onto
* others. Useful as a backup mechanism or as shorthand for copying an NPC's configuration without building an
* entire class for them.
*
* @author Hiroku
* @since August 16th, 2023
*/
class NPCConfiguration {
var battle: NPCBattleConfiguration? = null
fun applyTo(npc: NPCEntity) {
}
fun loadFrom(npc: NPCEntity) {
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.JsonElement
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
/**
* A provider of a party for battling the NPC. Completely custom party providers will only display
* as text labels in any GUIs.
*
* @author Hiroku
* @since August 16th, 2023
*/
interface NPCPartyProvider {
companion object {
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
SimplePartyProvider.TYPE to { SimplePartyProvider() }
)
}
val type: String
fun provide(npc: NPCEntity, challengers: List<ServerPlayerEntity>): PartyStore
fun encode(buffer: PacketByteBuf)
fun decode(buffer: PacketByteBuf)
fun saveToNBT(nbt: NbtCompound)
fun loadFromNBT(nbt: NbtCompound)
fun loadFromJSON(json: JsonElement)
}
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc
import net.minecraft.util.Identifier
class NPCPreset {
lateinit var id: Identifier
var aspects: Set<String>? = null
var party: NPCPartyProvider? = null
// var mergeMode = MergeMode.KEEP
}
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc.configuration
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.util.DataKeys
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
class NPCBattleConfiguration {
var canChallenge = false
var party: NPCPartyProvider? = null
var simultaneousBattles = false
var healAfterwards = true
fun encode(buffer: PacketByteBuf) {
buffer.writeBoolean(canChallenge)
buffer.writeBoolean(simultaneousBattles)
buffer.writeBoolean(healAfterwards)
buffer.writeNullable(party) { _, provider ->
buffer.writeString(provider.type)
provider.encode(buffer)
}
}
fun decode(buffer: PacketByteBuf) {
canChallenge = buffer.readBoolean()
simultaneousBattles = buffer.readBoolean()
healAfterwards = buffer.readBoolean()
party = buffer.readNullable {
val type = buffer.readString()
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
providerBuilder(type)
}
}
fun saveToNBT(nbt: NbtCompound) {
nbt.putBoolean(DataKeys.NPC_CAN_CHALLENGE, canChallenge)
nbt.putBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES, simultaneousBattles)
nbt.putBoolean(DataKeys.NPC_HEAL_AFTERWARDS, healAfterwards)
val party = party
if (party != null) {
val partyNBT = NbtCompound()
partyNBT.putString(DataKeys.NPC_PARTY_TYPE, party.type)
party.saveToNBT(partyNBT)
nbt.put(DataKeys.NPC_PARTY, partyNBT)
}
}
fun loadFromNBT(nbt: NbtCompound) {
canChallenge = nbt.getBoolean(DataKeys.NPC_CAN_CHALLENGE)
simultaneousBattles = nbt.getBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES)
healAfterwards = nbt.getBoolean(DataKeys.NPC_HEAL_AFTERWARDS)
val partyNBT = nbt.getCompound(DataKeys.NPC_PARTY)
if (!partyNBT.isEmpty) {
val type = partyNBT.getString(DataKeys.NPC_PARTY_TYPE)
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
party = providerBuilder(type).also { it.loadFromNBT(partyNBT) }
}
}
}
@@ -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.npc.configuration
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
class NPCBehaviourConfiguration {
var canBeHurt = true
fun encode(buffer: PacketByteBuf) {
}
fun decode(buffer: PacketByteBuf) {
}
fun saveToNBT(nbt: NbtCompound) {
}
fun loadFromNBT(nbt: NbtCompound) {
}
}
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc.configuration
import net.minecraft.server.network.ServerPlayerEntity
/**
* A type of interaction handler for when a player right clicks the NPC.
*
* @author Hiroku
* @since August 19th, 2023
*/
interface NPCInteractConfiguration {
val type: String
fun interact(player: ServerPlayerEntity): Boolean
}
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc.partyproviders
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
class SimplePartyProvider : NPCPartyProvider {
companion object {
const val TYPE = "simple"
}
@Transient
override val type = TYPE
val pokemon = mutableListOf<PokemonProperties>()
override fun encode(buffer: PacketByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, pokemon.size)
for (pokemon in this.pokemon) {
buffer.writeString(pokemon.originalString)
}
}
override fun decode(buffer: PacketByteBuf) {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
pokemon.add(PokemonProperties.parse(buffer.readString()))
}
}
override fun saveToNBT(nbt: NbtCompound) {
for ((index, pokemon) in this.pokemon.withIndex()) {
nbt.putString(DataKeys.NPC_PARTY_POKEMON + index, pokemon.originalString)
}
}
override fun loadFromNBT(nbt: NbtCompound) {
var index = 0
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
this.pokemon.add(PokemonProperties.parse(nbt.getString(DataKeys.POKEMON_PROPERTIES + index)))
index++
}
}
override fun loadFromJSON(json: JsonElement) {
json as JsonObject
if (json.has(DataKeys.NPC_PARTY_POKEMON.lowercase())) {
json.get(DataKeys.NPC_PARTY_POKEMON.lowercase()).asJsonArray.forEach { pokemon.add(PokemonProperties.parse(it.asString)) }
}
}
override fun provide(npc: NPCEntity, challengers: List<ServerPlayerEntity>): PartyStore {
return NPCPartyStore(npc).apply {
for (properties in pokemon) {
add(properties.create())
}
}
}
}
@@ -70,6 +70,8 @@ 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 NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -168,7 +168,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
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.typeBoosting(2F, ElementalTypes.WATER))
createDefault("lure_ball", CatchRateModifiers.LURE)
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
createDefault("love_ball", CatchRateModifiers.LOVE)
createDefault("friend_ball", effects = listOf(CaptureEffects.friendshipSetter(150)))
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokeball.catching.modifiers
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -121,6 +122,18 @@ object CatchRateModifiers {
1F
}
/**
* Used by [PokeBalls.LURE_BALL].
* Checks if the entity has an aspect for being spawned from fishing.
* If yes boosts the catch rate by *4
*/
val LURE = WorldStateModifier { _, entity ->
if (FishingSpawnCause.FISHED_ASPECT in entity.aspects)
4f
else
1F
}
/**
* Used by [PokeBalls.HEAVY_BALL].
* The base implementation of the heavy ball modifier mechanics.
@@ -59,9 +59,9 @@ open class PokemonProperties {
val props = PokemonProperties()
props.originalString = string
val keyPairs = string.splitMap(delimiter, assigner)
props.customProperties = CustomPokemonProperty.properties.mapNotNull { property ->
val matchedKeyPair = keyPairs.find { it.first.lowercase() in property.keys }
if (matchedKeyPair == null) {
props.customProperties = CustomPokemonProperty.properties.flatMap { property ->
val matchedKeyPairs = keyPairs.filter { it.first.lowercase() in property.keys }
if (matchedKeyPairs.isEmpty()) {
if (!property.needsKey) {
var savedProperty: CustomPokemonProperty? = null
val keyPair = keyPairs.find { keyPair ->
@@ -71,13 +71,20 @@ open class PokemonProperties {
if (keyPair != null) {
keyPairs.remove(keyPair)
}
return@mapNotNull savedProperty
return@flatMap savedProperty?.let { listOf(it) } ?: emptyList()
} else {
return@mapNotNull null
return@flatMap emptyList()
}
} else {
keyPairs.remove(matchedKeyPair)
return@mapNotNull property.fromString(matchedKeyPair.second)
val properties = mutableListOf<CustomPokemonProperty>()
for ((customKey, customValue) in matchedKeyPairs) {
val property = property.fromString(customValue)
keyPairs.remove(customKey to customValue)
if (property != null) {
properties.add(property)
}
}
return@flatMap properties
}
}.toMutableList()
props.gender = Gender.values().toList().parsePropertyOfCollection(keyPairs, listOf("gender"), labelsOptional = true) { it.name.lowercase() }
@@ -96,7 +96,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect>(Registries::STATUS_EFFECT))
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
@@ -51,6 +51,7 @@ enum class Stats(override val identifier: Identifier, override val displayName:
/** Gets the [Stat] from the respective Showdown id. */
fun getStat(statKey: String) = when(statKey) {
"hp" -> HP
"atk" -> ATTACK
"def" -> DEFENCE
"spa" -> SPECIAL_ATTACK
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
/**
* An interface for which an implementation must provide a [SchedulingTracker] so that the standard scheduling
* functions can be executed.
@@ -52,5 +54,13 @@ interface Schedulable {
}
}
fun delayedFuture(seconds: Float): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
after(seconds = seconds) {
future.complete(Unit)
}
return future
}
fun taskBuilder(): ScheduledTask.Builder = ScheduledTask.Builder().tracker(schedulingTracker)
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
import com.cobblemon.mod.common.util.runOnServer
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
@JvmOverloads
@@ -45,4 +46,12 @@ fun lerp(seconds: Float = 0F, serverThread: Boolean = false, action: (Float) ->
fun lerpOnServer(seconds: Float = 0F, action: (Float) -> Unit) = ServerTaskTracker.lerp(seconds = seconds, action = action)
@JvmOverloads
fun lerpOnClient(seconds: Float = 0F, action: (Float) -> Unit) = ClientTaskTracker.lerp(seconds = seconds, action = action)
fun taskBuilder() = ScheduledTask.Builder()
fun taskBuilder() = ScheduledTask.Builder()
fun delayedFuture(seconds: Float): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
afterOnServer(seconds = seconds) {
future.complete(Unit)
}
return future
}
@@ -0,0 +1,69 @@
/*
* 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.scripting
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
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.ScriptRegistrySyncPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object CobblemonScripts : DataRegistry {
const val MOLANG_EXTENSION = ".molang"
override val id = cobblemonResource("molang")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<CobblemonScripts>()
val clientScripts = mutableMapOf<Identifier, ExpressionLike>()
val scripts = mutableMapOf<Identifier, ExpressionLike>()
override fun reload(manager: ResourceManager) {
manager.findResources("molang") { path -> path.endsWith(MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
try {
val expression = reader.readText().asExpressionLike()
if (identifier.path.startsWith("molang/client/")) {
clientScripts[resolvedIdentifier] = expression
} else {
scripts[resolvedIdentifier] = expression
}
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script: $identifier", exception)
}
}
}
}
Cobblemon.LOGGER.info("Loaded ${scripts.size} server scripts and ${clientScripts.size} client scripts")
observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(ScriptRegistrySyncPacket(clientScripts.entries))
}
fun run(identifier: Identifier, runtime: MoLangRuntime): MoValue? {
return scripts[identifier]?.resolve(runtime)
}
}
@@ -12,11 +12,13 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.FishingSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
@@ -29,9 +31,11 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawnin
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SubmergedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
@@ -83,6 +87,7 @@ object BestSpawner {
var config = BestSpawnerConfig()
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun loadConfig() {
LOGGER.info("Starting the Best Spawner...")
@@ -91,6 +96,7 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -104,10 +110,12 @@ object BestSpawner {
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
config = BestSpawnerConfig.load()
@@ -118,5 +126,6 @@ object BestSpawner {
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.spawning
import com.bedrockk.molang.Expression
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.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
@@ -24,6 +25,7 @@ import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawningCo
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailSelectorAdapter
import com.cobblemon.mod.common.util.adapters.SpawnRuleComponentAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
@@ -50,6 +52,7 @@ object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
.registerTypeAdapter(SpawningContextSelector::class.java, SpawningContextSelectorAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(Text::class.java, TextAdapter)
.create()
@@ -8,11 +8,22 @@
package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.util.server
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityType
open class SpawnCause(
val spawner: Spawner,
val bucket: SpawnBucket,
val entity: Entity? = null
)
entity: Entity? = null
): SpawningInfluence {
val entityWorldId = entity?.world?.registryKey
val entityId = entity?.id
val entityUUID = entity?.uuid
val entityType = entity?.type
val entity: Entity?
get() = if (entityType == EntityType.PLAYER) server()?.playerManager?.getPlayer(entityUUID) else server()?.getWorld(entityWorldId)?.getEntityById(entityId!!)
}
@@ -0,0 +1,44 @@
/*
* 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.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
import net.minecraft.item.Item
/**
* A spawning condition that applies to [FishingSpawningContext]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
override fun contextClass() = FishingSpawningContext::class.java
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
return false
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
}
companion object {
const val NAME = "fishing"
}
}
@@ -11,12 +11,17 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.util.Merger
import com.cobblemon.mod.common.util.math.orMax
import com.cobblemon.mod.common.util.math.orMin
import com.mojang.datafixers.util.Either
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.world.biome.Biome
@@ -56,6 +61,10 @@ abstract class SpawningCondition<T : SpawningContext> {
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var structures: MutableList<Either<Identifier, TagKey<Structure>>>? = null
var minLureLevel: Int? = null
var maxLureLevel: Int? = null
var bait: String? = null
var rodType: String? = null
@Transient
var appendages = mutableListOf<AppendageCondition>()
@@ -108,8 +117,34 @@ abstract class SpawningCondition<T : SpawningContext> {
}
) {
return false
} else if (minLureLevel != null && ctx is FishingSpawningContext) { // check for the lureLevel of the rod
val pokerodStack = (ctx as FishingSpawningContext).rodStack
if (EnchantmentHelper.getLure(pokerodStack) < minLureLevel!!)
return false
if (maxLureLevel != null && EnchantmentHelper.getLure(pokerodStack) > maxLureLevel!!)
return false
} else if (bait != null && ctx is FishingSpawningContext) { // check for the bait on the bobber
val pokerodBait = (ctx as FishingSpawningContext).rodBait
if (Registries.ITEM.getId(PokerodItem.getBait(pokerodBait)?.item).path != bait)
return false
} else if (rodType != null && ctx is FishingSpawningContext) { // check for the type of pokerod being used
val pokerodItem = (ctx as FishingSpawningContext).rodItem
if (pokerodItem?.pokeRodId?.path != rodType)
return false
}
/*else if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
val pokerodItem = (ctx as FishingSpawningContext).rodItem
// todo check if the EV yield of the berry matches the bait EV attract maybe?
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
return false
}*/
return true
}
@@ -56,9 +56,10 @@ interface AreaContextResolver {
if (context != null) {
contexts.add(context)
// The position BlockPos has been used in a context, editing the same one
// will cause entities to spawn at the wrong location (buried in walls, usually)
// I made it so that our context calculators specifically take a copy of the
// BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// will cause entities to spawn at the wrong location (buried in walls, usually).
// I made it so that built-in context calculators explicitly take a copy of the
// BlockPos but it'd still be exposed in custom contexts so fixing it here too so
// custom context calculators don't have to remember to do it. - Hiroku
pos = BlockPos.Mutable(1, 2, 3)
input.position = pos
}
@@ -76,4 +77,51 @@ interface AreaContextResolver {
return contexts
}
// fun resolveFishing(
// spawner: Spawner,
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
// slice: WorldSlice
// ): List<AreaSpawningContext> {
// var pos = BlockPos.Mutable(1, 2, 3)
// val input = AreaSpawningInput(spawner, pos, slice)
// val contexts = mutableListOf<AreaSpawningContext>()
//
// var x = slice.baseX
// var y = slice.baseY
// var z = slice.baseZ
//
// while (x < slice.baseX + slice.length) {
// while (y < slice.baseY + slice.height) {
// while (z < slice.baseZ + slice.width) {
// pos.set(x, y, z)
// val vec = pos.toVec3d()
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
// val fittedContextCalculator = contextCalculators
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
// if (fittedContextCalculator != null) {
// val context = fittedContextCalculator.calculate(input)
// if (context != null) {
// contexts.add(context)
// // The position BlockPos has been used in a context, editing the same one
// // will cause entities to spawn at the wrong location (buried in walls, usually)
// // I made it so that our context calculators specifically take a copy of the
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// pos = BlockPos.Mutable(1, 2, 3)
// input.position = pos
// }
// }
// }
// z++
// }
// y++
// z = slice.baseZ
// }
// x++
// y = slice.baseY
// z = slice.baseZ
// }
//
// return contexts
// }
}
@@ -0,0 +1,45 @@
/*
* 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.spawning.context
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.util.getBlockStates
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
/**
* A spawning context that was generated for a fishing action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningContext(
cause: FishingSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(Box.of(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.mapNotNull { it.block }.distinct() }
val rodStack = cause.rodStack
val rodItem = cause.rodItem
val rodBait = cause.bait
}
@@ -68,7 +68,7 @@ abstract class SpawningContext {
abstract val skyLight: Int
/** Whether or not the sky is visible at this location. */
abstract val canSeeSky: Boolean
/** A list of [SpawningInfluence]s that apply due to this specific context. */
/** A list of [SpawningInfluence]s that apply due to this specific context. This generally shouldn't be consumed; use [getAllInfluences].*/
abstract val influences: MutableList<SpawningInfluence>
/** Gets a cache of structures by block coordinates, grouped by chunk. */
abstract fun getStructureCache(pos: BlockPos): StructureChunkCache
@@ -147,7 +147,7 @@ abstract class SpawningContext {
*/
open fun preFilter(detail: SpawnDetail): Boolean {
/** Returns true if none of the influences.affectSpawnable return false */
return influences.none { !it.affectSpawnable(detail, this) }
return !anyForInfluences { !it.affectSpawnable(detail, this) }
}
/**
@@ -157,14 +157,12 @@ abstract class SpawningContext {
open fun postFilter(detail: SpawnDetail): Boolean = true
open fun afterSpawn(entity: Entity) {
influences.forEach { it.affectSpawn(entity) }
applyInfluences { it.affectSpawn(entity) }
}
open fun getWeight(detail: SpawnDetail): Float {
var weight = detail.weight
for (influence in influences + detail.weightMultipliers) {
weight = influence.affectWeight(detail, this, weight)
}
applyInfluences(extraInfluences = detail.weightMultipliers) { weight = it.affectWeight(detail, this, weight) }
return weight
}
@@ -184,4 +182,34 @@ abstract class SpawningContext {
structCompiled = true
return struct
}
/**
* Gets all influences that apply to this context, including the cause, and does something with them.
*
* Technically we could simple make a list of influences + cause and run that, but influences MUST be able
* to run extremely frequently in performance-critical code, so we don't want to have to allocate a list.
*/
fun applyInfluences(extraInfluences: List<SpawningInfluence>? = null, usage: (SpawningInfluence) -> Unit) {
influences.forEach(usage)
usage(cause)
extraInfluences?.forEach(usage)
}
/**
* Iterates through every operable influence to find if any match the predicate.
*/
fun anyForInfluences(extraInfluences: List<SpawningInfluence>? = null, usage: (SpawningInfluence) -> Boolean): Boolean {
if (influences.any(usage)) {
return true
}
if (usage(cause)) {
return true
}
extraInfluences?.forEach {
if (usage(it)) {
return true
}
}
return false
}
}
@@ -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.api.spawning.context
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawning context relating to triggered spawning. Uses a single-point structure cache which is
* the only contribution of this implementation.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class TriggerSpawningContext(
override val cause: SpawnCause,
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>
) : SpawningContext() {
val triggerStructureCache = StructureChunkCache()
override fun getStructureCache(pos: BlockPos) = triggerStructureCache
}
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* A [SpawnAction] for creating [NPCEntity]s.
*
* @author Hiroku
* @since October 8th, 2023
*/
class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SpawnAction<NPCEntity>(ctx, detail) {
override fun run(): NPCEntity {
val npc = NPCEntity(ctx.world)
npc.npc = detail.npcClass
npc.appliedAspects.addAll(detail.aspects)
return npc
}
}
@@ -0,0 +1,37 @@
/*
* 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.spawning.detail
import com.cobblemon.mod.common.api.npc.NPCClass
import com.cobblemon.mod.common.api.npc.NPCConfiguration
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.annotations.SerializedName
/**
* A [SpawnDetail] describing an [NPCEntity] spawn.
*
* @author Hiroku
* @since October 8th, 2023
*/
class NPCSpawnDetail : SpawnDetail() {
companion object {
val TYPE = "npc"
val blankClass = NPCClass()
}
override val type = TYPE
@SerializedName(value = "npcClass", alternate = ["class", "npc"])
val npcClass: NPCClass = blankClass
val configuration: NPCConfiguration? = null
val aspects: Set<String> = emptySet()
override fun doSpawn(ctx: SpawningContext) = NPCSpawnAction(ctx, this)
}
@@ -45,6 +45,7 @@ class PokemonSpawnAction(
null
}?.createStack(ctx)
val entity = props.createEntity(ctx.world)
entity.spawnCause = ctx.cause
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(ctx.world, ctx.position))
if (heldItem != null) {
entity.pokemon.swapHeldItem(heldItem)
@@ -34,15 +34,19 @@ abstract class SpawnAction<R>(
/**
* Runs the spawn action and processes any non-null result.
*/
open fun complete(): Boolean {
ctx.influences.forEach { it.affectAction(this) }
open fun complete(): R? {
if (future.isDone) {
return null
}
ctx.applyInfluences { it.affectAction(this) }
val result = run()
return if (result != null) {
if (result != null) {
future.complete(result)
true
} else {
future.completeExceptionally(Exception("Nothing was spawned."))
false
}
return result
}
}
@@ -0,0 +1,71 @@
/*
* 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.spawning.fishing
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.pokemon.Species
import net.minecraft.entity.Entity
import net.minecraft.item.FishingRodItem
import net.minecraft.item.ItemStack
/**
* A spawning cause that is embellished with fishing information. Could probably also
* have the bobber entity or something.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?,
val rodStack: ItemStack
) : SpawnCause(spawner, bucket, entity) {
companion object {
const val FISHED_ASPECT = "fished"
}
val rodItem = rodStack.item as? PokerodItem
val bait = PokerodItem.getBait(rodStack)
override fun affectSpawn(entity: Entity) {
super.affectSpawn(entity)
if (entity is PokemonEntity) {
entity.pokemon.forcedAspects += FISHED_ASPECT
}
}
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
if (!ItemStack.areItemsEqual(ItemStack.EMPTY, bait) && bait != null ){
val bait = FishingBaits.getFromItemStack(bait)
if (detail is PokemonSpawnDetail) {
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
val baitEVEffect = bait!!.effects.firstOrNull() { it.type == FishingBait.Effects.EV && detailSpecies?.evYield?.get(Stats.getStat(it.subcategory?.path.toString()))!! > 0 }
if(detailSpecies != null && baitEVEffect != null) {
return super.affectWeight(detail, ctx, weight * baitEVEffect.value.toFloat())
}
}
}
return super.affectWeight(detail, ctx, weight)
}
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class FishingSpawner(
override val name: String = "fishing",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
override fun parseContext(cause: FishingSpawnCause, world: ServerWorld, pos: BlockPos): FishingSpawningContext? {
// Maybe confirm that it's water we're fishing in?
val ctx = FishingSpawningContext(
cause = cause,
world = world,
pos = pos,
influences = influences
)
return ctx
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
@@ -49,7 +50,7 @@ class LocationRuleCalculator : SpawnRuleComponent {
@Transient
private lateinit var reusableDimensionTypeValue: ObjectValue<RegistryEntry<DimensionType>>
val allow = "true".asExpression()
val allow = "true".asExpressionLike()
override fun isAllowedPosition(
world: ServerWorld,
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawningContextSe
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveFloat
/**
@@ -30,7 +31,7 @@ import com.cobblemon.mod.common.util.resolveFloat
class WeightTweakRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val weight: Expression = "v.weight".asExpression()
val weight = "v.weight".asExpressionLike()
@Transient
val runtime = MoLangRuntime().setup()
@@ -12,12 +12,13 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawnDetailSelector : SpawnDetailSelector {
@Transient
val runtime = MoLangRuntime().setup()
var expression = "true".asExpression()
var expression = "true".asExpressionLike()
override fun selects(spawnDetail: SpawnDetail): Boolean {
runtime.environment.setSimpleVariable("spawn", spawnDetail.struct)
@@ -12,12 +12,13 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawningContextSelector : SpawningContextSelector {
@Transient
val runtime = MoLangRuntime().setup()
var expression = "true".asExpression()
var expression = "true".asExpressionLike()
override fun selects(ctx: SpawningContext): Boolean {
runtime.environment.setSimpleVariable("context", ctx.getOrSetupStruct())
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A type of spawner that occurs at a single point and provides its own means of generating a
* context from a particular cause and position. The trigger spawner must be directly called to
* spawn things (almost as if it was being triggered) as opposed to being scheduled on a ticker.
*
* @author Hiroku
* @since February 3rd, 2024
*/
abstract class TriggerSpawner<T : SpawnCause>(override val name: String, var spawns: SpawnPool) : Spawner {
private var selector: SpawningSelector = FlatContextWeightedSelector()
override val influences = mutableListOf<SpawningInfluence>()
open fun run(cause: T, world: ServerWorld, pos: BlockPos): CompletableFuture<*>? {
val context = parseContext(cause, world, pos) ?: return null
selector.select(this, listOf(context))?.let { (_, spawn) ->
val action = spawn.doSpawn(context)
action.complete()
return action.future
}
return null
}
/** Parses a context, if possible, from the given cause and position. */
abstract fun parseContext(cause: T, world: ServerWorld, pos: BlockPos): SpawningContext?
override fun canSpawn() = true
override fun getSpawningSelector() = selector
override fun setSpawningSelector(selector: SpawningSelector) { this.selector = selector }
override fun getSpawnPool() = spawns
override fun setSpawnPool(spawnPool: SpawnPool) { spawns = spawnPool }
}
@@ -0,0 +1,15 @@
/*
* 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.storage.party
import com.cobblemon.mod.common.entity.npc.NPCEntity
class NPCPartyStore(val npc: NPCEntity) : PartyStore(npc.uuid) {
val isLiving: Boolean get() = npc.isLiving
}
@@ -99,7 +99,7 @@ open class PCStore(
this.lockedSize = lockNewSize
if (boxes.size > newSize) {
// reduce
val slicedBoxes = boxes.slice(boxes.size until newSize)
val slicedBoxes = boxes.slice(newSize until boxes.size)
boxes.removeAll(slicedBoxes)
slicedBoxes.flatMap { it.asIterable() }.forEach(overflowHandler)
} else {
@@ -9,19 +9,20 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getBattleTheme
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import java.util.Optional
import com.cobblemon.mod.common.util.update
import java.util.UUID
import net.minecraft.entity.Entity
import net.minecraft.server.network.ServerPlayerEntity
@@ -63,6 +64,7 @@ object BattleBuilder {
}
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(player1Actor),
@@ -70,7 +72,9 @@ object BattleBuilder {
).ifSuccessful {
player1Actor.battleTheme = player2.getBattleTheme()
player2Actor.battleTheme = player1.getBattleTheme()
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
@@ -129,6 +133,7 @@ object BattleBuilder {
}
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
@@ -138,7 +143,72 @@ object BattleBuilder {
pokemonEntity.battleId = it.battleId
}
playerActor.battleTheme = pokemonEntity.getBattleTheme()
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
}
/**
* Attempts to create a PvE battle against the given Pokémon.
*
* @param player The player battling the wild Pokémon.
* @param pokemonEntity The Pokémon to battle.
* @param leadingPokemon The Pokémon in the player's party to send out first. If null, it uses the first in the party.
* @param battleFormat The format to use for the battle. By default it is [BattleFormat.GEN_9_SINGLES].
* @param cloneParties Whether the player's party should be cloned so that damage will not affect their party afterwards. Defaults to false.
* @param healFirst Whether the player's Pokémon should be healed before the battle starts. Defaults to false.
* @param fleeDistance How far away the player must get to flee the Pokémon. If the value is -1, it cannot be fled.
* @param party The party of the player to use for the battle. This does not need to be their actual party. Defaults to it though.
*/
@JvmOverloads
fun pvn(
player: ServerPlayerEntity,
npcEntity: NPCEntity,
leadingPokemon: UUID? = null,
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
cloneParties: Boolean = false,
healFirst: Boolean = false,
party: PartyStore = player.party()
): BattleStartResult {
val playerTeam = party.toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemon = leadingPokemon)
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
val party = npcEntity.getBattleConfiguration().party?.provide(npcEntity, listOf(player)) ?: TODO("Deal with this issue!")
val npcActor = NPCBattleActor(npcEntity, party)
val errors = ErroredBattleStart()
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
player = player,
requiredCount = battleFormat.battleType.slotsPerActor,
hadCount = playerActor.pokemonList.size
)
}
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
errors.participantErrors[playerActor] += BattleStartError.alreadyInBattle(playerActor)
}
// if (npcEntity.battleIds.get().isPresent) {
// errors.participantErrors[npcActor] += BattleStartError.alreadyInBattle(npcActor)
// }
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
side2 = BattleSide(npcActor)
).ifSuccessful { battle ->
// TODO NPC battle themes
// playerActor.battleTheme = pokemonEntity.getBattleTheme()
npcEntity.dataTracker.update(NPCEntity.BATTLE_IDS) { it + battle.battleId }
result = SuccessfulBattleStart(battle)
}
result
} else {
errors
}
@@ -11,13 +11,17 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
import com.cobblemon.mod.common.util.battleLang
import java.util.concurrent.CompletableFuture
/**
* Format: |faint|POKEMON
@@ -32,18 +36,28 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
override fun invoke(battle: PokemonBattle) {
battle.dispatchWaiting(waitTime) {
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchWaiting
battle.dispatchFuture {
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
battle.sendUpdate(BattleFaintPacket(pnx))
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.sendUpdate()
val actor = pokemon.actor
pokemon.effectedPokemon.currentHealth = 0
val preamble = if (actor is EntityBackedBattleActor<*>) {
(actor.entity as? PokemonSender)?.let { sender -> pokemon.entity?.recallWithAnimation()}
} else {
null
} ?: delayedFuture(seconds = waitTime)
val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, faintingPokemon, context))
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
faintingPokemon.contextManager.add(context)
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
battle.majorBattleActions[faintingPokemon.uuid] = message
preamble.thenAccept {
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.sendUpdate()
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
faintingPokemon.contextManager.add(context)
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
battle.majorBattleActions[faintingPokemon.uuid] = message
}
}
battle.dispatchWaiting(0.5F) {
val faintMessage = battleLang("fainted", faintingPokemon.getName()).red()
@@ -13,7 +13,6 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.Moves
@@ -29,17 +29,11 @@ class FossilAnalyzerBlockEntity(
class FossilAnalyzerInventory(val analyzerEntity: FossilAnalyzerBlockEntity) : SidedInventory {
override fun clear() {
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
fossilMultiblockStructure.fossilInventory.clear()
}
TODO("Not yet implemented")
}
override fun size(): Int {
if (analyzerEntity.multiblockStructure != null) {
return 3
}
return 0
TODO("Not yet implemented")
}
override fun isEmpty(): Boolean {
@@ -47,37 +41,19 @@ class FossilAnalyzerBlockEntity(
}
override fun getStack(slot: Int): ItemStack {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
override fun removeStack(slot: Int): ItemStack {
if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory.removeAt(slot)
}
}
return ItemStack.EMPTY
}
override fun setStack(slot: Int, stack: ItemStack) {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
if (analyzerEntity.multiblockStructure != null) {
val struct = analyzerEntity.multiblockStructure as FossilMultiblockStructure
analyzerEntity.world?.let {
struct.insertFossil(stack, it)
@@ -96,9 +72,6 @@ class FossilAnalyzerBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
return intArrayOf(0,1,2)
}
return IntArray(1)
}
@@ -14,6 +14,7 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.client.MinecraftClient
import net.minecraft.client.sound.PositionedSoundInstance
@@ -31,6 +32,13 @@ object ClientMoLangFunctions {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, pitch))
}
},
"is_time" to java.util.function.Function { params ->
val time = (MinecraftClient.getInstance().world?.timeOfDay ?: 0) % 24000
val min = params.getInt(0)
val max = params.getInt(1)
time in min..max
},
"say" to java.util.function.Function { params -> MinecraftClient.getInstance().player?.sendMessage(params.getString(0).text()) ?: Unit },
)
fun MoLangRuntime.setupClient(): MoLangRuntime {
@@ -10,12 +10,12 @@ package com.cobblemon.mod.common.client
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonClientImplementation
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.client.battle.ClientBattle
@@ -23,12 +23,13 @@ import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
@@ -36,8 +37,13 @@ import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController
@@ -47,9 +53,6 @@ import com.cobblemon.mod.common.client.trade.ClientTrade
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asTranslated
@@ -264,8 +267,14 @@ object CobblemonClient {
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
LOGGER.info("Registering Boat with Chest renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
LOGGER.info("Registering Generic Bedrock renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering NPC renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.NPC, ::NPCRenderer)
LOGGER.info("Registering Generic Bedrock Entity renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering PokeRod Bobber renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.POKE_BOBBER) { ctx -> PokeBobberEntityRenderer(ctx) }
}
fun reloadCodedAssets(resourceManager: ResourceManager) {
@@ -273,10 +282,11 @@ object CobblemonClient {
BedrockParticleEffectRepository.loadEffects(resourceManager)
BedrockAnimationRepository.loadAnimations(
resourceManager = resourceManager,
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories + NPCModelRepository.animationDirectories
)
PokemonModelRepository.reload(resourceManager)
PokeBallModelRepository.reload(resourceManager)
NPCModelRepository.reload(resourceManager)
BerryModelRepository.reload(resourceManager)
FossilModelRepository.reload(resourceManager)
BlockEntityModelRepository.reload(resourceManager)
@@ -40,11 +40,7 @@ class EmptyPokeBallClientDelegate : PokeBallPoseableState(), EntitySideDelegate<
this.currentEntity = entity
age = entity.age
initSubscriptions()
this.runtime.environment.getQueryStruct().addFunctions(mapOf(
"pokeball_type" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokeBall.name.toString())
}
))
this.runtime.environment.getQueryStruct().addFunctions(getEntity().struct.functions)
}
override fun tick(entity: EmptyPokeBallEntity) {
@@ -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.client.entity
import com.cobblemon.mod.common.api.entity.NPCSideDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.entity.npc.NPCEntity
class NPCClientDelegate : PoseableEntityState<NPCEntity>(), NPCSideDelegate {
lateinit var npcEntity: NPCEntity
override val schedulingTracker
get() = npcEntity.schedulingTracker
override fun initialize(entity: NPCEntity) {
this.npcEntity = entity
this.age = entity.age
}
override fun tick(entity: NPCEntity) {
super.tick(entity)
updateLocatorPosition(entity.pos)
incrementAge(entity)
}
override fun getEntity() = npcEntity
override fun updatePartialTicks(partialTicks: Float) {
this.currentPartialTicks = partialTicks
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.entity
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
@@ -18,6 +19,7 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnClient
import com.cobblemon.mod.common.api.scheduling.lerpOnClient
import com.cobblemon.mod.common.client.ClientMoLangFunctions
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
@@ -50,6 +52,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
override val schedulingTracker: SchedulingTracker
get() = currentEntity.schedulingTracker
lateinit var currentEntity: PokemonEntity
var phaseTarget: Entity? = null
var entityScaleModifier = 1F
@@ -117,7 +120,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
currentEntity.isInvisible = true
ballDone = false
var soundPos = currentEntity.pos
currentEntity.pokemon.getOwnerUUID()?.let{
currentEntity.ownerUuid?.let{
currentEntity.world.getPlayerByUuid(it)?.let {
val offset = it.pos.subtract(currentEntity.pos.add(0.0, 2.0 - (ballOffset.toDouble()/10f), 0.0)).normalize().multiply(-ease(ballOffset.toDouble()))
with(it.pos.subtract(currentEntity.pos)) {
@@ -131,7 +134,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
val client = MinecraftClient.getInstance()
val sound = MovingSoundInstance(SoundEvent.of(CobblemonSounds.POKE_BALL_TRAIL.id), SoundCategory.PLAYERS, { sendOutPosition?.add(sendOutOffset) }, 0.1f, 1f, false, 20, 0)
if(!playedThrowingSound){
if (!playedThrowingSound){
client.soundManager.play(sound)
playedThrowingSound = true
}
@@ -145,33 +148,31 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
}
// Display ball-specific particles
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.lowercase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
currentEntity.ownerUuid?.let {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
ballsendsparkle?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
}
}
}
@@ -250,6 +251,13 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
pokemon.isClient = true
}
override fun addToStruct(struct: QueryStruct) {
super.addToStruct(struct)
struct.addFunctions(functions.functions)
struct.addFunctions(ClientMoLangFunctions.clientFunctions)
runtime.environment.structs["query"] = struct
}
override fun initialize(entity: PokemonEntity) {
this.currentEntity = entity
this.age = entity.age
@@ -272,6 +280,9 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
},
"weight" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.species.weight.toDouble())
},
"friendship" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.friendship.toDouble())
}
))
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
@@ -148,7 +149,14 @@ class PartyOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.ge
0.0
)
drawPortraitPokemon(pokemon.species, pokemon.aspects, matrices, partialTicks = partialDeltaTicks)
drawPoseablePortrait(
identifier = pokemon.species.resourceIdentifier,
aspects = pokemon.aspects,
matrixStack = matrices,
partialTicks = partialDeltaTicks,
contextScale = pokemon.form.baseScale,
repository = PokemonModelRepository
)
matrices.pop()
context.disableScissor()
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.gui.battle
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.text.bold
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
@@ -238,11 +239,13 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
)
} else {
matrixStack.push()
drawPortraitPokemon(
species = species,
drawPoseablePortrait(
identifier = species.resourceIdentifier,
aspects = aspects,
matrixStack = matrixStack,
scale = 18F * (ballState?.scale ?: 1F),
contextScale = species.getForm(aspects).baseScale,
repository = PokemonModelRepository,
reversed = reversed,
state = state,
partialTicks = partialTicks
@@ -28,7 +28,6 @@ import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueDTO
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.asTranslated
@@ -116,7 +115,7 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
.associate { it.key to it.value }
)
dialogueDTO.currentPageDTO.clientActions.forEach { runtime.resolve(it.asExpression()) }
dialogueDTO.currentPageDTO.clientActions.forEach { runtime.resolve(it.asExpressionLike()) }
val centerX = scaledWidth / 2F
val boxMinY = (scaledHeight / 2F - BOX_HEIGHT / 2F) - 10
val boxMaxY = boxMinY + BOX_HEIGHT
@@ -9,12 +9,17 @@
package com.cobblemon.mod.common.client.gui.dialogue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.npc.NPCFloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonFloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.entity.npc.NPCEntity
import java.util.UUID
import kotlin.math.atan
import net.minecraft.client.MinecraftClient
@@ -39,8 +44,8 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
// All of the maths below is shamelessly stolen from InventoryScreen.drawEntity.
// the -20 and 5 divided by 40 are for configuring the yaw and pitch tilt of the body and head respectively.
// For more information, pray for divine inspiration or something idk.
val f = atan((-20 / 40.0f).toDouble()).toFloat()
val g = atan((5 / 40.0f).toDouble()).toFloat()
val f = atan((-40 / 40.0f).toDouble()).toFloat()
val g = atan((-10 / 40.0f).toDouble()).toFloat()
val quaternionf = Quaternionf().rotateZ(Math.PI.toFloat())
val quaternionf2 = Quaternionf().rotateX(g * 20.0f * (Math.PI.toFloat() / 180))
quaternionf.mul(quaternionf2)
@@ -50,14 +55,14 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
val oldPrevHeadYaw = entity.prevHeadYaw
val oldHeadYaw = entity.headYaw
// Modifies the entity for rendering based on our f and g values
entity.bodyYaw = 180.0f + f * 20.0f
entity.setYaw(180.0f + f * 40.0f)
entity.setPitch(-g * 20.0f)
entity.bodyYaw = 180.0F + f * 20.0F
entity.setYaw(180.0F + f * 40.0F)
entity.setPitch(0F)
entity.headYaw = entity.yaw
entity.prevHeadYaw = entity.yaw
val size = 37
val size = 34
val xOffset = 0
val yOffset = 75
val yOffset = 72
InventoryScreen.drawEntity(drawContext, xOffset, yOffset, size, quaternionf, quaternionf2, entity)
// Resets the entity
entity.bodyYaw = oldBodyYaw
@@ -68,18 +73,36 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
}
}
class ReferenceRenderableFace(entity: Poseable): RenderableFace {
class ReferenceRenderableFace(val entity: Poseable): RenderableFace {
val state = entity.delegate as PoseableEntityState<*>
override fun render(drawContext: DrawContext, partialTicks: Float) {
val state = this.state
if (state is PokemonClientDelegate) {
drawPortraitPokemon(
species = state.currentEntity.pokemon.species,
drawPoseablePortrait(
identifier = state.currentEntity.pokemon.species.resourceIdentifier,
aspects = state.currentEntity.pokemon.aspects,
repository = PokemonModelRepository,
contextScale = state.currentEntity.pokemon.form.baseScale,
matrixStack = drawContext.matrices,
state = state,
partialTicks = 0F // It's already being rendered potentially so we don't need to tick the state.
)
} else if (state is NPCClientDelegate) {
entity as NPCEntity
val limbSwing = entity.limbAnimator.getPos(partialTicks)
val limbSwingAmount = entity.limbAnimator.getSpeed(partialTicks)
drawPoseablePortrait(
identifier = state.npcEntity.npc.resourceIdentifier,
aspects = state.npcEntity.aspects,
repository = NPCModelRepository,
matrixStack = drawContext.matrices,
state = state,
partialTicks = 0F, // It's already being rendered potentially so we don't need to tick the state.
limbSwing = limbSwing,
limbSwingAmount = limbSwingAmount,
ageInTicks = entity.age.toFloat(),
)
}
}
}
@@ -89,12 +112,10 @@ class ArtificialRenderableFace(
val identifier: Identifier,
val aspects: Set<String>
): RenderableFace {
val species = PokemonSpecies.getByIdentifier(identifier) ?: run {
Cobblemon.LOGGER.error("Unable to find species for $identifier for a dialogue face. Defaulting to first species.")
PokemonSpecies.species.first()
}
val state: PoseableEntityState<*> = if (modelType == "pokemon") {
PokemonFloatingState()
} else if (modelType == "npc") {
NPCFloatingState()
} else {
throw IllegalArgumentException("Unknown model type: $modelType")
}
@@ -102,11 +123,26 @@ class ArtificialRenderableFace(
override fun render(drawContext: DrawContext, partialTicks: Float) {
val state = this.state
if (state is PokemonFloatingState) {
drawPortraitPokemon(
species = species,
val species = PokemonSpecies.getByIdentifier(identifier) ?: run {
Cobblemon.LOGGER.error("Unable to find species for $identifier for a dialogue face. Defaulting to first species.")
PokemonSpecies.species.first()
}
drawPoseablePortrait(
identifier = species.resourceIdentifier,
aspects = aspects,
matrixStack = drawContext.matrices,
contextScale = species.getForm(aspects).baseScale,
state = state,
repository = PokemonModelRepository,
partialTicks = partialTicks
)
} else if (state is NPCFloatingState) {
drawPoseablePortrait(
identifier = identifier,
aspects = aspects,
matrixStack = drawContext.matrices,
state = state,
repository = NPCModelRepository,
partialTicks = partialTicks
)
}

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