Compare commits

...
Author SHA1 Message Date
plastered_crab cf1054070c Dumb test branch with current versions of Breeding, Riding, Cooking, and TMs (Not for 1.7 just a test) 2025-05-22 11:17:45 -07:00
plastered_crab a02866bbed Merge remote-tracking branch 'refs/remotes/origin/1.21.1-breeding' into big-merge-test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	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/block/entity/PokemonPastureBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/CobblemonModelPredicateRegistry.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/world/feature/CobblemonPlacedFeatures.kt
2025-05-22 11:04:57 -07:00
plastered_crab 5418b8e148 Merge branch 'refs/heads/1.21.1-TMs' into big-merge-test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/assets/cobblemon/sounds.json
2025-05-22 11:02:21 -07:00
plastered_crab 5da16df5ae Changes to Type Gem Cluster logic 2025-05-20 23:41:42 -07:00
plastered_crab 24fc4b05ab Toast appears now when learning a new TM + Licenses 2025-05-20 22:27:44 -07:00
Plastered_Crab 173902feb1 Starting code to display Toasts when unlocking new TMs 2025-05-20 19:14:41 -07:00
Plastered_Crab fd5f08fe84 Call syncTMsFromPokemon from add method in PokemonStore class. 2025-05-20 18:46:56 -07:00
Plastered_Crab d430ad6ca1 Move the syncTMsFromPokemon method to TMMoveManager 2025-05-20 18:21:05 -07:00
Plastered_Crab 1309c18292 Player unlocks TMs by catching pokemon with those moves and TM Machine lists only player learned moves 2025-05-20 18:08:42 -07:00
plastered_crab b6deebac97 Preparing for TM Move Data store 2025-05-19 15:16:01 -07:00
Paul ab0f2cbd79 Bug fix for grotto block editor 2025-05-11 10:10:58 +10:00
Paul 62c7882469 Apply grotto structure markers that can be detected by spawn files 2025-04-28 20:09:34 +10:00
Paul b6c2c65a6c Integrated spawning upgrades from the other branch with surprisingly little pain 2025-04-27 21:43:01 +10:00
Paul 01206fb67a Merge branch 'spawning-upgrades' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/RidingManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/BestSpawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/CobblemonSpawningZoneGenerator.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawningZone.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/condition/SpawningCondition.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/fishing/FishingSpawnCause.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/influence/BucketNormalizingInfluence.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/influence/detector/GrottoBlockDetector.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/influence/detector/SpawningInfluenceDetector.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/position/AreaSpawnablePositionResolver.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/spawner/AreaSpawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/spawner/Spawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/spawner/TriggerSpawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/SpawnPokemonFromPool.kt
#	common/src/test/kotlin/com/cobblemon/mod/common/spawning/SpawnValidator.kt
2025-04-27 21:20:01 +10:00
Paul 07af63db25 Conflicto fixo 2025-04-27 21:15:53 +10:00
Paul 1047648f68 Merge remote-tracking branch 'origin/main' into 1.21.1-breeding 2025-04-27 20:55:53 +10:00
Paul 6523a610ff immediate compile error fix 2025-04-25 12:08:32 +10:00
Paul 6d132e3f39 Merge remote-tracking branch 'origin/smarterer-pokemons' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	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/CobblemonPoiTypes.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/CobblemonSpawningZoneGenerator.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/SpawningZone.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/condition/FishingSpawningCondition.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/context/AreaContextResolver.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/fishing/FishingSpawnCause.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/preset/BestSpawnerConfig.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/selection/FlatContextWeightedSelector.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/spawner/TickingSpawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/keybind/keybinds/DebugKeybindings.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/spawn/SpawnPokemonPacket.kt
#	common/src/main/resources/cobblemon-common.accesswidener
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
2025-04-25 12:07:21 +10:00
Plastered_Crab 83711f2dcb Merge branch '1.21.1-TMs-(dont-be-shelvish)' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2025-04-24 14:11:35 -07:00
Plastered_Crab 18e1a767af Use new TM Shelf Block and starting to get it working 2025-04-24 14:07:38 -07:00
Polymeta 6ae3efb461 fix merge and tidy up
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-24 19:29:38 +01:00
Polymeta 84e1934134 Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/cobblemon-common.accesswidener
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
2025-04-24 16:58:20 +01:00
Plastered_Crab 9aed9342ee Fix atrocious formatting 2025-04-23 13:33:54 -07:00
Plastered_Crab e4d0bf39ca Added in WIP tooltips for TMs 2025-04-23 13:26:32 -07:00
Paul b311d092de Fixed tests that assume all spawn files are pokemon ones 2025-04-23 21:44:09 +10:00
Paul 6f134a50e0 Touch ups to herd logic with new controls. It works >:] 2025-04-23 20:58:04 +10:00
plastered_crab 847fa6842e Test Bookcase can hold TMs, Upgrade, Dubious Disc, Pokedex, etc (very WIP) 2025-04-22 13:51:15 -07:00
plastered_crab 08aa4f29bd Dubious Disc and Upgrade able to be placed in the Bookshelf too 2025-04-22 10:59:35 -07:00
plastered_crab 539218381b Corrected face being mirrored 2025-04-22 09:21:45 -07:00
plastered_crab 9dff39b4d7 Shelf is no longer upside down lmao 2025-04-22 08:42:24 -07:00
plastered_crab 1e578e94e5 Shelf renders without redish color 2025-04-22 08:32:35 -07:00
plastered_crab ec35c9b9bb shelf face rendered (upside down...) 2025-04-22 07:55:36 -07:00
plastered_crab 0deaeaeb7d fit the TM case textures onto shelves better 2025-04-22 07:22:49 -07:00
Paul a0531f2d2f Spawning refactors and herd spawning (untested!!!) 2025-04-22 22:16:15 +10:00
plastered_crab ec3d9e1a56 Test code for placing TMs in Chiseled Bookshelves 2025-04-21 17:11:10 -07:00
Paul 80f5050ecd Major rename of spawning terms and moved spawn bucket decision later in the process 2025-04-20 20:08:22 +10:00
Landon Wills e32a2da8a4 Fix animation sometimes not playing 2025-04-19 21:05:24 -04:00
Aram e92609b6c7 Fixed Grotto Mimic Particles/Sound 2025-04-19 22:43:16 +02:00
Aram abc0c72707 Small cleanup again 2025-04-19 22:22:48 +02:00
Aram e55f46c990 small cleanup 2025-04-19 21:48:58 +02:00
Aram 6ee5714bf9 Grotto Debug GUI 2025-04-19 21:47:20 +02:00
plastered_crab bb379764f7 Time for pokemon to breed with their brains! Merging in brain branch finally!!! 2025-04-18 09:50:53 -07:00
plastered_crab 3ec78d9d7e Merge branch 'refs/heads/1.21.1-breeding-pokemingle.com' into 1.21.1-breeding 2025-04-18 09:36:56 -07:00
plastered_crab 12973dd66a Grotto Block finally mimics blocks perfectly (with AO) 2025-04-18 09:26:46 -07:00
Aram ed30e4cd6f Almost working Ambient Occlusion, it doesnt check for all surroundings yet 2025-04-18 17:39:35 +02:00
Aram 32c260446e Grotto isnt black anymore!, Occlusion still broken 2025-04-18 17:30:42 +02:00
plastered_crab 41f1eb699f Marging with smarterer and fixing reference issues 2025-04-18 07:56:23 -07:00
plastered_crab e68a593e9e Merge branch 'refs/heads/smarterer-pokemons' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	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/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/RidingManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PosableModel.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/net/messages/client/spawn/SpawnPokemonPacket.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Species.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/ai/PokemonBrain.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/EntityExtensions.kt
#	common/src/main/resources/data/cobblemon/behaviours/pokemon/pokemon_breeding.json
#	common/src/main/resources/data/cobblemon/behaviours/pokemon/pokemon_core.json
2025-04-18 06:51:29 -07:00
plastered_crab 903ffe2143 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-04-18 06:44:22 -07:00
Aram 1d6845d595 When in Creative, the Incubator also doesn't remove the egg from your inventory 2025-04-18 12:41:15 +02:00
Aram f8eb72e437 Finished Incubator Hatching Animations 2025-04-18 11:39:18 +02:00
Polymeta 814f84ae42 licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-18 01:11:33 +01:00
Polymeta 904be475ef Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2025-04-18 00:44:25 +01:00
Plastered_Crab 28a3b8edfe Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-04-17 15:51:39 -07:00
Plastered_Crab 96ee23b7d1 🥚2️⃣ Add ALL Finalized Gen 2 Egg Sprites and Egg Textures 2025-04-17 15:51:23 -07:00
Plastered_Crab 5edecbe81b Add crafting recipes / loot tables type gems, fix collision boxes, break rules, etc 2025-04-17 15:29:42 -07:00
Plastered_Crab f357033404 Fixed 100+ recipes, fixed CraftTMPacketHandler, adjusted GUI for TM Machine, and fixed names for some moves 2025-04-17 12:27:54 -07:00
Plastered_Crab 4800ae6649 Tweaking of some TM recipes 2025-04-17 09:29:21 -07:00
Aram d3ce829e5b Incubator Hatching Animation WIP
Currently Missing the BlockPos so the Pokemon wont spawn

check: EggRenderHelper
2025-04-17 16:35:46 +02:00
plastered_crab 60b0690c1e Add remaining 10 TM recipes. now all are present for testing 2025-04-17 00:40:00 -07:00
plastered_crab 82e31dd766 Add 330+ TM recipes for testing >:) 2025-04-17 00:00:34 -07:00
Plastered_Crab 4010f138ae Remove some old code and comment out debug lines 2025-04-16 11:32:24 -07:00
Plastered_Crab 5100d6c028 Habitat spawning system now references player party levels to determine the level of things that spawn 2025-04-16 11:25:20 -07:00
plastered_crab 154ff75a9d Comment out debugging lines to check other things 2025-04-16 07:38:07 -07:00
plastered_crab 678c98a5b6 GrottoBlockEntities now grab a set of habitat spawns during each Full Moon or if it is empty 2025-04-16 07:37:26 -07:00
plastered_crab d9143788bf Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-04-16 06:14:17 -07:00
plastered_crab 7e1956f165 Use POI system to detect Grotto Blocks and use them to spawn Pokemon around habitats 2025-04-16 00:08:28 -07:00
plastered_crab 994a41af78 Add structureCenter variable to GrottoBlockEntity to try to use later to center habitat spawns 2025-04-15 21:32:37 -07:00
Plastered_Crab 2d5f15a4db Fix another oopsie 2025-04-15 15:37:05 -07:00
Plastered_Crab 8442000708 Habitats files now work with weighted spawnpools and per-entry weights too 🌳 2025-04-15 15:28:06 -07:00
Plastered_Crab 695ac93f99 Habitats are starting to work now :D 2025-04-15 12:46:58 -07:00
plastered_crab e5e5c29c12 Edits to Dead Wood habitat 2025-04-15 07:45:20 -07:00
plastered_crab 893277fdec Habitats support list of spawn choices per moon cycle instead of only individual spawns 2025-04-15 00:43:38 -07:00
plastered_crab 60dc881e3d Basic system for Grotto Blocks to cycle spawn pool choices each Full Moon cycle 2025-04-14 23:50:15 -07:00
plastered_crab f98acf0bc4 Fix oopsie 2025-04-14 22:04:38 -07:00
plastered_crab 6e104fdb01 Spawn system detects Grotto blocks as POIs and detects the habitat they belong to along with reads the spawn pool of the habitat 2025-04-14 22:00:48 -07:00
plastered_crab 05c559e203 Grotto block now detects and saves the structure they spawn with onto them 2025-04-14 18:49:54 -07:00
plastered_crab 1323caeeec Add test dead tree habitat for testing 2025-04-14 18:15:44 -07:00
Plastered_Crab 6dc5809622 Add test Grotto Block for habitat testing 2025-04-11 15:24:27 -07:00
Plastered_Crab d4416e50b2 Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-04-10 16:13:22 -07:00
Plastered_Crab 520bcda956 Adding test files for experimenting with habitats 2025-04-10 16:12:56 -07:00
Sterrezo d10c082e67 Subtitled the egg sounds 2025-04-10 22:33:23 +02:00
Sterrezo 72d608b300 Volume corrections and fixed a missing sound 2025-04-10 22:13:14 +02:00
Sterrezo 2b4dc467ff Egg sounds! 2025-04-10 21:36:14 +02:00
Plastered_Crab b9390838d0 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-04-10 09:59:44 -07:00
plastered_crab e36bbd98f2 No more stuttering hatch animation (Thank you Landon <3) 2025-04-09 22:37:56 -07:00
plastered_crab c4aa925f39 Put hatch animation onto PokemonEntity 2025-04-09 20:57:26 -07:00
plastered_crab ad5e85ad97 Final Hatching animation + particles working more now! 2025-04-09 14:50:09 -07:00
plastered_crab 5279603432 Particle keyframes starting to work on Hatching Animations now :D 2025-04-09 13:02:15 -07:00
plastered_crab 583fba4e77 Sound keyframes starting to work 2025-04-09 08:56:20 -07:00
plastered_crab 9ee983b2a2 Good checkpoint on trying to get the hatching sound to play via keyframe 2025-04-08 21:31:30 -07:00
Plastered_Crab 16ffbbbd9e Some code to try to get Hatching sounds working :D 2025-04-08 16:33:13 -07:00
Plastered_Crab f23495fe99 Fix for hatching multiple eggs on the same nest due to a missing sync 2025-04-08 14:53:35 -07:00
Plastered_Crab f61448c333 Hatching animations work now! REJOICE! 2025-04-08 14:44:58 -07:00
Plastered_Crab 406770eaf1 More work towards getting the hatching animations working 2025-04-08 13:34:16 -07:00
Plastered_Crab 2513fab317 More work towards getting the hatching animations working 2025-04-08 12:11:47 -07:00
Plastered_Crab 3a79e49edb More work towards getting the hatching animations working 2025-04-08 10:44:16 -07:00
plastered_crab 87dad0143b Hatching animation FINALLY starting to work 2025-04-08 00:48:39 -07:00
plastered_crab bdc91fea71 Add in new files to get ready for Hatching animation implementation 2025-04-07 19:04:41 -07:00
Plastered_Crab eb64666229 Merge with main to keep things up to date for less pain later 2025-04-07 13:43:07 -07:00
Plastered_Crab f7ed0c8799 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/NestBlockEntity.kt
2025-04-07 13:25:27 -07:00
Plastered_Crab 9fde7adb7b Merge with main to keep things up to date for less pain later 2025-04-07 13:14:56 -07:00
Plastered_Crab 8b8c07065b Merge branch 'main' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/RidingManager.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PosableModel.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
#	common/src/main/kotlin/com/cobblemon/mod/common/util/EntityExtensions.kt
2025-04-07 12:48:42 -07:00
Guilherme Pereira 130e767e75 force type gems to grow halfway upon world generation 2025-03-28 05:02:33 -03:00
Plastered_Crab 6d761fef4f Merge in new Brains code and remove Nest Spawner 2025-03-27 16:59:38 -07:00
Plastered_Crab 737ce1314c Remove old unused "Nest" spawner and merge in new code 2025-03-27 16:31:46 -07:00
Plastered_Crab 7b89f82d45 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonActivities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMemories.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonSpecies.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/FormData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/ai/PokemonBrain.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2025-03-27 15:43:17 -07:00
Guilherme Pereira b1a1d509ec make type gems grow naturally 2025-03-27 19:37:46 -03:00
Plastered_Crab 2203d543a1 Merge branch 'smarterer-pokemons' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonSpecies.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/spawning/BestSpawner.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
2025-03-27 15:32:11 -07:00
Guilherme Pereira c129157687 add foliage color to leaf nests 2025-03-27 03:52:41 -03:00
Guilherme Pereira 276c49ac5c fix lush nest caves configured feature 2025-03-27 03:52:26 -03:00
Plastered_Crab 0698cf495e Type Gem Clusters fill in empty space when they grow grow stunted buds when hitting max capactity 2025-03-26 16:49:00 -07:00
Plastered_Crab 6047660405 Type Gem clusters grow in the same direction as their previous state more often and also fill in gaps with clusters 2025-03-26 14:53:05 -07:00
Plastered_Crab aaf3495f83 New transitional stages for the type gem clusters and fixes for rendering issues 2025-03-26 13:09:59 -07:00
Plastered_Crab ae6529c9fd Type gem clusters grow their respective type gem blocks when placed on the core 2025-03-25 17:05:03 -07:00
plastered_crab 45a96b2a42 Prepare for new Type Gem generation for each type of gem 2025-03-19 16:07:58 -07:00
Plastered_Crab f517a63cc9 Make it so nests naturally generate in the world in their proper biomes 2025-03-18 15:52:31 -07:00
Plastered_Crab 1e2f685ee5 fix for filename for Gastly's egg 2025-03-17 16:33:13 -07:00
Plastered_Crab 5245614804 Add light level to Warm Ocean nest and adjust collision boxes of new nests 2025-03-17 16:29:12 -07:00
Plastered_Crab 8b97c3cb85 Whoopsie these files were missed 2025-03-17 15:51:07 -07:00
Plastered_Crab d607463116 Added the 8 new nest types! 2025-03-17 15:50:42 -07:00
Plastered_Crab 07cc92eb18 Added new underwater Seagrass nest for cold oceans/rivers :D 2025-03-04 16:21:04 -08:00
Apion 4212903c67 Merge branch 'fix-tm-automation' into '1.21.1-TMs'
General Fixes and Implementation of the TM Machine for 1.21.1

See merge request cable-mc/cobblemon!1542
2025-03-01 08:52:01 +00:00
BuildTools be47dbcede Some Requested Changes 2025-03-01 06:05:38 +02:00
Plastered_Crab 9665eba7b0 Wild parents will bring their lost eggs back to their nest more consistently now 2025-02-28 12:25:05 -08:00
Plastered_Crab 9f83c7c72c Put that thing back where it came from or so help me 2025-02-26 15:52:10 -08:00
Plastered_Crab 9bb74358f7 Get this thing on track with Main again too 2025-02-26 15:48:06 -08:00
Plastered_Crab e2219730e6 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/NestBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-02-26 15:40:47 -08:00
Plastered_Crab 4daaf50b1a Get this thing on track with Main again 2025-02-26 15:37:01 -08:00
Apion 9556c57c69 Fix broken accesswideners 2025-02-23 21:33:54 -08:00
BuildTools f72b65bfac Fix for TMM GUI elements being offset in wrong postion after a previus TMM GUI Fix 2025-02-22 13:13:28 +02:00
BuildTools 6121700c9b Fixed disk in TMM rendering in wrong location after fixing TMM rendering backwards 2025-02-22 12:20:52 +02:00
BuildTools 91c968e743 A better fix for a previos TMM GUI fix 2025-02-22 12:05:30 +02:00
BuildTools 8fed6f4823 Fixed TMM rendering backwards and now the TMM will eject disc from its facing direction proper 2025-02-22 10:31:43 +02:00
BuildTools 34e4b39d75 Added Properties to TMM regarding breaking the block and loot table (based of PC) 2025-02-22 09:52:58 +02:00
Plastered_Crab b79792ebbf Merge branch 'main' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonSpecies.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/FormData.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2025-02-21 15:14:44 -08:00
Plastered_Crab 7a0b294d3d Merge branch 'main' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2025-02-21 15:04:14 -08:00
Plastered_Crab ad1312d077 Merge branch 'main' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-02-21 14:36:04 -08:00
BuildTools a21c70ce46 Resizing TMM GUI no longer breaks the GUI Buttons 2025-02-21 23:00:52 +02:00
BuildTools 8865a71f4c TMM will now output signal when ready to craft Blank TM and will drop Filter TM on break 2025-02-21 20:32:52 +02:00
BuildTools 142cadc187 TMM can now craft blank TMs if no TM is set in the filter, as well as allow taking out the filter TM 2025-02-21 19:43:36 +02:00
BuildTools 66f4c2531d Fixed TMM GUI quick move feature to properly work as well as what is considered to be outside the GUI 2025-02-19 20:16:00 +02:00
BuildTools 3f8ddd5f7d TM Machine now turns on/off and plays sound then opened/closed 2025-02-16 22:09:07 +02:00
BuildTools c076e14cbd Made Comparator read TM Machine and it provides power if the TM Machine has the materials to autocraft 2025-02-15 23:00:32 +02:00
BuildTools d724eb068b Fixed missing tooltip render call causing tooltips over items not showing in TM Machine 2025-02-15 13:10:15 +02:00
BuildTools d3ac263e29 Made the item form of TM Machine render properly in usual cases 2025-02-15 12:32:35 +02:00
BuildTools a6b7fe1f17 TM can now finally autocraft TMs based on filtered TM by receiving redstone signal 2025-02-15 09:50:28 +02:00
Plastered_Crab d65d7b779f Add delay to NestSensor 2025-02-12 15:49:45 -08:00
Plastered_Crab 7ce25d8158 Wild pokemon will claim the nests they build for themselves 2025-02-12 15:20:55 -08:00
Plastered_Crab 178d7293fb Okay they go to nests in single pairs for real this time 2025-02-12 14:29:04 -08:00
Plastered_Crab ba0bba2202 Code change for parity with the pokemingle branch 2025-02-12 13:32:16 -08:00
Plastered_Crab 29d5ce5f0e Pairs of wild pokemon will "claim" a nest before they get there 2025-02-12 12:57:25 -08:00
BuildTools ec714ccb84 Fixed Filtering for Hopper input not working 2025-02-12 19:53:54 +02:00
Plastered_Crab b9ebfc17ed Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-02-12 09:36:10 -08:00
Plastered_Crab 2e28792c92 Add variable to NestBlockEntity to allow wild mons to "claim" them before breeding so hoards don't run to a single nest 2025-02-12 09:02:21 -08:00
BuildTools ab0a129ba1 TM Machine GUI and Inventory synced and working properly 2025-02-12 18:45:32 +02:00
plastered_crab 648d15403f Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-02-11 12:09:17 -08:00
plastered_crab 3fe3f521ee Fix regional form predicate registration 2025-02-11 12:08:54 -08:00
plastered_crab 0ff638a0ca Merge remote-tracking branch 'origin/1.21.1-breeding-pokemingle.com' into 1.21.1-breeding-pokemingle.com 2025-02-11 11:25:02 -08:00
plastered_crab 485b5f46f6 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-02-11 11:24:42 -08:00
plastered_crab b7c2063510 Add ALL Gen 1 - Gen 9 Egg predicate files updated YIPPEE 2025-02-11 11:01:46 -08:00
plastered_crab 94d7ae5290 Add ALL Gen 1 - Gen 9 Egg item model predicate files 2025-02-11 08:37:28 -08:00
plastered_crab d75d5da250 Add ALL gen 9 Egg sprites! That is ALL NOW (THANK YOU CUMULO) 2025-02-11 08:27:43 -08:00
plastered_crab d3cd1be24e Add ALL gen 8 Egg sprites! (THANK YOU CUMULO) 2025-02-11 07:44:07 -08:00
Plastered_Crab d1f11316ec Wild pokemon are more mindful of biomes/materials when making their own nests 2025-02-10 16:47:30 -08:00
plastered_crab 1e50bd999b Wild pokemon now gather nesting material dropped on the ground to make tree, water, snow, etc nests! 2025-02-09 22:34:45 -08:00
plastered_crab 0257f6c6a0 Wild pokemon now have a chance to gather materials around them to make nests for themselves if none are around 2025-02-07 22:15:58 -08:00
Plastered_Crab a3bc1cf72b Wild pokemon will try to detect nest materials to build their own nests if there is not one nearby 2025-02-07 17:12:59 -08:00
Plastered_Crab e236632c67 Wild Pokemon Parents will attack the player until they give the egg back >:( 2025-02-06 17:32:25 -08:00
Plastered_Crab 4730d97898 Wild Pokemon Parents will now give up searching for a lost egg after some time 2025-02-06 15:20:30 -08:00
Plastered_Crab 36994a134d Wild Pokemon Parents will now attack players who take their eggs and bring eggs back 2025-02-06 13:09:36 -08:00
Plastered_Crab 97899b3fa9 Wild pokemon parents will look for their lost egg and bring it back to the nest 2025-02-06 11:56:25 -08:00
plastered_crab 713bd6d6ef Tons of code for wild parent pokemon to know when their egg is missing and to go looking for it (not working fully yet) 2025-02-06 08:01:47 -08:00
Guilherme Pereira a3f272b2aa Merge branch 'main' into 1.21.1-TMs 2025-02-06 03:48:28 -03:00
plastered_crab 48736fad1f Fix aggression once more since refactor 2025-02-03 20:00:02 -08:00
plastered_crab 5a47deb9a7 Ground pokemon will ignore nests in trees and other areas they cannot pathfind to! 2025-02-01 22:59:33 -08:00
plastered_crab 4b01facb4f Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-02-01 21:08:48 -08:00
plastered_crab 158d4d6aed Tweaking generation of wild nests 2025-02-01 21:03:58 -08:00
plastered_crab ab91776d97 Natural generation of all 15 wild nests almost working properly now 2025-02-01 18:08:35 -08:00
plastered_crab 286ffe7219 More work to add new world gen wild nests 2025-02-01 01:39:21 -08:00
plastered_crab 79da4cb8a8 Add new world gen files for nests (thank you Slippy!) 2025-02-01 01:35:02 -08:00
Plastered_Crab 32f3c75d45 Fix to make Snow Nests only spawn where it is cold 2025-01-31 15:46:46 -08:00
Plastered_Crab acc81d60ad Remove test code for spawning pokemon 2025-01-31 15:31:53 -08:00
Plastered_Crab 4b093a0072 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-01-31 14:39:41 -08:00
Plastered_Crab e0c57007db Wild Nests test generation on ground, in trees, and in snow 2025-01-31 14:38:54 -08:00
Plastered_Crab 66b3579a9b Wild Nests test generation on ground, in trees, and in snow 2025-01-31 14:34:41 -08:00
Plastered_Crab 0981bb71ce Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-01-31 09:47:19 -08:00
Plastered_Crab 6b762cd6d3 Add all Gen 6 and Gen 7 Egg Sprites 2025-01-31 09:47:03 -08:00
Plastered_Crab 581c466c0e Add all Gen 6 and Gen 7 Egg Sprites 2025-01-31 09:46:05 -08:00
Plastered_Crab 5622c65fca Add all Gen 6 and Gen 7 Egg Sprites 2025-01-31 09:45:55 -08:00
Plastered_Crab 51c6ea576b Add all Gen 6 and Gen 7 Egg Sprites 2025-01-31 09:42:24 -08:00
plastered_crab b7b8fe1fb9 fix some sillies 2025-01-31 06:29:51 -08:00
plastered_crab 1634a20464 Config for increased hatching rates in the nether for nests and incubators 2025-01-30 23:57:24 -08:00
plastered_crab 7190c33dab Test bounding box data for 9 new nests coming later 2025-01-30 20:38:26 -08:00
Plastered_Crab 8ea20925d3 Mons with only Water 1/2/3 egg groups can breed at underwater nests. Mons with water + other egg groups can breed at both 2025-01-30 16:52:22 -08:00
Plastered_Crab d4c7ce4eb9 Merge branch 'smarterer-pokemons' into 1.21.1-breeding-pokemingle.com 2025-01-30 15:05:15 -08:00
Plastered_Crab 625237457a move initialization of config to be before CobblemonItems 2025-01-30 12:50:23 -08:00
Plastered_Crab 0f21173f5f move initialization of config to be before CobblemonItems 2025-01-30 12:48:18 -08:00
Plastered_Crab 7fda4570c6 More preparations for the other 9 nests 2025-01-30 10:46:30 -08:00
Plastered_Crab e0aae17f37 Prepare for the other 9 types of nests 2025-01-30 10:14:01 -08:00
plastered_crab 32b4459e19 Adjust BreedAtNest brain task to account for Destiny Knot durability via config 2025-01-30 07:38:06 -08:00
plastered_crab 9072a7ac3c Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/breeding/BreedingLogic.kt
2025-01-30 07:31:50 -08:00
plastered_crab 0e39874f9a Added logic for Destiny Knot durability IF the configs are set to true 2025-01-30 06:31:38 -08:00
plastered_crab aa5d116910 Added config for Destiny Knot Durability and if it breaks or not during breeding 2025-01-30 06:22:21 -08:00
plastered_crab 1f49d0d21b Added config Masuda method & config Legend, Genderless, and Ditto breeding 2025-01-29 23:40:43 -08:00
Plastered_Crab 05a612bf79 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-01-29 13:24:06 -08:00
Plastered_Crab ab0d839006 Comment out possibly unused access wideners 2025-01-29 13:22:46 -08:00
Plastered_Crab 56b061c93d Optimize bounding box for Incubators 2025-01-29 11:39:03 -08:00
Plastered_Crab ec97e16609 Fix faulty bounding boxes on Eggs, Nests, and Incubators! 2025-01-29 11:32:55 -08:00
plastered_crab e4ce58414c Changes to the 7 new nest types 🪺🥺 2025-01-29 07:41:45 -08:00
Guilherme Pereira 77d91ed979 make last type gems growing from core not fully grow into blocks 2025-01-29 03:56:35 -03:00
plastered_crab b07856ed58 Add 7 new nest types 🪺🥺 2025-01-28 21:51:17 -08:00
plastered_crab b7d2f38490 More changes to the MateSensor to try to "pair" mates together 2025-01-28 17:56:26 -08:00
plastered_crab 295185c1cf Change to the MateSensor to try to "pair" mates together 2025-01-28 10:15:22 -08:00
plastered_crab f0d8ee21d9 No more pokemon swingers partys (hopefully) 2025-01-28 09:20:43 -08:00
plastered_crab b203020b4d Pokemon now have a cooldown memory for checking for near mates + compat checks 2025-01-28 08:06:52 -08:00
plastered_crab 5962149da8 Cancel path to nest task if an egg is in it 2025-01-28 06:51:25 -08:00
plastered_crab 24101bed08 Fixes to AI pathing to nests 2025-01-27 08:10:38 -08:00
Guilherme Pereira 9907ba17c1 update licenses 2025-01-27 06:28:51 -03:00
Guilherme Pereira a6f0e19a44 type gem core now generates more gems 2025-01-27 06:27:27 -03:00
Guilherme Pereira 7ee064b04d make type gem bud grow into a block in its final stage 2025-01-27 05:29:14 -03:00
Guilherme Pereira f2e4c735f7 add type gems 2025-01-27 05:04:04 -03:00
plastered_crab 321736657e AI breeding is broken once more, but some progress trying to fix 2025-01-25 20:04:21 -08:00
plastered_crab e50309871a Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/breeding/BreedingLogic.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/NestBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/NestBlockEntity.kt
2025-01-24 22:51:58 -08:00
plastered_crab 36cd7e426a Fix give pokemon egg command (it is slow for some reason) 2025-01-24 16:47:37 -08:00
plastered_crab 6660747b31 Fix give pokemon egg command (it is slow for some reason) 2025-01-24 14:55:57 -08:00
plastered_crab c021f28dc7 Eggs now animate based on how ready they are to hatch! :D 2025-01-24 14:42:22 -08:00
plastered_crab a7a2eceda6 More work to refactor the pokemon ai breeding tasks 2025-01-24 06:15:28 -08:00
plastered_crab a4cac592e6 Merge branch 'refs/heads/smarterer-pokemons' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/resources/data/cobblemon/brain_presets/pokemon/pokemon_core.json
2025-01-23 18:13:16 -08:00
plastered_crab b29c6e0d9c Merge branch 'refs/heads/1.21.1-breeding-egganims' into 1.21.1-breeding 2025-01-23 18:06:09 -08:00
amo ba9ea5b793 Add eggmodelrepo to posableModelRepositories so anims and groups are registered 2025-01-23 23:30:36 +00:00
Plastered_Crab 47d5ac4108 trying to get these eggs to wiggle 2025-01-23 09:56:02 -08:00
plastered_crab c6df8bbb61 Why won't these damn egg WIGGLE 2025-01-23 07:12:41 -08:00
plastered_crab 97055b7ede so close to getting egg animations working (no more crashing) 2025-01-22 21:45:34 -08:00
plastered_crab df64ac268e change to Bedrock egg structure 2025-01-22 21:12:28 -08:00
plastered_crab 541a3e257b change to Bedrock egg structure 2025-01-22 19:53:22 -08:00
plastered_crab 1a995dfca8 change to Bedrock egg structure 2025-01-22 19:23:43 -08:00
plastered_crab 9aef6b02ad change to Bedrock egg structure 2025-01-22 19:22:00 -08:00
Plastered_Crab f914f20283 Incubator Recipe 2025-01-22 16:28:10 -08:00
Plastered_Crab 03e558efbb Incubator Recipe 2025-01-22 16:27:54 -08:00
Plastered_Crab 925a0551b2 Nests can only be mined with silk touch + break when block underneath is broken 2025-01-22 16:04:32 -08:00
Plastered_Crab 1677f9fe89 Some preparations for Egg animations based on Ready to Hatch State 2025-01-22 15:30:10 -08:00
Plastered_Crab 1db4c90479 Egg incubators now have a little light in them that lights up when it has an egg 💡🥚 2025-01-22 14:22:52 -08:00
Plastered_Crab e3beeff5a8 Egg Incubators send out Comparator values if they are ready to hatch 🧪👨‍🏫 2025-01-22 12:18:03 -08:00
Plastered_Crab 52a6d84041 Protections and Comparator code for Incubators 🧪👨‍🏫 2025-01-22 11:14:27 -08:00
plastered_crab 84f2183f36 Eggs can be hoppered into Incubators :D 2025-01-22 07:57:56 -08:00
plastered_crab 492cd7bc9e Work towards hoppers working on nests :D 2025-01-21 22:09:04 -08:00
Guilherme Pereira 5345bd321f update licenses 2025-01-21 19:08:49 -03:00
Guilherme Pereira eab8d0063b fix tms item sprite colors 2025-01-21 19:08:00 -03:00
Plastered_Crab c21a25610c Add all Gen 5 Egg Sprites 2025-01-21 11:40:47 -08:00
plastered_crab 4eed3c06a7 Work towards Eggs showing patterns for forms 2025-01-21 09:44:05 -08:00
plastered_crab 7f804445c4 You can make a Charizard breed with Blastoise to make Bulbasaur now (forms don't work...) 2025-01-20 22:55:38 -08:00
plastered_crab 89eee30488 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/breeding/BreedingLogic.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/breeding/BreedingLogicManager.kt
2025-01-20 22:36:30 -08:00
plastered_crab b386a20351 You can make a Charizard breed with Blastoise to make Bulbasaur now (forms don't work...) 2025-01-20 22:33:23 -08:00
Plastered_Crab ac06bc6103 Custom Breeding rules are almost fully functional 2025-01-20 16:15:09 -08:00
plastered_crab cda8697dd9 You can now catch a pokemon in wild Doubles battles when there is only 1 mon left 2025-01-19 22:33:11 -08:00
plastered_crab f7f7400163 Parents can no longer move to kill you during a wild Double Battle!✋🛑🥚😡 2025-01-19 21:05:32 -08:00
plastered_crab ea0c4731d8 Merge remote-tracking branch 'origin/1.21.1-breeding-pokemingle.com' into 1.21.1-breeding-pokemingle.com 2025-01-19 19:34:02 -08:00
plastered_crab 204b923289 Merge branch 'refs/heads/smarterer-pokemons' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/ai/config/task/TaskConfig.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonSpecies.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/FormData.kt
#	common/src/main/resources/data/cobblemon/brain_presets/pokemon/pokemon_core.json
2025-01-19 18:54:12 -08:00
Plastered_Crab 6f34b6347d Pokemon no longer want to mate during battle 😳😳 2025-01-18 20:46:31 -08:00
Plastered_Crab a935edbb55 You can now Double Battle with the parents of a nest 2025-01-18 20:22:02 -08:00
Plastered_Crab e58c3b9422 Pokemon will now warn players when they start to get too close to the nest, then will attack if they get closer 2025-01-18 17:20:56 -08:00
Plastered_Crab a5ddc556f3 Merge remote-tracking branch 'origin/1.21.1-breeding-pokemingle.com' into 1.21.1-breeding-pokemingle.com 2025-01-18 15:27:37 -08:00
Plastered_Crab a541cc3e55 Merge branch '1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/resources/data/cobblemon/species/generation1/growlithe.json
#	common/src/main/resources/data/cobblemon/species/generation3/zigzagoon.json
2025-01-18 15:27:10 -08:00
Plastered_Crab e355bdd68a Add ALL gen 4 egg sprites 2025-01-18 15:24:00 -08:00
Plastered_Crab d687d91090 Add ALL gen 4 egg sprites 2025-01-18 15:23:46 -08:00
Plastered_Crab 6b38147c23 Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-01-18 15:09:49 -08:00
Plastered_Crab 0241dbcfa1 Merge branch 'main' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonItemTags.kt
2025-01-18 15:09:35 -08:00
plastered_crab b2e641e269 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-01-17 22:48:38 -08:00
plastered_crab 63f32cb79c Add all gen 3 Egg block textures🥚 2025-01-17 22:48:04 -08:00
plastered_crab 7b05868c5a Add all gen 3 Egg block textures🥚 2025-01-17 22:46:51 -08:00
plastered_crab c4fd0d6fd7 Wild Pokemon attack players close to their nests 🧍🥚😤👊 2025-01-17 21:57:28 -08:00
plastered_crab a4f0eb999c dumb commit to make you not read my horrible sentence in the last commit 2025-01-17 20:22:17 -08:00
plastered_crab 85b7231ff6 Wild pokemon will patrolling the nest will now phyiscally beat up the player that gets too close to the nest 🧍🥚😤👊 2025-01-17 20:20:52 -08:00
plastered_crab 0f3d0008e9 Wild pokemon will now patrol a radius around their nest AND get angry at visible players that get near the nest 2025-01-17 18:40:37 -08:00
Guilherme Pereira 6877977234 tm branch ressurect attempt 2025-01-17 23:23:01 -03:00
plastered_crab 74addf45c3 Pastured Pokemon will only breed in Player-placed nests, wild pokemon only breed in wild nests 2025-01-17 14:43:01 -08:00
Plastered_Crab f97a8a8475 prep work for wild pokemon ignoring player nests and owned pokemon ignoring wild nests 🥚🪺 2025-01-16 16:20:59 -08:00
Plastered_Crab e8cc212968 Pokemon now get tired from mating 😴🫃🏻🥚 2025-01-16 16:05:30 -08:00
plastered_crab 7918924c1c Add test egg cooldown system 2025-01-16 10:59:16 -08:00
plastered_crab 28a41860b9 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com 2025-01-16 09:52:17 -08:00
plastered_crab 20ef700422 Add all gen 3 Egg Sprites 2025-01-16 09:24:20 -08:00
plastered_crab c017863ef8 Add Unown Egg sprite 2025-01-16 08:48:59 -08:00
Plastered_Crab d28ac2facf Pokemon now listen to their brains that it is time to MATE 🫃🏻🥚 2025-01-15 13:49:20 -08:00
Plastered_Crab b391f13b11 Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-01-15 09:28:57 -08:00
plastered_crab c2f29a0dc3 Alter Breeding Logic to prepare for more Brain stuff 2025-01-15 07:19:45 -08:00
plastered_crab 0c4a148c52 Merge branch 'refs/heads/1.21.1-breeding' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
2025-01-15 07:18:49 -08:00
plastered_crab 5c4aa543fa Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-01-15 06:08:18 -08:00
plastered_crab d3a9b50c03 Add gen 2 egg sprites (minus unown) 2025-01-15 06:07:13 -08:00
Plastered_Crab 46962d95fc Pokemon AI is starting to learn how to find a mate and go to a nest to breed 2025-01-13 15:13:23 -08:00
plastered_crab ea50837750 Changes to AI driven Mating tasks 2025-01-13 07:42:13 -08:00
plastered_crab b03fe17e8c Start of AI driven Mating tasks 2025-01-12 20:27:07 -08:00
Guilherme Pereira 16cf5ecbb7 add '/givepokemonegg' and '/giveallpokemonegg' commands 2025-01-12 18:33:09 -03:00
plastered_crab 1b34278ec4 Merge branch 'refs/heads/smarterer-pokemons' into 1.21.1-breeding-pokemingle.com
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMemories.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSensors.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/FormData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Species.kt
#	common/src/main/resources/data/cobblemon/flows/battle_victory/npc_battle_end_scripts.molang
2025-01-12 13:32:38 -08:00
plastered_crab 045d5a8a5e Start to the new Pokemon dating site 2025-01-12 13:26:38 -08:00
plastered_crab 5729c9d5be Egg tweaks 2025-01-12 09:55:32 -08:00
plastered_crab a286584c32 Egg sprites are working now! YIPPEEE 2025-01-12 09:37:09 -08:00
plastered_crab a5895aaa7c Move sprites and egg item model file 2025-01-12 00:46:45 -08:00
plastered_crab 0ab6fef566 Added more predicate files for sprites 2025-01-11 22:51:48 -08:00
plastered_crab a973d21bc0 Added TONS of Egg sprites :D 2025-01-11 22:48:29 -08:00
plastered_crab e149d3f4c8 Add more egg sprite prep 2025-01-11 14:41:44 -08:00
plastered_crab adfde3e236 Add more egg sprite prep 2025-01-11 14:41:23 -08:00
plastered_crab 18cdf705ed Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/item/EggItemRenderer.kt
#	common/src/main/resources/assets/cobblemon/models/item/egg.json
#	common/src/main/resources/assets/cobblemon/textures/egg_patterns/inventory/charmander_egg.png
2025-01-11 00:20:37 -08:00
plastered_crab e5730a5119 Add egg test sprites and get model predicates for sprites working 2025-01-11 00:17:06 -08:00
plastered_crab b295cd7bef Merge branch 'refs/heads/1.21.1-breeding-sprite-test' into 1.21.1-breeding 2025-01-10 23:26:15 -08:00
plastered_crab a168715b3c Merge branch 'refs/heads/main' into 1.21.1-breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
2025-01-10 23:21:42 -08:00
plastered_crab 55f8212451 Holy Schmoly! So many egg files! 2025-01-10 23:09:42 -08:00
plastered_crab c5e7c79c2a Changes to egg sprite renderer 2025-01-10 17:21:04 -08:00
Plastered_Crab d4a44b4977 Merge branch 'main' into 1.21.1-breeding 2025-01-10 14:20:55 -08:00
Plastered_Crab f9f0668f6b Back to basics to start fresh later 2025-01-10 13:59:56 -08:00
Plastered_Crab d01beb3ba8 plz sprites be nice 2025-01-10 13:36:41 -08:00
Plastered_Crab 3cac052772 Bro why won't these sprites render 2025-01-10 13:29:51 -08:00
Plastered_Crab 84bc8bc426 Added New Egg sprites to test with 2025-01-10 10:02:50 -08:00
Plastered_Crab 98a25908c2 More stuff to try to get egg sprites working 2025-01-10 09:02:25 -08:00
plastered_crab 4ed3d41eb1 Progress towards trying to get Egg Items to render separately from the Egg Blocks 2025-01-09 22:04:02 -08:00
Plastered_Crab 672fd7365f OOPS I should have deleted this test Egg Move rule lmao 2025-01-09 15:42:25 -08:00
Plastered_Crab 39a16beb14 Breeding Logic reworked + Moves passed down work now according to games (Egg moves, Tm/Hm, learned moves, etc) + Added new datapackable Egg Move system for custom egg move rules 2025-01-09 15:38:58 -08:00
plastered_crab 60cb81362e Added some small langs 2025-01-09 07:46:53 -08:00
plastered_crab 088acb8e86 Added TMs, created TechnicalMachineRegistrySyncPacket.kt, TM json files are loading, TM machine GUI working better, altered obtain methods, and more 2025-01-08 20:04:24 -08:00
plastered_crab 77f59f06cb add tm_disk.geo file 2025-01-08 10:49:41 -08:00
plastered_crab a0882c6105 TM branch lives to see another day! (Still needs some issues fixed) 2025-01-08 10:49:21 -08:00
plastered_crab d50eb52172 Merge branch 'refs/heads/main' into 1.21.1-TMs 2025-01-07 19:49:30 -08:00
plastered_crab 32dbd1f8c9 Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2025-01-07 01:30:25 -08:00
plastered_crab bcc23ec4fc Merge branch 'refs/heads/main' into 1.21.1-breeding 2025-01-07 01:30:08 -08:00
luke 143fc8256b Added gen 2 non-regional egg textures 2025-01-02 20:23:34 -07:00
luke 70d753d1d3 Incubator hatch handling 2024-12-29 00:33:13 -07:00
luke 00a67b22df Fixed incubator interaction 2024-12-28 11:11:24 -07:00
luke 12811fe504 Implemented most incubator block logic 2024-12-27 18:42:40 -07:00
Plastered_Crab 5e5cadd314 Some more changes to unported files 2024-12-26 16:34:29 -08:00
Plastered_Crab 75b06f82f0 Oops forgor two files (they were not staged oops again) 2024-12-26 16:21:04 -08:00
Plastered_Crab 2ab050aa60 Oops forgor two files 2024-12-26 16:20:25 -08:00
Plastered_Crab 6d2f645e4a More work to port over TM stuff to 1.21.1 2024-12-26 16:10:04 -08:00
Plastered_Crab 9f937e29f2 Merge branch 'main' into 1.21.1-TMs 2024-12-26 13:54:27 -08:00
Plastered_Crab 40ffb99928 Merge branch 'main' into 1.21.1-breeding 2024-12-26 13:49:35 -08:00
Plastered_Crab 78fb8fb834 Merge remote-tracking branch 'origin/1.21.1-breeding' into 1.21.1-breeding 2024-12-26 13:40:45 -08:00
Plastered_Crab 614663a49c Merge branch 'main' into 1.21.1-breeding 2024-12-26 13:27:46 -08:00
plastered_crab ca9955f4cd Some more changes to try to prepare for eggs to render as items properly 2024-12-24 02:14:19 -08:00
Plastered_Crab 2f40c5d71b Pokemon cry when they hatch from eggs now! CUTE >:D 2024-12-20 15:22:01 -08:00
Plastered_Crab 1a84ca4ba0 Eggs in nests can now hatch!! (in a basic sense) they just don't cry when hatching yet though :( 2024-12-20 15:04:24 -08:00
Plastered_Crab 5f29986d41 Fix some persistent renderings of eggs in the incubator and ground and fix some item models 2024-12-20 12:49:02 -08:00
Plastered_Crab bfb63088c1 Increase time for wild nests to have a family 2024-12-20 10:12:33 -08:00
Plastered_Crab 626bd7f4f4 Now created eggs are always the base pre-evolution (yippeee) 2024-12-20 10:10:27 -08:00
Guilherme Pereira bff941264c temporary workaround for nest spawner not working 2024-12-20 12:30:10 -03:00
plastered_crab 10b472e980 More work on trying to get parent Pokemon to spawn at wild nests 2024-12-20 00:05:22 -08:00
Plastered_Crab 7888a8ac31 Added NestSpawner (and friends). Test to have Charizard guarding nest 2024-12-19 21:25:57 -08:00
plastered_crab d79adfc0be Very early prototype system for "Wild Pokemon Nests" 2024-12-19 16:31:38 -08:00
plastered_crab e4baf5fe3e Pastures recalculate breeding pairs after recalling pokemon 2024-12-19 13:38:07 -08:00
plastered_crab 3013c30084 Eggs will have two parents always now (don't ask about before...) 2024-12-19 13:28:29 -08:00
plastered_crab fc10b550cd Fix Egg position in DisplayCaseRenderer 2024-12-19 13:14:43 -08:00
plastered_crab 514787a1c4 Eggs can now be placed in Nests, Incubators, Display Cases, and on the ground! 2024-12-19 12:32:46 -08:00
plastered_crab c32f3c5581 Fix some issues with the Egg Incubator block and renderer 2024-12-19 01:27:39 -08:00
plastered_crab 94b9b7ade3 Add Egg Incubator block and entity/renderer classes 2024-12-19 00:59:54 -08:00
Plastered_Crab ee33b4292c Added more textures for some species eggs (thank you Cumulo) 2024-12-18 15:51:43 -08:00
Plastered_Crab 271e74d6f2 Added more textures for some species eggs (thank you Cumulo) 2024-12-18 15:50:53 -08:00
Plastered_Crab 2ea41c3dae Adding some placeholder starter/legendary egg textures for testing 2024-12-18 14:47:49 -08:00
Plastered_Crab a6d93aba17 EGGS RENDER IN NESTS NOW!!! WOOOO 2024-12-18 13:55:44 -08:00
Plastered_Crab db1711b76f More attempts to render the eggs (I hate these things) 2024-12-18 10:23:34 -08:00
Plastered_Crab ab5b2e8fdf Add EggComponent and try to get Server to sync to Client (I AM SO CLOSE) 2024-12-17 17:02:16 -08:00
Plastered_Crab efa4028948 Change Egg data to prevent cheesing, and try to make them render via texture instead of overlay 2024-12-17 14:13:29 -08:00
Plastered_Crab abc61a96c8 Merge branch 'main' into 1.21.1-TMs 2024-12-17 10:21:05 -08:00
Plastered_Crab 65529474cb Merge main into Breeding branch to keep it up to date 2024-12-17 10:19:04 -08:00
Plastered_Crab 7d756249ae Merge branch 'main' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/fishing/FishingBait.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/moves/MoveSet.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/PokemonPastureBlockEntity.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/entity/fishing/PokeRodFishingBobberEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2024-12-17 10:14:54 -08:00
Plastered_Crab b1d10822d4 Merge branch 'main' into 1.21.1-TMs 2024-12-12 08:35:22 -08:00
Plastered_Crab 09d984456d Start of new branch for 1.21.1 version of TM Machine code (this was painful) 2024-11-26 15:04:32 -08:00
apion 3fcfd2ff6d We can go into worlds pog 2024-08-02 18:06:02 -04:00
apion 98545541e0 Port to 1.21 (it compiles at least) 2024-08-02 05:44:33 -04:00
apion 56820ad626 Merge branch 'refs/heads/sega_bass_pro_fishing_2' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonItemTags.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/BerryBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/PokemonPastureBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/atlas/CobblemonAtlases.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/BasicBlockEntityRenderState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/BerryBlockRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/FossilAnalyzerRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/RestorationTankRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/layer/CobblemonRenderLayers.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/item/group/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
#	common/src/main/resources/cobblemon-common.accesswidener
#	common/src/main/resources/data/cobblemon/loot_table/blocks/egg.json
#	common/src/main/resources/data/cobblemon/tags/item/defaulting_poke_balls.json
2024-08-02 03:03:41 -04:00
Plastered_Crab 64546b25f2 - Tweaked Breeding Logic for passing IVs 2024-02-28 12:35:22 -08:00
Plastered_Crab d5496fb465 - Adjusted the Heart Breeding particles to look more vanilla 2024-02-28 12:31:57 -08:00
jeffw773 bcd2474d5f - Pokemon emit Heart particles when bred + logic for nests with eggs is tweaked 2024-02-27 22:13:16 -08:00
apion e331b959c8 Fixed IV logic 2024-02-25 07:49:20 -05:00
apion ce159ac742 Allow insertion of eggs to nests 2024-02-14 17:20:09 -05:00
apion 3e4eaf0397 Fixed another persistence issue 2024-02-14 13:12:30 -05:00
apion 2a2308b4a4 Fixed some desync/persistence issues 2024-02-14 12:54:19 -05:00
apion 47511bac89 Made eggs hatchable 2024-02-13 21:54:47 -05:00
apion c6a5e69f91 Merge remote-tracking branch 'origin/main' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/FossilAnalyzerRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/FossilTubeRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/layer/CobblemonRenderLayers.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
2024-02-13 20:58:48 -05:00
apion 32a9185cc1 Made egg blocks drop their egg 2024-02-13 20:09:25 -05:00
apion ec54aeb1fd Fixed crash if parent pokemon had no hidden ability options, sped up egg sprite rendering a bit 2024-02-13 17:55:27 -05:00
apion 9aab1113fd Fixed inventory lighting 2024-02-13 16:21:51 -05:00
apion 86578cd266 Got 3D sprite model generation working properly, except for lighting in inventory. Also, licenses 2024-02-13 09:43:03 -05:00
apion 566d74a638 Move egg inventory rendering to generate and use BakedModels 2024-02-12 14:21:42 -05:00
apion 23988b55a1 Egg inventory rendering fully working 2024-02-10 01:06:57 -05:00
apion d5418ed5c7 Got egg rendering working in a less sus way 2024-02-09 22:54:06 -05:00
apion 5318b1470d Eggs now kinda work in inventory 2024-01-30 23:29:57 -05:00
apion 0ca91eb071 Merge branch 'main' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/BerryBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/BerryBlockRenderer.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2024-01-30 22:58:08 -05:00
apion 7d31719cd1 Update patterns to include inv sprites 2024-01-30 22:51:45 -05:00
apion 25ed3d28f4 Added placeable eggs 2024-01-30 20:22:21 -05:00
apion 5df64d038b Start working on adding egg item, and egg item rendering 2024-01-30 16:40:56 -05:00
apion ec6d838ea6 Fixed render distances for all BERs 2024-01-08 13:27:34 -05:00
apion 6bea8aafe6 Added a bunch of egg patterns 2024-01-08 13:12:13 -05:00
Lily 5bbf5d2d85 Added documentation to BreedingLogic, added Gender and PokeBall functionality 2024-01-02 21:59:59 -05:00
apion 6d09590374 Fixed lighting and tinting issues on eggs 2024-01-02 14:18:18 -05:00
apion 0e0ebc0b48 Pasture blocks now properly update nest entities, fix some rendering bugs, fix block entity sync issues 2024-01-02 12:40:27 -05:00
apion 0c4a4ddda1 Generified berry rendering optimizations and apply them to eggs, added new data class for egg pokemon, added a bunch of nbt serializer stuff 2024-01-02 10:01:11 -05:00
apion 1efeddc875 Merge branch 'berry-rendering' into breeding 2024-01-02 07:56:31 -05:00
apion 82c6e8f42d Merge branch 'main' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
2024-01-02 07:56:19 -05:00
apion 5ff522bb85 Added egg rendering, and nest block entity 2024-01-02 06:59:52 -05:00
apion b2b73575d4 Add some API stuff 2024-01-02 04:41:41 -05:00
apion d7bbccab08 Move BerryBlockRenderState to be client specific 2024-01-02 04:26:52 -05:00
apion 21b8fe8474 Fix flower texture 2024-01-02 04:15:55 -05:00
apion 94c210efd4 Big cool rendering stuff 2024-01-02 04:06:46 -05:00
apion d3100be95a Refined breeding logic 2023-12-29 23:17:51 -05:00
apion 3353c15af6 Add nest detection in pasture block 2023-12-20 23:26:35 -05:00
apion 1cced11431 Merge conflicts lul 2023-12-20 22:54:06 -05:00
apion 646ad5606a Merge branch 'archaeology' into breeding
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2023-12-20 22:50:17 -05:00
apion 8214e848f9 Added nest blocks and oak twig texture + licenses :D 2023-12-20 22:18:26 -05:00
apion ece9c63121 Added breeding events, refactored logic a bit 2023-12-20 20:16:08 -05:00
apion ce81f07aa2 Started work on pasture behavior for breeding 2023-12-19 19:48:47 -05:00
apion 9bcaa6dfad Merge branch 'main' into breeding 2023-12-16 21:19:45 -05:00
apion 295efe8c11 Merge branch 'breeding' of https://gitlab.com/cable-mc/cobblemon into breeding 2023-12-16 21:17:43 -05:00
apion 5cff811aab Merge branch 'main' into breeding 2023-12-16 21:14:57 -05:00
apion 00e721d714 Add nest assets 2023-11-20 07:01:10 -05:00
Nick 0c602027cb Begin work on an API for breeding logic 2023-10-26 22:17:55 -07:00
Lily 7070369cea breeding logic 2023-10-23 22:10:36 -04:00
2616 changed files with 45133 additions and 285 deletions
+6
View File
@@ -0,0 +1,6 @@
### Developer
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
- Renamed heaps of things in the spawning system to make more sense.
@@ -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.mixin.invoker;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.inventory.MenuType.MenuSupplier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(MenuType.class)
public interface MenuTypeInvoker {
@Invoker("<init>")
static <T extends AbstractContainerMenu> MenuType<T> create(MenuSupplier<T> factory, FeatureFlagSet requiredFeatures) {
throw new UnsupportedOperationException();
}
}
@@ -56,10 +56,7 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
import com.cobblemon.mod.common.api.storage.player.adapter.*
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
@@ -195,6 +192,8 @@ object Cobblemon {
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
}
this.loadConfig()
implementation.registerRecipeSerializers()
implementation.registerRecipeTypes()
implementation.registerPermissionValidator()
@@ -221,7 +220,6 @@ object Cobblemon {
ExperienceGroups.registerDefaults()
CaptureCalculators.registerDefaults()
this.loadConfig()
// CobblemonBlockPredicates.touch()
CobblemonOrePlacedFeatures.register()
CobblemonPlacedFeatures.register()
@@ -358,8 +356,12 @@ object Cobblemon {
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
pokedexNbtFactory.setup(server)
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
tmMoveFactory.setup(server)
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
@@ -386,8 +388,14 @@ object Cobblemon {
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
pokedexMongoFactory.setup(server)
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
)
tmMovesMongoFactory.setup(server)
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
} catch (e: ClassNotFoundException) {
LOGGER.error("MongoDB driver not found.")
@@ -18,6 +18,7 @@ object CobblemonActivities {
val POKEMON_SLEEPING_ACTIVITY = register(Activity("pokemon_sleeping"))
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
val GUARD_NEST = register(Activity("guard_nest"))
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -40,6 +40,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@JvmField
val TM_SHELF: BlockEntityType<TMShelfBlockEntity> = this.create("tm_shelf", BlockEntityType.Builder.of(::TMShelfBlockEntity, CobblemonBlocks.TM_SHELF).build(null))
@JvmField
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@@ -49,6 +52,13 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
@JvmField
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
@JvmField
val TM_BLOCK: BlockEntityType<TMBlockEntity> = this.create("tm_block", BlockEntityType.Builder.of(::TMBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
@JvmField
val GROTTO_BLOCK: BlockEntityType<GrottoBlockEntity> = this.create("grotto_block",
BlockEntityType.Builder.of(::GrottoBlockEntity, CobblemonBlocks.GROTTO_BLOCK).build(null)
)
@JvmField
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
@@ -83,6 +93,46 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
).build(null)
)
val NEST: BlockEntityType<NestBlockEntity> = this.create("nest",
BlockEntityType.Builder.of(
::NestBlockEntity,
CobblemonBlocks.NEST_STICK,
CobblemonBlocks.NEST_LEAF,
CobblemonBlocks.NEST_STONE,
CobblemonBlocks.NEST_DEEPSLATE,
CobblemonBlocks.NEST_BASALT,
CobblemonBlocks.NEST_SNOW,
CobblemonBlocks.NEST_NETHER,
CobblemonBlocks.NEST_SOUL_SAND,
CobblemonBlocks.NEST_WARM_OCEAN,
CobblemonBlocks.NEST_SEAGRASS,
CobblemonBlocks.NEST_LUSH,
CobblemonBlocks.NEST_HAY,
CobblemonBlocks.NEST_SAND,
CobblemonBlocks.NEST_RED_SAND,
CobblemonBlocks.NEST_CHORUS,
CobblemonBlocks.WATER_NEST,
CobblemonBlocks.CAVE_NEST,
CobblemonBlocks.BASIC_NEST
).build(null))
val EGG: BlockEntityType<EggBlockEntity> = this.create("egg",
BlockEntityType.Builder.of(
::EggBlockEntity,
CobblemonBlocks.EGG
).build(null)
)
@JvmField
val INCUBATOR: BlockEntityType<IncubatorBlockEntity> = this.create("incubator",
BlockEntityType.Builder.of(
::IncubatorBlockEntity,
CobblemonBlocks.INCUBATOR
).build(null)
)
@JvmField
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
@@ -98,12 +148,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
)
/*@JvmField
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
CobblemonBlocks.LURE_CAKE
).build(null)*/
@JvmField
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
"lure_cake",
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
import com.cobblemon.mod.common.block.tm.TMBlock
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
@@ -32,6 +33,7 @@ import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.EntityType
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.item.Item
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
@@ -243,31 +245,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@@ -276,6 +278,51 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val TYPE_GEM_CORE = this.create("type_gem_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0f, 10.0f)))
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
@JvmField val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("type_gem_block_normal")
@JvmField val TYPE_GEM_BLOCK_FIRE = typeGemBlock("type_gem_block_fire")
@JvmField val TYPE_GEM_BLOCK_WATER = typeGemBlock("type_gem_block_water")
@JvmField val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("type_gem_block_electric")
@JvmField val TYPE_GEM_BLOCK_GRASS = typeGemBlock("type_gem_block_grass")
@JvmField val TYPE_GEM_BLOCK_ICE = typeGemBlock("type_gem_block_ice")
@JvmField val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("type_gem_block_fighting")
@JvmField val TYPE_GEM_BLOCK_POISON = typeGemBlock("type_gem_block_poison")
@JvmField val TYPE_GEM_BLOCK_GROUND = typeGemBlock("type_gem_block_ground")
@JvmField val TYPE_GEM_BLOCK_FLYING = typeGemBlock("type_gem_block_flying")
@JvmField val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("type_gem_block_psychic")
@JvmField val TYPE_GEM_BLOCK_BUG = typeGemBlock("type_gem_block_bug")
@JvmField val TYPE_GEM_BLOCK_ROCK = typeGemBlock("type_gem_block_rock")
@JvmField val TYPE_GEM_BLOCK_GHOST = typeGemBlock("type_gem_block_ghost")
@JvmField val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("type_gem_block_dragon")
@JvmField val TYPE_GEM_BLOCK_DARK = typeGemBlock("type_gem_block_dark")
@JvmField val TYPE_GEM_BLOCK_STEEL = typeGemBlock("type_gem_block_steel")
@JvmField val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("type_gem_block_fairy")
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
@JvmField val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("type_gem_cluster_normal", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("type_gem_cluster_normal"))
@JvmField val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("type_gem_cluster_fire", TYPE_GEM_BLOCK_FIRE, cobblemonResource("type_gem_cluster_fire"))
@JvmField val TYPE_GEM_CLUSTER_WATER = typeGemCluster("type_gem_cluster_water", TYPE_GEM_BLOCK_WATER, cobblemonResource("type_gem_cluster_water"))
@JvmField val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("type_gem_cluster_electric", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("type_gem_cluster_electric"))
@JvmField val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("type_gem_cluster_grass", TYPE_GEM_BLOCK_GRASS, cobblemonResource("type_gem_cluster_grass"))
@JvmField val TYPE_GEM_CLUSTER_ICE = typeGemCluster("type_gem_cluster_ice", TYPE_GEM_BLOCK_ICE, cobblemonResource("type_gem_cluster_ice"))
@JvmField val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("type_gem_cluster_fighting", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("type_gem_cluster_fighting"))
@JvmField val TYPE_GEM_CLUSTER_POISON = typeGemCluster("type_gem_cluster_poison", TYPE_GEM_BLOCK_POISON, cobblemonResource("type_gem_cluster_poison"))
@JvmField val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("type_gem_cluster_ground", TYPE_GEM_BLOCK_GROUND, cobblemonResource("type_gem_cluster_ground"))
@JvmField val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("type_gem_cluster_flying", TYPE_GEM_BLOCK_FLYING, cobblemonResource("type_gem_cluster_flying"))
@JvmField val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("type_gem_cluster_psychic", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("type_gem_cluster_psychic"))
@JvmField val TYPE_GEM_CLUSTER_BUG = typeGemCluster("type_gem_cluster_bug", TYPE_GEM_BLOCK_BUG, cobblemonResource("type_gem_cluster_bug"))
@JvmField val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("type_gem_cluster_rock", TYPE_GEM_BLOCK_ROCK, cobblemonResource("type_gem_cluster_rock"))
@JvmField val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("type_gem_cluster_ghost", TYPE_GEM_BLOCK_GHOST, cobblemonResource("type_gem_cluster_ghost"))
@JvmField val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("type_gem_cluster_dragon", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("type_gem_cluster_dragon"))
@JvmField val TYPE_GEM_CLUSTER_DARK = typeGemCluster("type_gem_cluster_dark", TYPE_GEM_BLOCK_DARK, cobblemonResource("type_gem_cluster_dark"))
@JvmField val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("type_gem_cluster_steel", TYPE_GEM_BLOCK_STEEL, cobblemonResource("type_gem_cluster_steel"))
@JvmField val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("type_gem_cluster_fairy", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("type_gem_cluster_fairy"))
@JvmField
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
@@ -482,9 +529,26 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
val TM_MACHINE = create(
"tm_machine",
TMBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_GREEN)
.sound(SoundType.METAL)
.pushReaction(PushReaction.BLOCK)
.strength(2F)
.noOcclusion()
.lightLevel { if (it.getValue(TMBlock.ON) as Boolean) 10 else 0 }
)
)
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@JvmField
val TM_SHELF = create("tm_shelf", TMShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
@JvmField
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
@@ -614,6 +678,61 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val YACHE_BERRY = this.berryBlock("yache")
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
val NEST_STICK = nestBlock(NestBlock.NestVariant.STICK)
val NEST_LEAF = nestBlock(NestBlock.NestVariant.LEAF)
val NEST_STONE = nestBlock(NestBlock.NestVariant.STONE)
val NEST_DEEPSLATE = nestBlock(NestBlock.NestVariant.DEEPSLATE)
val NEST_BASALT = nestBlock(NestBlock.NestVariant.BASALT)
val NEST_SNOW = nestBlock(NestBlock.NestVariant.SNOW)
val NEST_NETHER = this.create(
"nest_nether",
NestBlock(NestBlock.NestVariant.NETHER, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 2 })
)
val NEST_SOUL_SAND = nestBlock(NestBlock.NestVariant.SOUL_SAND)
val NEST_WARM_OCEAN = this.create(
"nest_warm_ocean",
NestBlock(NestBlock.NestVariant.WARM_OCEAN, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 6 })
)
val NEST_SEAGRASS = nestBlock(NestBlock.NestVariant.SEAGRASS)
val NEST_LUSH = nestBlock(NestBlock.NestVariant.LUSH)
val NEST_HAY = nestBlock(NestBlock.NestVariant.HAY)
val NEST_SAND = nestBlock(NestBlock.NestVariant.SAND)
val NEST_RED_SAND = nestBlock(NestBlock.NestVariant.RED_SAND)
val NEST_CHORUS = nestBlock(NestBlock.NestVariant.CHORUS)
val BASIC_NEST = nestBlock(NestBlock.NestVariant.BASIC)
val CAVE_NEST = nestBlock(NestBlock.NestVariant.CAVE)
//val NETHER_NEST = nestBlock(NestBlock.NestVariant.NETHER)
val WATER_NEST = nestBlock(NestBlock.NestVariant.WATER)
val EGG = this.create("egg", EggBlock(BlockBehaviour.Properties
.ofFullCopy(Blocks.DRAGON_EGG)
.pushReaction(PushReaction.BLOCK)
.sound(SoundType.BONE_BLOCK)
))
@JvmField
val GROTTO_BLOCK = this.create("grotto_block", GrottoBlock(BlockBehaviour.Properties.of()
.mapColor(MapColor.STONE)
.strength(1.5F)
.pushReaction(PushReaction.BLOCK)
))
@JvmField
val INCUBATOR = this.create("incubator", IncubatorBlock(BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_LIGHT_GRAY)
.sound(SoundType.METAL)
.strength(Blocks.COAL_BLOCK.defaultDestroyTime(), Blocks.DIAMOND_ORE.explosionResistance)
.noOcclusion()
.pushReaction(PushReaction.BLOCK)
.lightLevel { state ->
if (state.getValue(IncubatorBlock.HAS_EGG)) 5 else 0 // Emit 5 light when HAS_EGG is true
}
))
init {
/**
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
@@ -642,6 +761,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
return block
}
private fun nestBlock(variant: NestBlock.NestVariant): NestBlock {
val identifier = cobblemonResource(variant.serializedName)
val block = NestBlock(variant, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY))
return this.create(identifier.path, block)
}
/**
* Calls helper method from Vanilla
*/
@@ -677,4 +802,26 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
return this.create(name, block)
}
private fun typeGemBlock(name: String): Block {
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0f, 6.0f)))
val identifier = cobblemonResource(name)
typeGemBlocks[identifier] = typeGemBlock
return typeGemBlock
}
fun typeGemBlocks() = typeGemBlocks.toMap()
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0f, 3.0f).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
val identifier = cobblemonResource(name)
typeGemClusters[identifier] = gemClusterBlock
return gemClusterBlock
}
fun typeGemClusters() = typeGemClusters.toMap()
}
@@ -60,6 +60,8 @@ object CobblemonCommands {
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
GivePokemonEggCommand.register(dispatcher)
GiveAllPokemonEggCommand.register(dispatcher)
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
UnlockPCBoxWallpaperCommand.register(dispatcher)
@@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.PreparableReloadListener
import net.minecraft.tags.TagKey
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.GameRules
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.biome.Biome
@@ -10,6 +10,16 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.PotComponent
import com.cobblemon.mod.common.item.components.*
import com.cobblemon.mod.common.item.components.BaitEffectsComponent
import com.cobblemon.mod.common.item.components.FlavourComponent
import com.cobblemon.mod.common.item.components.FoodColourComponent
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.block.entity.EggBlockEntity
import com.cobblemon.mod.common.item.components.HeldItemCapableComponent
import com.cobblemon.mod.common.item.components.PokemonItemComponent
import com.cobblemon.mod.common.item.components.RodBaitComponent
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.item.components.EggComponent
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.component.DataComponentType
@@ -69,6 +79,17 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.persistent(MobEffectsComponent.CODEC)
.networkSynchronized(MobEffectsComponent.PACKET_CODEC)
.build())
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
.persistent(TMMoveComponent.CODEC)
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
.build())
val EGG_DATA: DataComponentType<EggComponent> = create("egg", DataComponentType.builder<EggComponent>()
.persistent(EggComponent.CODEC) // For saving/loading
.networkSynchronized(EggComponent.PACKET_CODEC) // For network synchronization
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
@@ -80,6 +101,8 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:egg_data"), EGG_DATA)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.NestBlock
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
@@ -49,13 +50,13 @@ import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.item.Item.Properties
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.HangingSignItem
import net.minecraft.world.item.Item
import net.minecraft.world.item.Item.Properties
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.Rarity
@@ -467,6 +468,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
@JvmField
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -588,6 +593,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
val NEST_STICK = blockItem(NestBlock.NestVariant.STICK.serializedName, CobblemonBlocks.NEST_STICK)
val NEST_LEAF = blockItem(NestBlock.NestVariant.LEAF.serializedName, CobblemonBlocks.NEST_LEAF)
val NEST_STONE = blockItem(NestBlock.NestVariant.STONE.serializedName, CobblemonBlocks.NEST_STONE)
val NEST_DEEPSLATE = blockItem(NestBlock.NestVariant.DEEPSLATE.serializedName, CobblemonBlocks.NEST_DEEPSLATE)
val NEST_BASALT = blockItem(NestBlock.NestVariant.BASALT.serializedName, CobblemonBlocks.NEST_BASALT)
val NEST_SNOW = blockItem(NestBlock.NestVariant.SNOW.serializedName, CobblemonBlocks.NEST_SNOW)
val NEST_NETHER = blockItem(NestBlock.NestVariant.NETHER.serializedName, CobblemonBlocks.NEST_NETHER)
val NEST_SOUL_SAND = blockItem(NestBlock.NestVariant.SOUL_SAND.serializedName, CobblemonBlocks.NEST_SOUL_SAND)
val NEST_WARM_OCEAN = blockItem(NestBlock.NestVariant.WARM_OCEAN.serializedName, CobblemonBlocks.NEST_WARM_OCEAN)
val NEST_SEAGRASS = blockItem(NestBlock.NestVariant.SEAGRASS.serializedName, CobblemonBlocks.NEST_SEAGRASS)
val NEST_LUSH = blockItem(NestBlock.NestVariant.LUSH.serializedName, CobblemonBlocks.NEST_LUSH)
val NEST_HAY = blockItem(NestBlock.NestVariant.HAY.serializedName, CobblemonBlocks.NEST_HAY)
val NEST_SAND = blockItem(NestBlock.NestVariant.SAND.serializedName, CobblemonBlocks.NEST_SAND)
val NEST_RED_SAND = blockItem(NestBlock.NestVariant.RED_SAND.serializedName, CobblemonBlocks.NEST_RED_SAND)
val NEST_CHORUS = blockItem(NestBlock.NestVariant.CHORUS.serializedName, CobblemonBlocks.NEST_CHORUS)
val BASIC_NEST = blockItem(NestBlock.NestVariant.BASIC.serializedName, CobblemonBlocks.BASIC_NEST)
val CAVE_NEST = blockItem(NestBlock.NestVariant.CAVE.serializedName, CobblemonBlocks.CAVE_NEST)
val WATER_NEST = blockItem(NestBlock.NestVariant.WATER.serializedName, CobblemonBlocks.WATER_NEST)
@JvmField
val INCUBATOR = blockItem("incubator", CobblemonBlocks.INCUBATOR)
@JvmField
val GROTTO_BLOCK = blockItem("grotto_block", CobblemonBlocks.GROTTO_BLOCK)
@JvmField
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
@@ -917,7 +949,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
val DESTINY_KNOT = heldItem("destiny_knot", null, if (Cobblemon.config.canDestinyKnotBreak) Properties().durability(Cobblemon.config.destinyKnotDurability) else Properties())
@JvmField
val DRAGON_FANG = heldItem("dragon_fang")
@JvmField
@@ -1173,6 +1205,93 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val TYPE_GEM_CORE = blockItem("type_gem_core", CobblemonBlocks.TYPE_GEM_CORE)
/* @JvmField
val TYPE_GEM_BLOCK = blockItem("type_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK)
@JvmField
val TYPE_GEM_CLUSTER = blockItem("type_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER)
@JvmField
val LARGE_TYPE_GEM = blockItem("large_type_gem", CobblemonBlocks.LARGE_TYPE_GEM)
@JvmField
val MEDIUM_TYPE_GEM = blockItem("medium_type_gem", CobblemonBlocks.MEDIUM_TYPE_GEM)
@JvmField
val SMALL_TYPE_GEM = blockItem("small_type_gem", CobblemonBlocks.SMALL_TYPE_GEM)*/
@JvmField
val TYPE_GEM_CLUSTER_NORMAL = blockItem("type_gem_cluster_normal", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
@JvmField
val TYPE_GEM_CLUSTER_FIRE = blockItem("type_gem_cluster_fire", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
@JvmField
val TYPE_GEM_CLUSTER_WATER = blockItem("type_gem_cluster_water", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
@JvmField
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("type_gem_cluster_electric", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
@JvmField
val TYPE_GEM_CLUSTER_GRASS = blockItem("type_gem_cluster_grass", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
@JvmField
val TYPE_GEM_CLUSTER_ICE = blockItem("type_gem_cluster_ice", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
@JvmField
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("type_gem_cluster_fighting", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
@JvmField
val TYPE_GEM_CLUSTER_POISON = blockItem("type_gem_cluster_poison", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
@JvmField
val TYPE_GEM_CLUSTER_GROUND = blockItem("type_gem_cluster_ground", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
@JvmField
val TYPE_GEM_CLUSTER_FLYING = blockItem("type_gem_cluster_flying", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
@JvmField
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("type_gem_cluster_psychic", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
@JvmField
val TYPE_GEM_CLUSTER_BUG = blockItem("type_gem_cluster_bug", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
@JvmField
val TYPE_GEM_CLUSTER_ROCK = blockItem("type_gem_cluster_rock", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
@JvmField
val TYPE_GEM_CLUSTER_GHOST = blockItem("type_gem_cluster_ghost", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
@JvmField
val TYPE_GEM_CLUSTER_DRAGON = blockItem("type_gem_cluster_dragon", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
@JvmField
val TYPE_GEM_CLUSTER_DARK = blockItem("type_gem_cluster_dark", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
@JvmField
val TYPE_GEM_CLUSTER_STEEL = blockItem("type_gem_cluster_steel", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
@JvmField
val TYPE_GEM_CLUSTER_FAIRY = blockItem("type_gem_cluster_fairy", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
@JvmField
val TYPE_GEM_BLOCK_NORMAL = blockItem("type_gem_block_normal", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
@JvmField
val TYPE_GEM_BLOCK_FIRE = blockItem("type_gem_block_fire", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
@JvmField
val TYPE_GEM_BLOCK_WATER = blockItem("type_gem_block_water", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
@JvmField
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("type_gem_block_electric", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
@JvmField
val TYPE_GEM_BLOCK_GRASS = blockItem("type_gem_block_grass", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
@JvmField
val TYPE_GEM_BLOCK_ICE = blockItem("type_gem_block_ice", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
@JvmField
val TYPE_GEM_BLOCK_FIGHTING = blockItem("type_gem_block_fighting", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
@JvmField
val TYPE_GEM_BLOCK_POISON = blockItem("type_gem_block_poison", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
@JvmField
val TYPE_GEM_BLOCK_GROUND = blockItem("type_gem_block_ground", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
@JvmField
val TYPE_GEM_BLOCK_FLYING = blockItem("type_gem_block_flying", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
@JvmField
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("type_gem_block_psychic", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
@JvmField
val TYPE_GEM_BLOCK_BUG = blockItem("type_gem_block_bug", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
@JvmField
val TYPE_GEM_BLOCK_ROCK = blockItem("type_gem_block_rock", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
@JvmField
val TYPE_GEM_BLOCK_GHOST = blockItem("type_gem_block_ghost", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
@JvmField
val TYPE_GEM_BLOCK_DRAGON = blockItem("type_gem_block_dragon", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
@JvmField
val TYPE_GEM_BLOCK_DARK = blockItem("type_gem_block_dark", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
@JvmField
val TYPE_GEM_BLOCK_STEEL = blockItem("type_gem_block_steel", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
@JvmField
val TYPE_GEM_BLOCK_FAIRY = blockItem("type_gem_block_fairy", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
@JvmField
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
Component.translatable("item.minecraft.fishing_rod").blue(),
@@ -1397,6 +1516,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
@JvmField
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
@JvmField
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Item.Properties()))
@JvmField
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
// Type Gems
@JvmField
@@ -1437,6 +1560,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val FAIRY_GEM = noSettingsItem("fairy_gem")
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
@JvmField
val POKEMON_EGG = eggItem("egg", CobblemonBlocks.EGG, Item.Properties().stacksTo(1))
private fun eggItem(name: String, block: Block, settings: Item.Properties = Item.Properties()): BlockItem = this.create(name, BlockItem(block, settings))
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
@@ -1530,9 +1657,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return create(name, RegionalFoodItem(properties))
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
private fun heldItem(name: String, remappedName: String? = null, settings:Item.Properties = Item.Properties()): CobblemonItem = create(
name,
CobblemonItem(Item.Properties()).also {
CobblemonItem(settings).also {
if (remappedName != null) {
CobblemonHeldItemManager.registerRemap(it, remappedName)
}
@@ -10,15 +10,18 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.UUIDUtil
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import java.util.Optional
import java.util.UUID
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
object CobblemonMemories {
val memories = mutableMapOf<String, MemoryModuleType<*>>()
@@ -33,6 +36,11 @@ object CobblemonMemories {
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
val DIALOGUES = register<List<ActiveDialogue>>("npc_dialogues")
val ACTIVE_ACTION_EFFECT = register<ActionEffectContext>("active_action_effect")
val POTENTIAL_MATE = register<PokemonEntity>("potential_mate")
val MATE_SCAN_COOLDOWN = register("mate_scan_cooldown", PrimitiveCodec.BOOL)
val NEST_LOCATION = register<BlockPos>("nest_location")
val PATERNAL_NEST_LOCATION = register<BlockPos>("paternal_nest")
val IRRITATED_AT = register<UUID>("irritated_at")
val POKEMON_FLYING = register("pokemon_flying", PrimitiveCodec.BOOL)
val POKEMON_DROWSY = register("pokemon_drowsy", PrimitiveCodec.BOOL)
val POKEMON_SLEEPING = register("pokemon_sleeping", PrimitiveCodec.BOOL)
@@ -41,6 +49,16 @@ object CobblemonMemories {
val TARGETED_BATTLE_POKEMON = register<UUID>("targeted_battle_pokemon")
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
val EGG_THIEF = register<Player>("egg_thief") // Stores the player stealing the egg
val EGG_LOCATION = register<BlockPos>("egg_location") // Stores the last known location of the egg
val EGG_IN_NEST = register("egg_in_nest", PrimitiveCodec.BOOL)
val READY_TO_GRAB_EGG = register("ready_to_grab_egg", PrimitiveCodec.BOOL)
val HOLDING_EGG = register("holding_egg", PrimitiveCodec.BOOL)
val EGG_SEARCH_COUNTDOWN = register ("egg_search_countdown", PrimitiveCodec.BOOL)
val NESTING_MATERIAL_LOCATION = register<BlockPos>("nesting_material_location")
val SENSED_NEST_MATERIAL = register<ItemEntity>("sensed_nest_material")
val IDEAL_NEST_LOCATION = register<BlockPos>("ideal_nest_location")
val READY_TO_BUILD_NEST = register("ready_to_build_nest", PrimitiveCodec.BOOL)
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
@@ -10,9 +10,8 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.block.tm.TMMScreenHandler
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.client.gui.screens.MenuScreens
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
@@ -22,6 +21,7 @@ import net.minecraft.world.inventory.MenuType
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tmm_screen", ::TMMScreenHandler)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
@@ -26,6 +26,8 @@ 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.CraftBlankTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.CraftTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
@@ -44,6 +46,8 @@ import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
import com.cobblemon.mod.common.client.net.storage.RemoveClientPokemonHandler
import com.cobblemon.mod.common.client.net.storage.SwapClientPokemonHandler
import com.cobblemon.mod.common.client.net.storage.party.InitializePartyHandler
@@ -67,6 +71,7 @@ import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.*
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
@@ -108,9 +113,9 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
@@ -142,6 +147,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.breeding.TriggerEggHatchPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
@@ -150,6 +156,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.debug.ServerboundUpdateRidingStatRangePacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.messages.server.debug.UpdateGrottoBlockPacket
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
@@ -171,6 +178,12 @@ import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPa
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
@@ -188,7 +201,9 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.breeding.TriggerEggHatchHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
@@ -197,6 +212,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveS
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
import com.cobblemon.mod.common.net.serverhandling.debug.UpdateGrottoBlockHandler
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
@@ -222,6 +238,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonToPartyHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonToPCHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
@@ -229,6 +251,7 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -385,6 +408,8 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BreedingRuleRegistrySyncPacket.ID, BreedingRuleRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(HabitatSyncPacket.ID, HabitatSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -560,9 +585,19 @@ object CobblemonNetwork {
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
// Breeding Packets
list.add(PacketRegisterInfo(TriggerEggHatchPacket.ID, TriggerEggHatchPacket::decode, TriggerEggHatchHandler))
// Behaviour Packets
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
// TMs
list.add(PacketRegisterInfo(CraftBlankTMPacket.ID, CraftBlankTMPacket::decode, CraftBlankTMPacketHandler))
list.add(PacketRegisterInfo(CraftTMPacket.ID, CraftTMPacket::decode, CraftTMPacketHandler))
// Debug / cheats
list.add(PacketRegisterInfo(UpdateGrottoBlockPacket.ID, UpdateGrottoBlockPacket::decode, UpdateGrottoBlockHandler))
return list
}
@@ -39,6 +39,11 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("incense_sweet")
@JvmField
val INCENSE_SWEET = create(INCENSE_SWEET_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.INCENSE_SWEET), 0, 1))
@JvmField
val GROTTO_BLOCK_KEY: ResourceKey<PoiType> = createKey("grotto_block")
@JvmField
val GROTTO_BLOCK = create(GROTTO_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.GROTTO_BLOCK), 0, 1))
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
@@ -8,10 +8,7 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
@@ -25,6 +22,11 @@ object CobblemonSensors {
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
val MATE_SENSOR = register("mate_sensor", ::MateSensor)
val NEST_SENSOR = register("nest_sensor", ::NestSensor)
val EGG_SENSOR = register("egg_sensor", :: EggSensor)
val NEST_MATERIAL_SENSOR = register("nest_material_sensor", :: NestMaterialSensor)
val IDEAL_NEST_LOCATION_SENSOR = register("ideal_nest_location_sensor", ::IdealNestPlacementSensor)
// val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val POKEMON_DROWSY = register("pokemon_drowsy", ::DrowsySensor)
@@ -94,6 +94,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val POKEDEX_SCAN_ZOOM_INCREMENT = this.create("item.pokedex.scan_zoom_increment")
@JvmField
val BREEDING_EGG_HATCH = this.create("breeding.egg_hatch")
@JvmField
val ITEM_USE = this.create("item.use")
@JvmField
@@ -195,6 +198,18 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
@JvmField
val TM_USE = this.create("tm.use")
@JvmField
val TMM_CRAFT = this.create("tmm.craft")
@JvmField
val TMM_CRAFT_BLANK = this.create("tmm.craft_blank")
@JvmField
val TMM_OFF = this.create("tmm.off")
@JvmField
val TMM_ON = this.create("tmm.on")
@JvmField
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
@JvmField
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import java.util.Optional
import java.util.*
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
@@ -29,42 +29,6 @@ class PlantTumblestoneCriterion(
}
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
return context.block.canGrow(context.pos, player.level())
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
}
}
///**
// * A criterion condition for when a tumblestone block is planted
// * @param id The identifier of the criterion
// * @param predicate The predicate for the criterion
// *
// * @author Aethen
// * @since 02/28/2024
// */
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
// override fun toJson(json: JsonObject) {
// // Add properties to json if needed. None needed for this criterion
// }
//
// override fun fromJson(json: JsonObject) {
// // Parse properties from json if needed. None needed for this criterion
// }
//
// /**
// * Checks if the tumblestone block can grow at the given position
// * @param player The player that planted the tumblestone block
// * @param context The context of the criterion
// * @return True if the tumblestone block can grow at the given position, false otherwise
// */
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
// }
//}
//
///**
// * The context of the [PlantTumblestoneCriterionCondition]
// * @param pos The position of the tumblestone block
// * @param tumbleStoneBlock The tumblestone block
// */
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
}
@@ -0,0 +1,156 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.AttackEggThiefTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class AttackEggThiefTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(AttackEggThiefTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -0,0 +1,154 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class BreedAtNestTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(BreedAtNestTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -0,0 +1,114 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BuildNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class BuildNestTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed" // Category
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(BuildNestTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE)
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
// Don't do anything if the pokemon has a breeding cooldown
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
//val nestLocation = it.get(nestMemory)
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// todo try to find a way to only have pairs of mates instead of an orgy
// Create a target vector at the nest location
val targetVec = Vec3.atCenterOf(nestLocation)
// Check if Pokémon are close enough to the nest
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
return@Trigger false // Task ends when both are close enough
}
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
val test1 = 1 // for breakpointing here to test my pastured mons
}*//*
// try to get the main pokemon to path and look at the nest
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
// try to find a way to get the mate to pathfind and look at the nest too...
mate.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
)
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}*/
@@ -0,0 +1,181 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GuardNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
class GuardNestTaskConfig : SingleTaskConfig {
// todo make new variables
companion object {
const val BREED = "breed"
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(GuardNestTask.create(), PokemonEntity::class.java)
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET),
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.PATERNAL_NEST_LOCATION),
it.registered(MemoryModuleType.NEAREST_VISIBLE_PLAYER), // if player is invisible they do not patrol
it.registered(MemoryModuleType.ANGRY_AT),
it.absent(MemoryModuleType.ATTACK_TARGET),
it.registered(CobblemonMemories.IRRITATED_AT)
).apply(it) { lookTarget, walkTarget, paternalNest, nearestVisiblePlayer, angryAt, attackTarget, irritatedAt ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
//val nestLocation = it.get(nestMemory)
val nestLocation: BlockPos = (paternalNest.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val nestCenter = Vec3.atCenterOf(nestLocation)
// todo maybe cancel task if player is close to nest and set angry at task to that player
val nearestPlayerOptional = entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER)
val player = nearestPlayerOptional.orElse(null) as? ServerPlayer
val irritatedAtTarget = entity.brain.getMemory(CobblemonMemories.IRRITATED_AT)
if (player != null) {
val playerDistanceSquared = player.position().distanceToSqr(nestCenter)
if (!irritatedAtTarget.isPresent && playerDistanceSquared <= 15 * 15) {
// Player is within 15 blocks, set angry and cry
entity.cry()
// set irritated at target to player near egg
irritatedAt.set(player.uuid)
// Display angry particles above parent
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
}
if (playerDistanceSquared <= 8 * 8) {
// Player is within 8 blocks, attack
entity.cry()
// Display angry particles above parent
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
angryAt.set(player.uuid)
attackTarget.set(player)
return@Trigger false // Cancel the task
}
}
// Scan for a valid walkable position within an 8-block sphere
val validPosition = findValidWalkablePosition(world, nestCenter, entity, 8)
if (validPosition == null) {
return@Trigger false // No valid position found
}
// Set the entity's walk and look targets
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
private fun findValidWalkablePosition(
world: ServerLevel,
center: Vec3,
entity: PathfinderMob,
radius: Int
): Vec3? {
val random = world.random
// Try a number of random positions within the radius
repeat(20) { // Attempt up to 20 random positions
val offsetX = random.nextDouble() * 2 * radius - radius
val offsetY = random.nextDouble() * 2 * radius - radius
val offsetZ = random.nextDouble() * 2 * radius - radius
val candidate = center.add(offsetX, offsetY, offsetZ)
// Convert to block position
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
// Check if the position is walkable and not inside a solid block
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
entity.navigation.isStableDestination(blockPos)
) {
return Vec3.atCenterOf(blockPos) // Return the valid position
}
}
return null // No valid position found
}*/
}
@@ -0,0 +1,147 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import java.util.UUID
class HuntPlayerTaskConfig : SingleTaskConfig {
// todo make new variables
companion object {
const val BREED = "breed"
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET),
it.absent(MemoryModuleType.WALK_TARGET),
it.present(MemoryModuleType.NEAREST_PLAYERS), // this will be null most of the time
it.present(MemoryModuleType.ANGRY_AT),
it.registered(MemoryModuleType.ATTACK_TARGET),
it.registered(MemoryModuleType.DISTURBANCE_LOCATION),
).apply(it) { lookTarget, walkTarget, nearestPlayers, angryAt, attackTarget, disturbanceLocation ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val angryAtUuid = (angryAt.value() as? IdF<UUID?>)?.value() ?: return@Trigger false
val disturbancePos = (disturbanceLocation.value() as? IdF<BlockPos>)?.value()
val players = (nearestPlayers.value() as? IdF<List<Player?>?>)?.value()
val targetPlayer = players?.find { it?.uuid == angryAtUuid }
if (targetPlayer != null) {
// If the angry player is nearby, set them as the attack target
entity.cry()
attackTarget.set(targetPlayer)
return@Trigger true
}
// If the angry player is not nearby, search around the Pokémon
val maxTicksToSearch = 200L
if (time > maxTicksToSearch) {
// Timeout after 200 ticks
return@Trigger false
}
// If no disturbance location or it is caused by a nearby player, skip
if (disturbancePos != null) {
val disturberCausingDisturbance = players?.any {
it?.position()?.distanceToSqr(Vec3.atCenterOf(disturbancePos))!! <= 4.0 * 4.0
}
disturberCausingDisturbance?.let { it1 ->
if (!it1) {
// Pathfind toward disturbance location
walkTarget.set(WalkTarget(Vec3.atCenterOf(disturbancePos), speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(Vec3.atCenterOf(disturbancePos)))
return@Trigger true
}
}
}
// If no valid disturbance location, pathfind randomly within proximity
val validPosition = findValidWalkablePosition(world, entity.position(), entity, 8)
if (validPosition == null) {
return@Trigger false // No valid position found
}
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
private fun findValidWalkablePosition(
world: ServerLevel,
center: Vec3,
entity: PathfinderMob,
radius: Int
): Vec3? {
val random = world.random
// Try a number of random positions within the radius
repeat(20) { // Attempt up to 20 random positions
val offsetX = random.nextDouble() * 2 * radius - radius
val offsetY = random.nextDouble() * 2 * radius - radius
val offsetZ = random.nextDouble() * 2 * radius - radius
val candidate = center.add(offsetX, offsetY, offsetZ)
// Convert to block position
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
// Check if the position is walkable and not inside a solid block
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
entity.navigation.isStableDestination(blockPos)
) {
return Vec3.atCenterOf(blockPos) // Return the valid position
}
}
return null // No valid position found
}
}
@@ -0,0 +1,53 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToIdealNestLocationTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class PathToIdealNestLocationTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed" // Category
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PathToIdealNestLocationTask.create(), PokemonEntity::class.java)
}
}
@@ -0,0 +1,114 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class PathToNestMaterialTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed" // Category
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PathToNestMaterialTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE)
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
// Don't do anything if the pokemon has a breeding cooldown
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
//val nestLocation = it.get(nestMemory)
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// todo try to find a way to only have pairs of mates instead of an orgy
// Create a target vector at the nest location
val targetVec = Vec3.atCenterOf(nestLocation)
// Check if Pokémon are close enough to the nest
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
return@Trigger false // Task ends when both are close enough
}
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
val test1 = 1 // for breakpointing here to test my pastured mons
}*//*
// try to get the main pokemon to path and look at the nest
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
// try to find a way to get the mate to pathfind and look at the nest too...
mate.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
)
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}*/
@@ -0,0 +1,113 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class PathToNestTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed" // Category
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PathToNestTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE)
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
// Don't do anything if the pokemon has a breeding cooldown
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
//val nestLocation = it.get(nestMemory)
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// todo try to find a way to only have pairs of mates instead of an orgy
// Create a target vector at the nest location
val targetVec = Vec3.atCenterOf(nestLocation)
// Check if Pokémon are close enough to the nest
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
return@Trigger false // Task ends when both are close enough
}
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
val test1 = 1 // for breakpointing here to test my pastured mons
}*//*
// try to get the main pokemon to path and look at the nest
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
// try to find a way to get the mate to pathfind and look at the nest too...
mate.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
)
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}*/
@@ -0,0 +1,157 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpEggTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class PickUpEggTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PickUpEggTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -0,0 +1,114 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpNestMaterialTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class PickUpNestMaterialTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed" // Category
}
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PickUpNestMaterialTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE)
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
// Don't do anything if the pokemon has a breeding cooldown
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
//val nestLocation = it.get(nestMemory)
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// todo try to find a way to only have pairs of mates instead of an orgy
// Create a target vector at the nest location
val targetVec = Vec3.atCenterOf(nestLocation)
// Check if Pokémon are close enough to the nest
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
return@Trigger false // Task ends when both are close enough
}
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
val test1 = 1 // for breakpointing here to test my pastured mons
}*//*
// try to get the main pokemon to path and look at the nest
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
// try to find a way to get the mate to pathfind and look at the nest too...
mate.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
)
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}*/
@@ -0,0 +1,156 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class PlaceEggInNestTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(PlaceEggInNestTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -0,0 +1,156 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.RetrieveEggTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class RetrieveEggTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(RetrieveEggTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -0,0 +1,155 @@
/*
* 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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.breeding.BreedingLogicManager
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.kinds.IdF
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class SearchForEggTaskConfig : SingleTaskConfig {
companion object {
const val BREED = "breed_at_nest" // Category
}
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(SearchForEggTask.create(), PokemonEntity::class.java)
}
}
/*override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEST_LOCATION),
it.present(CobblemonMemories.POTENTIAL_MATE),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { nestMemory, mateMemory, walkTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// Retrieve nest location and mate entity from memory
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
// Convert nest location to Vec3
val targetVec = Vec3.atCenterOf(nestLocation)
// Check proximity to the nest
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
// Access the nest block entity
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
?: return@Trigger false
// double check to make sure the nest is not taken yet and cancel if it is
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
// Generate the egg using BreedingLogicManager
val parentOne = (entity as PokemonEntity).pokemon
val parentTwo = mate.pokemon
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
// Ensure the breeding result is valid
val egg = breedingResult.pokemon?.let { result ->
Egg(
species = result.species.resourceIdentifier,
parentOne = parentOne,
parentTwo = parentTwo,
form = form.name.lowercase(),
timeToHatch = 6000F
)
} ?: return@Trigger false
// Set the egg in the nest
nestBlockEntity.setItem(egg.createEggItemStack())
// set the parents of the nest if it/they are wild so they guard it
if (nestBlockEntity.ownerUUID == null)
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
// set breeding cooldown for both
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
// Display heart particles above parents
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
entity.getRandomX(1.0), // Particle X position with slight randomness
entity.getRandomY() + 0.5, // Slightly raised Y position
entity.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
(world as net.minecraft.server.level.ServerLevel).sendParticles(
net.minecraft.core.particles.ParticleTypes.HEART,
mate.getRandomX(1.0), // Particle X position with slight randomness
mate.getRandomY() + 0.5, // Slightly raised Y position
mate.getRandomZ(1.0), // Particle Z position with slight randomness
4 + kotlin.random.Random.nextInt(2), // Particle count
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
0.02 // Particle speed
)
// Notify nearby players
world.players().forEach { player ->
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
}
// todo maybe have them fall asleep after?
return@Trigger true
}
}
}*/
@@ -94,9 +94,22 @@ interface TaskConfig {
cobblemonResource("point_to_spawn") to PointToSpawnTaskConfig::class.java,
cobblemonResource("eat_grass") to EatGrassTaskConfig::class.java,
cobblemonResource("find_air") to FindAirTaskConfig::class.java,
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
cobblemonResource("path_to_nest") to PathToNestTaskConfig::class.java,
cobblemonResource("path_to_nest_material") to PathToNestMaterialTaskConfig::class.java,
cobblemonResource("breed_at_nest") to BreedAtNestTaskConfig::class.java,
cobblemonResource("guard_nest") to GuardNestTaskConfig::class.java,
cobblemonResource("search_for_egg") to SearchForEggTaskConfig::class.java,
cobblemonResource("attack_egg_thief") to AttackEggThiefTaskConfig::class.java,
cobblemonResource("retrieve_egg") to RetrieveEggTaskConfig::class.java,
cobblemonResource("pick_up_egg") to PickUpEggTaskConfig::class.java,
cobblemonResource("pick_up_nest_material") to PickUpNestMaterialTaskConfig::class.java,
cobblemonResource("path_to_ideal_nest_location") to PathToIdealNestLocationTaskConfig::class.java,
cobblemonResource("place_egg_in_nest") to PlaceEggInNestTaskConfig::class.java,
cobblemonResource("build_nest") to BuildNestTaskConfig::class.java,
cobblemonResource("hunt_player") to HuntPlayerTaskConfig::class.java,
)
val runtime = MoLangRuntime().setup()
}
@@ -0,0 +1,193 @@
/*
* 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.breeding
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import java.util.Optional
data class BreedingRule(
val parent1rules: ParentRules,
val parent2rules: ParentRules,
// The resulting baby data (all strings except hasItem will appear in ParentRules)
val species: String?,
val form: String?,
val nature: String?,
val ability: String?,
val moves: Moves,
val ivs: Ivs,
val gender: String?,
// If you want a separate pokeball string field:
val pokeball: String?
) {
companion object {
val CODEC: Codec<BreedingRule> = RecordCodecBuilder.create { builder ->
builder.group(
// Parent rules
ParentRules.CODEC.fieldOf("parent1rules")
.forGetter<BreedingRule> { it.parent1rules },
ParentRules.CODEC.fieldOf("parent2rules")
.forGetter<BreedingRule> { it.parent2rules },
// ---- All optional string fields for the baby data ----
Codec.STRING.optionalFieldOf("species")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.species },
Codec.STRING.optionalFieldOf("form")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.form },
Codec.STRING.optionalFieldOf("nature")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.nature },
Codec.STRING.optionalFieldOf("ability")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.ability },
// Moves & IVs can be empty objects but must exist in the JSON
Moves.CODEC.fieldOf("moves")
.forGetter<BreedingRule> { it.moves },
Ivs.CODEC.fieldOf("ivs")
.forGetter<BreedingRule> { it.ivs },
Codec.STRING.optionalFieldOf("gender")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.gender },
// If you want pokeball to be a string instead of ResourceLocation
Codec.STRING.optionalFieldOf("pokeball")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<BreedingRule> { it.pokeball }
).apply(builder, ::BreedingRule)
}
}
data class ParentRules(
val species: String?,
val form: String?,
val ability: String?,
val gender: String?,
// hasMove is also just a string now
val hasMove: String?,
// hasItem stays as a ResourceLocation?
val hasItem: ResourceLocation?
) {
companion object {
val CODEC: Codec<ParentRules> = RecordCodecBuilder.create { builder ->
builder.group(
Codec.STRING.optionalFieldOf("species")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.species },
Codec.STRING.optionalFieldOf("form")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.form },
Codec.STRING.optionalFieldOf("ability")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.ability },
Codec.STRING.optionalFieldOf("gender")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.gender },
Codec.STRING.optionalFieldOf("hasMove")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.hasMove },
// Keep hasItem as a ResourceLocation
ResourceLocation.CODEC.optionalFieldOf("hasItem")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<ParentRules> { it.hasItem }
).apply(builder, ::ParentRules)
}
}
}
data class Moves(
val move1: String?,
val move2: String?,
val move3: String?,
val move4: String?
) {
companion object {
val CODEC: Codec<Moves> = RecordCodecBuilder.create { builder ->
builder.group(
Codec.STRING.optionalFieldOf("move1")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Moves> { it.move1 },
Codec.STRING.optionalFieldOf("move2")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Moves> { it.move2 },
Codec.STRING.optionalFieldOf("move3")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Moves> { it.move3 },
Codec.STRING.optionalFieldOf("move4")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Moves> { it.move4 }
).apply(builder, ::Moves)
}
}
}
data class Ivs(
val hp: Int?,
val attack: Int?,
val defence: Int?,
val special_attack: Int?,
val special_defence: Int?,
val speed: Int?
) {
companion object {
val CODEC: Codec<Ivs> = RecordCodecBuilder.create { builder ->
builder.group(
Codec.INT.optionalFieldOf("hp")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.hp },
Codec.INT.optionalFieldOf("attack")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.attack },
Codec.INT.optionalFieldOf("defence")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.defence },
Codec.INT.optionalFieldOf("special_attack")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.special_attack },
Codec.INT.optionalFieldOf("special_defence")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.special_defence },
Codec.INT.optionalFieldOf("speed")
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
.forGetter<Ivs> { it.speed }
).apply(builder, ::Ivs)
}
}
}
}
@@ -0,0 +1,116 @@
/*
* 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.breeding
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
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.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object BreedingRules : JsonDataRegistry<BreedingRule> {
override val id = cobblemonResource("breeding_rules")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<BreedingRules>()
override val typeToken: TypeToken<BreedingRule> = TypeToken.get(BreedingRule::class.java)
override val resourcePath = "breeding_rules"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
private val registry = mutableMapOf<ResourceLocation, BreedingRule>()
override fun sync(player: ServerPlayer) {
// Send the entire list of BreedingRule entries to the player
BreedingRuleRegistrySyncPacket(this.registry.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, BreedingRule>) {
// Clear existing data and load new data from JSON
registry.clear()
data.forEach { (id, breedingRule) ->
registry[id] = breedingRule
}
println("Loaded Breeding Rules: ${registry.size}")
}
/**
* Attempts to find a single BreedingRule that matches the given mother/father conditions.
* Returns the first matching BreedingRule, or null if no match is found.
*/
fun getBreedingRule(mother: Pokemon, father: Pokemon): BreedingRule? {
// 1. Prepare the actual ParentRules objects for mother/father.
val motherRules = createParentRules(mother)
val fatherRules = createParentRules(father)
// 2. Find all matching breeding rules in the registry.
// A rule matches if parent1rules & parent2rules match the mother/father conditions.
// If multiple rules match, we just return the first (or you could randomize).
val matchingRules = registry.values.firstOrNull { rule ->
rule.species != null && (
(checkParentRules(rule.parent1rules, motherRules) && checkParentRules(rule.parent2rules, fatherRules)) ||
(checkParentRules(rule.parent1rules, fatherRules) && checkParentRules(rule.parent2rules, motherRules))
)
}
// 3. If none match, return null. Otherwise return the first match.
return matchingRules
}
/**
* Helper function to create a BreedingRule.ParentRules object from an actual Pokémon instance.
* Adjust as needed if you'd like more robust checking (e.g. multiple moves, alt forms, etc.).
*/
private fun createParentRules(parent: Pokemon): BreedingRule.ParentRules {
// NOTE: Adjust these calls if your Pokemon class has different ways to get form/ability
val speciesId = parent.species.name.lowercase()
val formId = parent.form.name.lowercase() // If you store form as a ResourceLocation
val abilityId = parent.ability.name.lowercase() // If you store ability as a ResourceLocation
return BreedingRule.ParentRules(
species = speciesId,
form = formId,
ability = abilityId,
gender = parent.gender.name.lowercase(),
hasMove = parent.moveSet.getMoves().firstOrNull()?.template?.name,
hasItem = parent.heldItemNoCopy().item.let {
BuiltInRegistries.ITEM.getKey(it) // If empty, might be minecraft:air
}
)
}
/**
* Check if a parent's actual rules match the expected parent rules from the data.
*/
private fun checkParentRules(
expected: BreedingRule.ParentRules,
actual: BreedingRule.ParentRules
): Boolean {
return (expected.species == null || expected.species == actual.species) &&
(expected.form == null || expected.form == actual.form) &&
(expected.ability == null || expected.ability == actual.ability) &&
(expected.gender == null || expected.gender == actual.gender) &&
(expected.hasMove == null || expected.hasMove == actual.hasMove) &&
(expected.hasItem == null || expected.hasItem == actual.hasItem)
}
}
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.events.breeding.BreedingEligibilityEvent
import com.cobblemon.mod.common.api.events.breeding.BreedingResultEvent
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
@@ -271,6 +273,12 @@ object CobblemonEvents {
@JvmField
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
//Breeding
@JvmField
val BREEDING_RESULT = EventObservable<BreedingResultEvent>()
@JvmField
val BREEDING_ELIGIBLE = EventObservable<BreedingEligibilityEvent>()
// -------------------------------------------------------------------------------------
//
// Riding
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.breeding
import com.cobblemon.mod.common.pokemon.Pokemon
data class BreedingEligibilityEvent(
val canBreed: Boolean,
val mother: Pokemon,
val father: Pokemon
)
@@ -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.events.breeding
import com.cobblemon.mod.common.api.pokemon.breeding.BreedingResult
import com.cobblemon.mod.common.pokemon.Pokemon
data class BreedingResultEvent(
val breedingResult: BreedingResult,
val mother: Pokemon,
val father: Pokemon
)
@@ -23,7 +23,7 @@ import net.minecraft.nbt.CompoundTag
* reload.
*
* @property pokemonEntity The Pokémon entity being saved.
* @property nbt The [NbtCompound] that the Pokémon is being saved to.
* @property nbt The [CompoundTag] that the Pokémon is being saved to.
* You should add or remove from this for whatever you
* want to accomplish. The existing contents will be all
* of what Cobblemon saves natively.
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
class TypeReturnIcon(
val x: Number,
val y: Number,
val centeredX: Boolean = false,
val small: Boolean = false,
val secondaryOffset: Float = 15F,
val doubleCenteredOffset: Float = 7.5F,
val opacity: Float = 1F
) {
companion object {
private const val TYPE_ICON_DIAMETER = 36
private const val SCALE = 0.5F
private val typeReturnResource = cobblemonResource("textures/gui/type_return.png")
private val smallTypeReturnResource = cobblemonResource("textures/gui/type_return_small.png")
}
fun render(context: GuiGraphics) {
val diameter = if (small) (TYPE_ICON_DIAMETER / 2) else TYPE_ICON_DIAMETER
blitk(
matrixStack = context.pose(),
texture = if (small) smallTypeReturnResource else typeReturnResource,
x = (x.toFloat()) / SCALE - 9,
y = y.toFloat() / SCALE,
height = diameter,
width = diameter,
alpha = opacity,
scale = SCALE
)
}
}
@@ -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.habitats
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
data class Habitat(
val structure: ResourceLocation,
val spawnPool: List<WeightedGroup>
) {
companion object {
val CODEC: Codec<Habitat> = RecordCodecBuilder.create { instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("structure").forGetter { it.structure },
WeightedGroup.CODEC.listOf().fieldOf("spawnPool").forGetter { it.spawnPool }
).apply(instance, ::Habitat)
}
val STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC)
val BLANK_HABITAT = Habitat(
structure = cobblemonResource("blank"),
spawnPool = emptyList()
)
}
fun flatten(): List<ResourceLocation> {
return spawnPool.flatMap { it.spawns.map { s -> s.species } }
}
}
data class WeightedGroup(
val weight: Int = 1,
val spawns: List<WeightedSpecies>
) {
companion object {
val CODEC: Codec<WeightedGroup> = RecordCodecBuilder.create { instance ->
instance.group(
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight },
WeightedSpecies.CODEC.listOf().fieldOf("spawns").forGetter { it.spawns }
).apply(instance, ::WeightedGroup)
}
}
}
data class WeightedSpecies(
val species: ResourceLocation,
val weight: Int = 1
) {
companion object {
val CODEC: Codec<WeightedSpecies> = RecordCodecBuilder.create { instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("species").forGetter { it.species },
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight }
).apply(instance, ::WeightedSpecies)
}
}
}
@@ -0,0 +1,67 @@
/*
* 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.habitats
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.HabitatSyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object Habitats : JsonDataRegistry<Habitat> {
override val id = cobblemonResource("habitats")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<Habitats>()
override val typeToken: TypeToken<Habitat> = TypeToken.get(Habitat::class.java)
override val resourcePath = "habitats"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
private val table = mutableMapOf<ResourceLocation, Habitat>()
override fun sync(player: ServerPlayer) {
HabitatSyncPacket(table.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, Habitat>) {
table.clear()
table.putAll(data)
println("[Habitats] Reloaded ${table.size} habitat(s):")
for ((structureId, habitat) in table) {
println(" - ${structureId}:")
if (habitat.spawnPool.isEmpty()) {
println(" (No spawn entries)")
} else {
habitat.spawnPool.forEachIndexed { i, group ->
println(" • Group $i (weight: ${group.weight}):")
group.spawns.forEach { entry ->
println(" - ${entry.species} (weight: ${entry.weight})")
}
}
}
}
}
fun getHabitat(structureId: ResourceLocation): Habitat? {
return table[structureId]
}
fun getSpawnOptions(structureId: ResourceLocation): List<ResourceLocation> {
return table[structureId]?.flatten() ?: emptyList()
}
}
@@ -92,10 +92,4 @@ class NPCProvider(npc: NPCEntity): EntityProvider {
interface EntityProvider {
val entities: List<Entity>
}
//class MoveAnimationKeyframe(
// val sound: ResourceLocation? = null,
// val animation: List<AnimationWithMoments> = listOf(),
// val effects: List<ActionEffect> = listOf()
//)
}
@@ -0,0 +1,216 @@
/*
* 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.nest
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
import com.cobblemon.mod.common.block.NestBlock
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.serialization.Codec
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.tags.BiomeTags
import net.minecraft.tags.TagKey
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import kotlin.jvm.optionals.getOrNull
/**
* Contains the different Nest variants and util methods to get their item, block, biome, and materials from.
*
* @author Plastered_Crab
* @since Feb 7th, 2025
*/
enum class Nest : StringRepresentable {
STICK,
LEAF,
STONE,
DEEPSLATE,
SNOW,
LUSH,
SEAGRASS,
WARM_OCEAN,
SAND,
RED_SAND,
NETHER,
BASALT,
SOUL_SAND,
CHORUS;
//HAY;
// Crab Note: todo maybe we want these to be a List of BlockItems?
fun item(): BlockItem = when(this) {
STICK -> CobblemonItems.NEST_STICK
LEAF -> CobblemonItems.NEST_LEAF
STONE -> CobblemonItems.NEST_STONE
DEEPSLATE -> CobblemonItems.NEST_DEEPSLATE
SNOW -> CobblemonItems.NEST_SNOW
LUSH -> CobblemonItems.NEST_LUSH
SEAGRASS -> CobblemonItems.NEST_SEAGRASS
WARM_OCEAN -> CobblemonItems.NEST_WARM_OCEAN
SAND -> CobblemonItems.NEST_SAND
RED_SAND -> CobblemonItems.NEST_RED_SAND
NETHER -> CobblemonItems.NEST_NETHER
BASALT -> CobblemonItems.NEST_BASALT
SOUL_SAND -> CobblemonItems.NEST_SOUL_SAND
CHORUS -> CobblemonItems.NEST_CHORUS
//HAY -> CobblemonItems.NEST_HAY
}
// todo make a map of ideal placement blocks for each nest
fun placementBlocks(): List<Block> = when (this) {
STICK -> listOf(Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.PODZOL, Blocks.COARSE_DIRT)
LEAF -> listOf(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.BIRCH_LEAVES, Blocks.ACACIA_LEAVES, Blocks.AZALEA_LEAVES, Blocks.MANGROVE_LEAVES, Blocks.CHERRY_LEAVES)
STONE -> listOf(Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.GRANITE, Blocks.DRIPSTONE_BLOCK, Blocks.COBBLESTONE, Blocks.ICE, Blocks.PACKED_ICE, Blocks.BLUE_ICE)
DEEPSLATE -> listOf(Blocks.DEEPSLATE, Blocks.COBBLED_DEEPSLATE)
SNOW -> listOf(Blocks.SNOW_BLOCK, Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.COARSE_DIRT) // todo dirt, grass, and coarse dirt ONLY if replacing the location of snowlayer
LUSH -> listOf(Blocks.MOSS_BLOCK, Blocks.CLAY)
SEAGRASS -> listOf(Blocks.DIRT, Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.MUD, Blocks.CLAY, Blocks.ANDESITE, Blocks.SAND, Blocks.GRAVEL) // todo (all only if surrounded by water AND not in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
WARM_OCEAN -> listOf(Blocks.SAND, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.FIRE_CORAL_BLOCK, Blocks.HORN_CORAL_BLOCK, Blocks.TUBE_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BUBBLE_CORAL_BLOCK) // todo (all only if surrounded by water AND only when in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
SAND -> listOf(Blocks.SAND, Blocks.SANDSTONE, Blocks.SMOOTH_SANDSTONE) // todo (only if NOT surrounded by water)
RED_SAND -> listOf(Blocks.RED_SAND, Blocks.RED_SANDSTONE, Blocks.SMOOTH_RED_SANDSTONE)
NETHER -> listOf(Blocks.NETHERRACK, Blocks.WARPED_NYLIUM, Blocks.CRIMSON_NYLIUM, Blocks.NETHER_WART_BLOCK, Blocks.WARPED_WART_BLOCK, Blocks.MAGMA_BLOCK)
BASALT -> listOf(Blocks.BASALT, Blocks.BLACKSTONE, Blocks.SMOOTH_BASALT)
SOUL_SAND -> listOf(Blocks.SOUL_SAND, Blocks.SOUL_SOIL)
CHORUS -> listOf(Blocks.END_STONE, Blocks.PURPUR_BLOCK, Blocks.END_STONE_BRICKS)
//HAY -> listOf(Blocks.HAY_BLOCK, Blocks.GRASS_BLOCK)
}
// todo make a map or something for each nest that can be waterlogged and not
/**
* Determines if a nest can be waterlogged.
* Some nests, like those made of water-related materials, can be placed in water.
*/
fun isWaterloggable(): Boolean = when (this) {
SEAGRASS, WARM_OCEAN -> true
else -> false
}
/**
* Determines if this nest type can be placed based on **biome tags**.
*/
fun isValidBiome(world: ServerLevel, pos: BlockPos): Boolean {
val biomeHolder: Holder<Biome> = world.getBiome(pos)
return when (this) {
STICK -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STICK_NEST)
LEAF -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LEAF_NEST_TREES)
STONE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STONE_NEST)
DEEPSLATE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_DEEPSLATE_NEST_CAVES)
SNOW -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SNOW_NEST)
LUSH -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LUSH_NEST_CAVES)
SEAGRASS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SEAGRASS_NEST)
WARM_OCEAN -> biomeHolder.`is`(CobblemonBiomeTags.HAS_WARM_OCEAN_NEST)
SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SAND_NEST)
RED_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_RED_SAND_NEST)
NETHER -> biomeHolder.`is`(CobblemonBiomeTags.HAS_NETHER_NEST)
BASALT -> biomeHolder.`is`(CobblemonBiomeTags.HAS_BASALT_NEST)
SOUL_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SOUL_SAND_NEST)
CHORUS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_CHORUS_NEST)
}
}
// Crab Note: todo maybe we want these to be a List of Items?
fun materials(): Item = when(this) {
STICK -> Items.STICK
LEAF -> Items.OAK_SAPLING
STONE -> Items.COBBLESTONE
DEEPSLATE -> Items.COBBLED_DEEPSLATE
SNOW -> Items.SNOWBALL
LUSH -> Items.MOSS_BLOCK
SEAGRASS -> Items.KELP
WARM_OCEAN -> Items.SEA_PICKLE
SAND -> Items.SAND
RED_SAND -> Items.RED_SAND
NETHER -> Items.WARPED_ROOTS
BASALT -> Items.BASALT
SOUL_SAND -> Items.SOUL_SAND
CHORUS -> Items.CHORUS_FRUIT
//HAY -> Items.WHEAT
}
// Crab Note: todo maybe we want to redo this if we have lists of items needed for each?
/*fun craftedNestResult(itemStack: ItemStack): BlockItem? = when(itemStack) {
ItemStack(Items.STICK, 4) -> CobblemonItems.NEST_STICK
ItemStack(Items.OAK_SAPLING, 4) -> CobblemonItems.NEST_LEAF
ItemStack(Items.COBBLESTONE, 4) -> CobblemonItems.NEST_STONE
ItemStack(Items.COBBLED_DEEPSLATE, 4) -> CobblemonItems.NEST_DEEPSLATE
ItemStack(Items.SNOWBALL, 4) -> CobblemonItems.NEST_SNOW
ItemStack(Items.MOSS_BLOCK, 4) -> CobblemonItems.NEST_LUSH
ItemStack(Items.KELP, 4) -> CobblemonItems.NEST_SEAGRASS
ItemStack(Items.SEA_PICKLE, 4) -> CobblemonItems.NEST_WARM_OCEAN
ItemStack(Items.SAND, 4) -> CobblemonItems.NEST_SAND
ItemStack(Items.RED_SAND, 4) -> CobblemonItems.NEST_RED_SAND
ItemStack(Items.WARPED_ROOTS, 4) -> CobblemonItems.NEST_NETHER
ItemStack(Items.BASALT, 4) -> CobblemonItems.NEST_BASALT
ItemStack(Items.SOUL_SAND, 4) -> CobblemonItems.NEST_SOUL_SAND
ItemStack(Items.CHORUS_FRUIT, 4) -> CobblemonItems.NEST_CHORUS
ItemStack(Items.WHEAT, 4) -> CobblemonItems.NEST_HAY
else -> null
}*/
override fun getSerializedName() = this.name.lowercase()
companion object {
val CODEC: Codec<Nest> = StringRepresentable.fromValues(::values)
private val materialRequirements: Map<Item, Int> = mapOf(
Items.STICK to 2,
Items.OAK_SAPLING to 2,
Items.COBBLESTONE to 2,
Items.COBBLED_DEEPSLATE to 2,
Items.SNOWBALL to 2,
Items.MOSS_BLOCK to 2,
Items.KELP to 2,
Items.SEA_PICKLE to 2,
Items.SAND to 2,
Items.RED_SAND to 2,
Items.WARPED_ROOTS to 2,
Items.BASALT to 2,
Items.SOUL_SAND to 2,
Items.CHORUS_FRUIT to 2,
//Items.WHEAT to 2
)
/**
* Determines if a held stack is sufficient to craft a nest and returns the crafted item and leftover count.
* @param heldStack The item stack in the Pokémon's hand.
* @return A Pair of (crafted nest block item, remaining materials in hand) or null if crafting is not possible.
*/
fun craftedNestResult(heldStack: ItemStack): Pair<BlockItem, Int>? {
val requiredAmount = materialRequirements[heldStack.item] ?: return null // Check if item is a valid material
// Ensure Pokémon has enough materials
if (heldStack.count >= requiredAmount) {
val remaining = heldStack.count - requiredAmount
// Find the nest type that matches the material
val matchingNest = entries.firstOrNull { it.materials() == heldStack.item } ?: return null
return Pair(matchingNest.item(), remaining)
}
return null // Not enough materials
}
}
}
@@ -63,7 +63,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
@@ -59,7 +59,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
//.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
@@ -31,6 +31,9 @@ object CobblemonPermissions {
val GIVE_POKEMON_SELF = this.create("${GIVE_POKEMON_BASE}.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val GIVE_POKEMON_OTHER = this.create("${GIVE_POKEMON_BASE}.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val GIVE_POKEMON_EGG = this.create("${COMMAND_PREFIX}givepokemonegg", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
private const val HEAL_POKEMON_BASE = "${COMMAND_PREFIX}healpokemon"
@JvmStatic
val HEAL_POKEMON_SELF = this.create("$HEAL_POKEMON_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -8,10 +8,13 @@
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -21,6 +24,7 @@ import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
class PokedexManager(
override val uuid: UUID,
@@ -110,7 +110,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
@@ -187,6 +187,22 @@ object PokemonSpecies : JsonDataRegistry<Species> {
*/
fun getByIdentifier(identifier: ResourceLocation) = this.speciesByIdentifier[identifier]
/**
* Finds the first [Species] in an evolution line
*
* @param species The [Species] evolution line to use
* @return The [Species] if existing.
*/
fun getFirstEvolution(species: Species): Species {
var returnSpecies = species
while (returnSpecies.preEvolution != null) {
returnSpecies = returnSpecies.preEvolution!!.species
}
return returnSpecies
}
/**
* Counts the currently loaded species.
*
@@ -0,0 +1,599 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.breeding
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
import com.cobblemon.mod.common.api.breeding.BreedingRule
import com.cobblemon.mod.common.api.breeding.BreedingRules
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.world.item.Item
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.Holder
import kotlin.random.Random
interface BreedingLogic {
/**
* Takes two [Pokemon] and returns a [BreedingResult].
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return The [BreedingResult] of the two [Pokemon]
* @author Apion, Nick, whatsy, plastered_crab
*/
// TODO we may want to call this method "hatch" now since we store parents on the egg itself now
fun breed(mother: Pokemon, father: Pokemon): BreedingResult {
if (!this.canBreed(mother, father)) {
return BreedingResult()
}
// Crab note: TODO Clean this shit up it is ugly af and idk if we even want to do custom breeding rules this way.... HIRO HELP
// find matching breeding rule
val rule = BreedingRules.getBreedingRule(mother, father)
val fallbackForm: FormData = this.calculateForm(mother, father)
val finalSpecies: Species = if (rule?.species != null) {
PokemonSpecies.getByName(rule.species.lowercase()) ?: fallbackForm.species
} else {
fallbackForm.species
}
val finalForm: FormData = if (rule != null && rule.form != null) {
val speciesEntry = rule.species?.let { PokemonSpecies.getByName(it.lowercase()) } ?: fallbackForm.species
speciesEntry.forms.find { formData ->
formData.name.lowercase() == rule.form.lowercase()
} ?: speciesEntry.standardForm
} else {
fallbackForm
}
// Calculate fallback IVs, then override stats from the rule if non-null
val finalIVs = mergeIvs(rule, mother, father)
val finalNature: Nature = if (rule?.nature != null) {
Natures.getNature(rule.nature.lowercase()) ?: this.calculateNature(mother, father)
} else {
this.calculateNature(mother, father)
}
// TODO get the ability to actually apply
val finalAbility: Ability = if (rule?.ability != null) {
Abilities.get(rule.ability.lowercase())?.create(true, Priority.HIGHEST) ?: this.calculateAbility(mother, father, finalForm)
} else {
this.calculateAbility(mother, father, finalForm)
}
// Handle moves from the rule. We read move1..move4 if present, turn them into MoveTemplates,
// then pass them as `eggMoves` to our `calculateMoveset`.
val ruleMoveTemplates: List<MoveTemplate> = if (rule != null) {
listOfNotNull(
rule.moves.move1,
rule.moves.move2,
rule.moves.move3,
rule.moves.move4
).mapNotNull { moveString ->
Moves.getByName(moveString.lowercase())
}
} else {
emptyList()
}
// Pass these move templates to `calculateMoveset` as the optional egg moves.
val finalMoveSet = this.calculateMoveset(
mother,
father,
finalForm ?: fallbackForm,
eggMoves = ruleMoveTemplates
)
// Determine shiny based on masuda
val masudaActive = masudaActive(mother,father)
val isShiny = when {
masudaActive -> (Random.nextFloat() < 1f / Cobblemon.config.masudaShinyRate)
else -> (Random.nextFloat() < 1f / Cobblemon.config.shinyRate)
}
val finalGender: Gender = if (rule?.gender != null) {
Gender.entries.find { it.name.equals(rule.gender, ignoreCase = true) }
?: this.calculateGender(mother, father, finalForm)
} else {
this.calculateGender(mother, father, finalForm)
}
val finalPokeball: PokeBall = if (rule?.pokeball != null) {
PokeBalls.getPokeBall(cobblemonResource(rule.pokeball.lowercase())) ?: this.calculatePokeball(mother, father)
} else {
this.calculatePokeball(mother, father)
}
// create final egg from all this awful data
val newPoke = EggPokemon(
ivs = finalIVs,
nature = finalNature,
species = getPreEvoSpecies(finalSpecies),
formData = finalForm,
ability = finalAbility,
moveSet = finalMoveSet,
isShiny = isShiny,
gender = finalGender,
pokeball = finalPokeball
)
return BreedingResult(newPoke, calcTimeToHatch(mother, father))
}
private fun mergeIvs(
rule: BreedingRule?,
mother: Pokemon,
father: Pokemon
): IVs {
val fallbackIvs = this.calculateIVs(mother, father)
if (rule == null) return fallbackIvs
// 3. For each stat, if rule.ivs.<stat> is non-null, override fallback
rule.ivs.hp?.let { fallbackIvs[Stats.HP] = it }
rule.ivs.attack?.let { fallbackIvs[Stats.ATTACK] = it }
rule.ivs.defence?.let { fallbackIvs[Stats.DEFENCE] = it }
rule.ivs.special_attack?.let { fallbackIvs[Stats.SPECIAL_ATTACK] = it }
rule.ivs.special_defence?.let { fallbackIvs[Stats.SPECIAL_DEFENCE] = it }
rule.ivs.speed?.let { fallbackIvs[Stats.SPEED] = it }
return fallbackIvs
}
fun masudaActive(mother: Pokemon, father: Pokemon) : Boolean {
val isDifferentOT = (mother.originalTrainer != father.originalTrainer)
val bothMaxFriendship = (mother.friendship == 255 && father.friendship == 255)
val requireBothOTs = Cobblemon.config.requireOTsForMasuda
return when {
requireBothOTs -> (isDifferentOT && bothMaxFriendship)
else -> (isDifferentOT || bothMaxFriendship)
}
}
/**
* Determines if two [Pokemon] can breed.
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return Whether the two [Pokemon] can breed
* @author Apion, whatsy, plastered_crab
*/
fun canBreed(mother: Pokemon, father: Pokemon): Boolean {
val canBreedUndiscovered = Cobblemon.config.canBreedUndiscovered
val canBreedGenderless = Cobblemon.config.canBreedGenderless
val canBreedDitto = Cobblemon.config.canBreedDitto
val areSameSpecies = mother.species == father.species
val sharedEggGroups = mother.form.eggGroups.intersect(father.form.eggGroups)
return when {
// check custom breeding rules first
(BreedingRules.getBreedingRule(mother, father) != null) -> true
// Check if config allows Undiscovered egg groups to breed
(canBreedUndiscovered && areSameSpecies && mother.form.eggGroups.contains(EggGroup.UNDISCOVERED)) -> true
// Check if either Pokémon is in the No Eggs Discovered egg group
(mother.form.eggGroups.contains(EggGroup.UNDISCOVERED) || father.form.eggGroups.contains(EggGroup.UNDISCOVERED)) -> false
// check if config allows ditto to breed with each other
(canBreedDitto && areSameSpecies && mother.form.eggGroups.contains(EggGroup.DITTO)) -> true
// Check if both Pokémon are Ditto
(mother.form.eggGroups.contains(EggGroup.DITTO) && father.form.eggGroups.contains(EggGroup.DITTO)) -> false
// check it config allows for same species genderless Pokemon can breed
(canBreedGenderless && (areSameSpecies && mother.gender == Gender.GENDERLESS)) -> true
// check for genderless breeding that is not with ditto
(mother.gender == Gender.GENDERLESS && !father.form.eggGroups.contains(EggGroup.DITTO)) ||
(father.gender == Gender.GENDERLESS && !mother.form.eggGroups.contains(EggGroup.DITTO)) -> false
// if either parent is ditto
(mother.form.eggGroups.contains(EggGroup.DITTO) || father.form.eggGroups.contains(EggGroup.DITTO)) -> true
// check if egg groups match
else -> sharedEggGroups.isNotEmpty() && mother.gender != father.gender
}
/*
// check custom breeding rules first
if (BreedingRules.getBreedingRule(mother, father) != null)
return true
// check if different species and Genderless
if (!areSameSpecies && (mother.gender == Gender.GENDERLESS))
return false
// Check if either Pokémon is in the No Eggs Discovered egg group
if (!canBreedUndiscovered && (mother.form.eggGroups.contains(EggGroup.UNDISCOVERED) || father.form.eggGroups.contains(EggGroup.UNDISCOVERED)))
return false
// Check if both Pokémon are Ditto
if (canBreedDitto && (mother.form.eggGroups.contains(EggGroup.DITTO) && father.form.eggGroups.contains(EggGroup.DITTO)))
return false
// check if both parents are Genderless and same species
if (canBreedGenderless && (areSameSpecies && mother.gender == Gender.GENDERLESS))
return true
// Check if at least one parent is Ditto
val anyDitto = mother.form.eggGroups.contains(EggGroup.DITTO) || father.form.eggGroups.contains(EggGroup.DITTO)
if (anyDitto)
return true
// Check for shared egg groups and opposite genders
val sharedEggGroups = mother.form.eggGroups.intersect(father.form.eggGroups)
return sharedEggGroups.isNotEmpty() && mother.gender != father.gender*/
}
/**
* Determines the base pre-evolution of a [Pokemon] species.
*
* @param species The Species of [Pokemon]
* @return The base pre-evolution Species of [Pokemon]
* @author Plastered_Crab
*/
private fun getPreEvoSpecies(species: Species): Species {
var currentSpecies = species
while (currentSpecies.preEvolution != null) {
currentSpecies = currentSpecies.preEvolution!!.species
}
return currentSpecies
}
/**
* Determines the [Gender] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @param form The [FormData] of the child
* @return The [Gender] of the child
* @author Apion, whatsy
*/
fun calculateGender(mother: Pokemon, father: Pokemon, form: FormData): Gender {
val maleRatio = form.maleRatio
if (maleRatio.toDouble() == -1.0) {
return Gender.GENDERLESS
}
val rand = Random.nextDouble(0.0, 1.0)
return if (rand < maleRatio) {
Gender.MALE
} else {
Gender.FEMALE
}
}
/**
* Determines the [FormData] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return The [FormData] of the child
* @author Nick, Apion, whatsy
*/
fun calculateForm(mother: Pokemon, father: Pokemon): FormData {
/*
* In most cases, if a hatched species has multiple forms (not dependent on in-battle conditions), it will
* often inherit the form of the mother or non-Ditto parent. Burmy will always hatch with the same cloak as
* its mother, and if bred between Mothim and Ditto, it will always hatch with a Plant Cloak. Rockruff with the
* Ability Own Tempo, which evolves into Lycanroc's Dusk Form, is treated as a separate form and can pass down
* this trait accordingly to its offspring. Oricorio, which changes its form using nectars, can also pass down
* its form by breeding.
*
* There are a few exceptions to this inheritance pattern:
*
* - Rotom will always hatch into its normal form, but can be changed afterwards.
* - Vivillon's pattern depends on the real-world, geographic area of the save file it originated from, regardless of its parent's pattern.
* - The form that Scatterbug will evolve into is predetermined when the Egg is first obtained, not when hatched or evolved.
* - Scatterbug hatched in Scarlet and Violet will only evolve into Fancy Pattern Vivillon.
* - Furfrou, whose trims are temporary, always hatch in its Natural Form.
* - Sinistea will always hatch as a Phony Form, even if its parent is an Antique Form.
*
* In addition, species with regional forms will hatch into whichever form is native to that region. However,
* if a parent of a foreign form and the same family is holding an Everstone, the offspring will be of that
* parent's form instead.
*
* The Gigantamax Factor, Alpha, Jumbo and Mini marks cannot be passed down by breeding.
*
* Pokémon such as Dunsparce, Wurmple, and Tandemaus, where the species and form they will evolve into is
* predetermined when they are first generated, do not follow form inheritance rules. The forms of the parents
* have no influence on the personality value or encryption constant.
*
* TODO - Handle this via form data parameters?
*/
return nonDittoPreferMother(mother, father).form
}
/**
* Determines the [Nature] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return The [Nature] of the child
* @author Nick, whatsy
*/
fun calculateNature(mother: Pokemon, father: Pokemon): Nature {
val motherHasEverstone = mother.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }.`is`(CobblemonItemTags.EVERSTONE)
val fatherHasEverstone = father.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }.`is`(CobblemonItemTags.EVERSTONE)
return when {
motherHasEverstone && fatherHasEverstone -> if (Random.nextInt(100) < 50) mother.nature else father.nature
motherHasEverstone -> mother.nature
fatherHasEverstone -> father.nature
else -> Natures.getRandomNature()
}
}
/**
* Determines the [Ability] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @param child The [FormData] of the child
* @return The [Ability] of the child
* @author Nick, whatsy
*/
fun calculateAbility(mother: Pokemon, father: Pokemon, child: FormData): Ability {
val parent = this.nonDittoPreferMother(mother, father)
val hidden = parent.form.abilities.filter { it.type == HiddenAbilityType }
.map { it.template.name }
.map { it == parent.ability.name }
.firstOrNull()
return if(hidden == true) {
val rng = Random.nextInt(0, 100) < 60
if(rng) {
parent.ability
} else {
child.abilities.select(child.species, child.aspects.toSet()).first
}
} else {
val rng = Random.nextInt(0, 100) < 80
if(rng) {
parent.ability
} else {
child.abilities.select(child.species, child.aspects.toSet()).first
}
}
}
/**
* Determines the [MoveSet] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @form the form of the [Pokemon]
* @return The [MoveSet] of the child
* @author Nick, Apion, whatsy
*/
// TODO: This, based off of https://pokemondb.net/mechanics/breeding
fun calculateMoveset(mother: Pokemon, father: Pokemon, form: FormData, eggMoves: List<MoveTemplate>? = null): MoveSet {
val babyLearnset = form.moves.levelUpMoves[1] ?: emptySet()
val inheritedMoves = mutableListOf<MoveTemplate>()
// Add Egg Moves using the JSON registry based on parent breeding rules
//val eggMoves = BreedingRules.getEggMoveList(mother, father, babySpecies.resourceIdentifier)
if (eggMoves != null) {
inheritedMoves.addAll(eggMoves)
}
// Add TM/HM Moves from both mother and father
val tmHmMoves = (mother.moveSet.getMoves() + father.moveSet.getMoves())
.map { it.template }
.filter { form.species.moves.tmMoves.contains(it) }
inheritedMoves.addAll(tmHmMoves)
// Add Inherited Level-Up Moves
inheritedMoves.addAll(mother.moveSet.getMoves()
.map { it.template }
.filter { it in babyLearnset })
inheritedMoves.addAll(father.moveSet.getMoves()
.map { it.template }
.filter { it in babyLearnset })
// Add Baby's Default Moves
inheritedMoves.addAll(babyLearnset)
// TODO sort the moves properly
// Limit to 4 moves, prioritizing certain moves first based off rules set by the games
val finalMoves = inheritedMoves.distinct().sortedWith(
compareByDescending<MoveTemplate> { it.name == "volt_tackle" } // Volt Tackle first
.thenByDescending { eggMoves?.contains(it) == true } // Egg moves next
.thenByDescending { tmHmMoves.contains(it) } // TM/HM moves after
.thenByDescending { babyLearnset.contains(it) } // Default moves last
).take(4)
val moveSet = MoveSet()
finalMoves.forEach { moveTemplate ->
moveSet.add(moveTemplate.create())
}
return moveSet
}
/**
* Determines the [IVs] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return The [IVs] of the child
* @author Nick, whatsy, Apion
*/
fun calculateIVs(mother: Pokemon, father: Pokemon): IVs {
//FIXME: Use registry holder
val motherItem = mother.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
val fatherItem = father.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
val isMotherDestinyKnotted = motherItem.`is`(CobblemonItemTags.DESTINY_KNOT)
val isFatherDestinyKnotted = fatherItem.`is`(CobblemonItemTags.DESTINY_KNOT)
val passedStats = mutableSetOf<Stat>()
val childStats = IVs.createRandomIVs()
var numPassed = if(isMotherDestinyKnotted || isFatherDestinyKnotted) {
5
} else {
3
}
fun calcForcedStat(holder: Holder<Item>?, mon: Pokemon) {
fun passStat(stats: Stat) {
if (!passedStats.contains(stats)) {
childStats[stats] = mon.ivs[stats]!!
passedStats.add(stats)
numPassed--
}
}
if(holder == null) {
return
}
if(holder.`is`(CobblemonItemTags.POWER_ANKLET)) {
passStat(Stats.SPEED)
} else if(holder.`is`(CobblemonItemTags.POWER_BAND)) {
passStat(Stats.SPECIAL_DEFENCE)
} else if(holder.`is`(CobblemonItemTags.POWER_BELT)) {
passStat(Stats.DEFENCE)
} else if(holder.`is`(CobblemonItemTags.POWER_BRACER)) {
passStat(Stats.ATTACK)
} else if(holder.`is`(CobblemonItemTags.POWER_LENS)) {
passStat(Stats.SPECIAL_ATTACK)
} else if(holder.`is`(CobblemonItemTags.POWER_WEIGHT)) {
passStat(Stats.HP)
}
}
calcForcedStat(fatherItem, father)
calcForcedStat(motherItem, mother)
val parentIvs = mother.ivs.toMutableList()
parentIvs.addAll(father.ivs)
while (numPassed > 0 && parentIvs.isNotEmpty()) {
val statPicked: Map.Entry<Stat, Int> = parentIvs.random()
if (!passedStats.contains(statPicked.key)) {
passedStats.add(statPicked.key)
childStats[statPicked.key] = statPicked.value
parentIvs.removeAll {it.key == statPicked.key}
numPassed--
}
}
return childStats
}
/**
* Determines the [PokeBall] of a child
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @return The [PokeBall] of the child
* @author Nick, whatsy
*/
fun calculatePokeball(mother: Pokemon, father: Pokemon): PokeBall {
/*
* The Master Ball, Cherish Ball, and Strange Ball cannot be inherited via breeding; instead, the game treats
* any parent obtained in those balls as if they were in a standard Poké Ball for the purposes of inheritance.
* To add Poké Balls to this list, add to the defaulting_poke_balls.json item tag.
*/
val anyDitto = listOf(mother, father).any { it.form.eggGroups.contains(EggGroup.DITTO) }
val motherBallItem = mother.caughtBall.item().let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
val fatherBallItem = father.caughtBall.item().let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
println(motherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS))
val motherBall = if (motherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS)) PokeBalls.POKE_BALL else mother.caughtBall
val fatherBall = if (fatherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS)) PokeBalls.POKE_BALL else father.caughtBall
val ball: PokeBall = if(anyDitto) {
if (nonDittoPreferMother(mother, father) == mother) motherBall else fatherBall
} else if(mother.species.resourceIdentifier == father.species.resourceIdentifier) {
if(Random.nextInt(100) < 50) {
motherBall
} else {
fatherBall
}
} else {
motherBall
}
return ball
}
/**
* If the mother is a Ditto, returns the father.
* Otherwise, returns the mother.
*
* @param mother The mother [Pokemon]
* @param father The father [Pokemon]
* @author Nick
*/
fun nonDittoPreferMother(mother: Pokemon, father: Pokemon): Pokemon {
return if(mother.form.eggGroups.contains(EggGroup.DITTO)) father else mother
}
fun resetBreedingCooldown(pokemon: Pokemon): Int {
val eggCycle = (pokemon.species.eggCycles).coerceAtLeast(1)
val minEggCycle = 5 // this is the lowest eggCycle (like caterpie)
val avgEggCycle = 40 // average eggCycle value
val minTime = 5 // in minutes
val avgTime = 20 // in minutes
val slope = (avgTime - minTime) / (avgEggCycle - minEggCycle)
val intercept = minTime - slope * minEggCycle
val cooldown = slope * eggCycle + intercept
return cooldown * 1200 // convert to ticks
}
fun calcTimeToHatch(mother: Pokemon, father: Pokemon): Int {
val avgEggCycle = ((mother.species.eggCycles + father.species.eggCycles) / 2).toInt()
val cycleToMinute = 2
return (avgEggCycle * cycleToMinute) * 1200 // convert to ticks
}
fun checkCompatibility(mother: Pokemon, father: Pokemon): Boolean {
val sameSpecies = mother.species == father.species
val sameTrainer = mother.getOwnerUUID() == father.getOwnerUUID()
val chance = when {
sameSpecies && !sameTrainer -> 70 // Same species, different trainer
sameSpecies && sameTrainer -> 50 // Same species, same trainer
!sameSpecies && !sameTrainer -> 50 // Different species, different trainer
!sameSpecies && sameTrainer -> 20
else -> 0 // Different species, same trainer
}
return Random.nextInt(100) < chance
}
}
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.breeding
import com.cobblemon.mod.common.pokemon.Pokemon
data class BreedingResult(
val pokemon: EggPokemon? = null,
val timeToHatch: Int = 20000,
val successful: Boolean = pokemon != null
)
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.breeding
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.item.components.EggComponent
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
import java.util.*
data class Egg(
val species: ResourceLocation, // Species of the Pokémon
val parentOne: Pokemon?, // First parent's UUID
val parentTwo: Pokemon?, // Second parent's UUID
val form: String, // Form of the Pokémon
var timeToHatch: Float // Time in ticks to hatch
) {
companion object {
val CODEC: Codec<Egg?> = RecordCodecBuilder.create { instance ->
instance.group(
ResourceLocation.CODEC.optionalFieldOf("species", cobblemonResource("unknown")).forGetter { it?.species ?: cobblemonResource("unknown") },
Pokemon.CODEC.optionalFieldOf("parentOne").forGetter { it?.parentOne?.let { Optional.of(it) } ?: Optional.empty() },
Pokemon.CODEC.optionalFieldOf("parentTwo").forGetter { it?.parentTwo?.let { Optional.of(it) } ?: Optional.empty() },
Codec.STRING.optionalFieldOf("form", "").forGetter { it?.form ?: "" },
Codec.FLOAT.optionalFieldOf("timeToHatch", 0F).forGetter { it?.timeToHatch ?: 0F }
).apply(instance) { species, parentOneOpt, parentTwoOpt, form, timeToHatch ->
if (species != cobblemonResource("unknown") || timeToHatch != 0F || parentOneOpt.isPresent || parentTwoOpt.isPresent || form.isNotEmpty()) {
Egg(
species = species,
parentOne = parentOneOpt.orElse(null),
parentTwo = parentTwoOpt.orElse(null),
form = form,
timeToHatch = timeToHatch
)
} else {
null // Represents no egg.
}
}
}
}
fun createEggItemStack(): ItemStack {
// Create a new ItemStack using the Pokémon Egg item
val eggItemStack = CobblemonItems.POKEMON_EGG.defaultInstance
// Add the EggData component to the ItemStack
val eggComponent = EggComponent(this)
eggItemStack.set(CobblemonItemComponents.EGG_DATA, eggComponent)
return eggItemStack
}
}
/*fun getPattern(): EggPattern? {
return EggPatterns.patternMap[patternId]
}
companion object {
fun fromBlockNbt(blockNbt: CompoundTag): Egg {
val eggNbt = blockNbt.get(DataKeys.EGG) as CompoundTag
return fromNbt(eggNbt)
}
fun fromNbt(nbt: CompoundTag): Egg {
return Egg(
EggPokemon.fromNBT(nbt.get(DataKeys.HATCHED_POKEMON) as CompoundTag),
ResourceLocation.tryParse(nbt.getString(DataKeys.EGG_PATTERN))!!,
nbt.getString(DataKeys.PRIMARY_COLOR),
if (nbt.contains(DataKeys.SECONDARY_COLOR)) nbt.getString(DataKeys.SECONDARY_COLOR) else null,
nbt.getInt(DataKeys.TIME_TO_HATCH)
)
}
//Less expensive than deserializing the whole thing, used for the color provider since it gets
//called every frame
fun getColorsFromNbt(nbt: CompoundTag): Pair<String, String> {
return Pair(
nbt.getString(DataKeys.PRIMARY_COLOR),
nbt.getString(DataKeys.SECONDARY_COLOR)
)
}
}
}*/
@@ -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.pokemon.breeding
import net.minecraft.resources.ResourceLocation
/**
* Represents a pattern on an egg
*
* @author Apion
* @since January 2nd, 2024
*/
data class EggPattern (
val model: ResourceLocation,
val baseTexturePath: ResourceLocation,
val overlayTexturePath: ResourceLocation?,
val baseInvSpritePath: ResourceLocation,
val overlayInvSpritePath: ResourceLocation?,
)
@@ -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.pokemon.breeding
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.server.packs.PackType
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
object EggPatterns : JsonDataRegistry<EggPattern> {
override val id = cobblemonResource("egg_patterns")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<EggPatterns>()
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
override val typeToken = TypeToken.get(EggPattern::class.java)
override val resourcePath = "egg_patterns"
val patternMap = hashMapOf<ResourceLocation, EggPattern>()
override fun reload(data: Map<ResourceLocation, EggPattern>) {
patternMap.putAll(data)
}
override fun sync(player: ServerPlayer) {
//TODO: Need to implement
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.breeding
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
class EggPokemon(
val ivs: IVs,
val nature: Nature,
val species: Species,
val formData: FormData,
val ability: Ability,
val moveSet: MoveSet,
val isShiny: Boolean,
val gender: Gender,
val pokeball: PokeBall
) {
fun generatePokemon(): Pokemon {
val result = Pokemon()
ivs.forEach {
result.setIV(it.key, it.value)
}
result.species = species
result.gender = gender
result.nature = nature
result.moveSet.clear()
result.moveSet.copyFrom(moveSet)
result.shiny = isShiny
result.caughtBall = pokeball
result.form = formData
result.ability = ability
return result
}
fun toNbt(): CompoundTag {
val result = CompoundTag()
val ivNbt = ivs.saveToNBT(CompoundTag())
result.put(DataKeys.POKEMON_IVS, ivNbt)
result.putString(DataKeys.POKEMON_NATURE, nature.name.toString())
result.putString(DataKeys.POKEMON_SPECIES_IDENTIFIER, species.resourceIdentifier.toString())
result.putString(DataKeys.POKEMON_FORM_ID, formData.formOnlyShowdownId())
val abilityNBT = ability.saveToNBT(CompoundTag())
result.put(DataKeys.POKEMON_ABILITY, abilityNBT)
result.put(DataKeys.POKEMON_MOVESET, moveSet.getNBT())
result.putBoolean(DataKeys.POKEMON_SHINY, isShiny)
result.putString(DataKeys.POKEMON_GENDER, gender.name)
result.putString(DataKeys.POKEMON_CAUGHT_BALL, pokeball.name.toString())
return result
}
companion object {
// I heard you like casts so I put some casts in your casts
fun fromNBT(nbt: CompoundTag): EggPokemon {
val species = PokemonSpecies.getByIdentifier(ResourceLocation.tryParse(nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER))!!)!!
val abilityNBT = nbt.getCompound(DataKeys.POKEMON_ABILITY) ?: CompoundTag()
val abilityName = abilityNBT.getString(DataKeys.POKEMON_ABILITY_NAME).takeIf { it.isNotEmpty() } ?: "runaway"
val moveSet = MoveSet()
val pokeball = nbt.getString(DataKeys.POKEMON_CAUGHT_BALL)
return EggPokemon(
IVs().loadFromNBT(nbt.get(DataKeys.POKEMON_IVS) as CompoundTag) as IVs,
Natures.getNature(ResourceLocation.tryParse(nbt.getString(DataKeys.POKEMON_NATURE))!!)!!,
species,
species.forms.firstOrNull { it.formOnlyShowdownId() == nbt.getString(DataKeys.POKEMON_FORM_ID) } ?: species.standardForm,
Abilities.getOrException(abilityName).create(abilityNBT),
moveSet.loadFromNBT(nbt),
nbt.getBoolean(DataKeys.POKEMON_SHINY),
Gender.valueOf(nbt.getString(DataKeys.POKEMON_GENDER)),
PokeBalls.getPokeBall(ResourceLocation.tryParse(pokeball)!!) ?: PokeBalls.POKE_BALL
)
}
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.moves
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
@@ -20,7 +21,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
open class Learnset : ClientDataSynchronizer<Learnset> {
class Interpreter(
val loadMove: (JsonElement, Learnset) -> Boolean
val loadMove: (JsonElement, Learnset) -> Boolean
) {
companion object {
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
@@ -28,10 +29,10 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val str = element.takeIf { it.isJsonPrimitive }?.asString ?: return@Interpreter false
if (str.startsWith(prefix)) {
Moves.getByName(str.substringAfter(":"))
?.let {
list(learnset).add(it)
return@Interpreter true
}
?.let {
list(learnset).add(it)
return@Interpreter true
}
}
return@Interpreter false
}
@@ -67,11 +68,11 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
val interpreters = mutableListOf(
tmInterpreter,
eggInterpreter,
tutorInterpreter,
levelUpInterpreter,
formChangeInterpreter
tmInterpreter,
eggInterpreter,
tutorInterpreter,
levelUpInterpreter,
formChangeInterpreter
)
}
@@ -87,11 +88,25 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val formChangeMoves = mutableListOf<MoveTemplate>()
fun getLevelUpMovesUpTo(level: Int) = levelUpMoves
.entries
.filter { it.key <= level }
.sortedBy { it.key }
.flatMap { it.value }
.toSet()
.entries
.filter { it.key <= level }
.sortedBy { it.key }
.flatMap { it.value }
.toSet()
fun tmLearnableMoves(): MutableList<MoveTemplate> {
val moves = mutableListOf<MoveTemplate>()
moves.addAll(tmMoves)
moves.addAll(tutorMoves)
moves.addAll(eggMoves)
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
moves.addAll(evolutionMoves)
moves.addAll(formChangeMoves)
return moves
}
// We only sync level up moves atm
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
@@ -68,7 +68,7 @@ object SpawnLoader {
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
@@ -66,6 +66,7 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawnablePosition>() {
return false
}
}
return true
}
@@ -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.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.position.NestSpawnablePosition
import com.cobblemon.mod.common.util.Merger
import net.minecraft.world.level.block.Block
/**
* A spawning condition that applies to [NestSpawnablePosition]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class NestSpawningCondition: SpawningCondition<NestSpawnablePosition>() {
override fun spawnablePositionClass() = NestSpawnablePosition::class.java
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(spawnablePosition: NestSpawnablePosition): Boolean {
if (!super.fits(spawnablePosition)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> spawnablePosition.nearbyBlockTypes.any { cond.fits(it, spawnablePosition.blockRegistry) } }) {
return false
}
return true
}
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
super.copyFrom(other, merger)
if (other is NestSpawningCondition) {
neededNearbyBlocks = merger.merge(neededNearbyBlocks, other.neededNearbyBlocks)?.toMutableList()
}
}
override fun isValid(): Boolean {
val containsNullValues = neededNearbyBlocks != null && neededNearbyBlocks!!.any {it == null}
return super.isValid() && !containsNullValues
}
companion object {
const val NAME = "nest"
}
}
@@ -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.spawning.influence
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import net.minecraft.resources.ResourceLocation
/**
* The grotto influence will normalize bucket weights and prevent non-habitat spawns from spawning.
*
* This influence exists once even if many grotto blocks are detected in the spawning zone.
*
* @author Hiroku
* @since April 20th, 2025
*/
class GrottoImpactedSpawningInfluence(
val structureIds: Set<ResourceLocation>
): BucketNormalizingInfluence(tier = 3) {
companion object {
const val HABITAT_LABEL = "habitat"
}
val markers = structureIds.map(ResourceLocation::toString)
override fun affectSpawnablePosition(spawnablePosition: SpawnablePosition) {
spawnablePosition.markers.addAll(markers)
}
override fun affectSpawnable(detail: SpawnDetail, spawnablePosition: SpawnablePosition) = HABITAT_LABEL in detail.labels
// TODO potentially insert spawns? Grotto block could supply some spawns
/*companion object {
private val LOGGER = LoggerFactory.getLogger("GrottoInfluence")
}
override fun affectSpawnable(detail: SpawnDetail, spawnablePosition: SpawningContext): Boolean {
if (detail !is PokemonSpawnDetail) return true
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return false
// If strict filtering enabled, reject anything not in allowed list
if (allowedSpawns != null && !allowedSpawns.contains(detailId)) {
LOGGER.info("[GrottoInfluence] Rejecting ${detailId.path} — not in current moon phase group.")
return false
}
return true
}
override fun affectWeight(detail: SpawnDetail, spawnablePosition: SpawningContext, weight: Float): Float {
if (detail !is PokemonSpawnDetail) return weight
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return weight
val boostTargetList = allowedSpawns ?: Habitats.getHabitat(structureId)?.spawnPool?.flatten() ?: emptyList()
return if (boostTargetList.contains(detailId)) {
LOGGER.info("[GrottoInfluence] Boosting spawn weight for ${detailId.path} (structure: $structureId)")
weight * 100f
} else {
weight
}
}*/
}
@@ -6,51 +6,60 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
///*
// * Copyright (C) 2023 Cobblemon Contributors
// *
// * This Source Code Form is subject to the terms of the Mozilla Public
// * License, v. 2.0. If a copy of the MPL was not distributed with this
// * file, You can obtain one at https://mozilla.org/MPL/2.0/.
// */
//
//package com.cobblemon.mod.common.api.spawning.influence.detector
//
//import com.cobblemon.mod.common.CobblemonPoiTypes
//import com.cobblemon.mod.common.api.spawning.influence.GrottoImpactedSpawningInfluence
//import com.cobblemon.mod.common.api.spawning.influence.UnconditionalZoneSpawningInfluence
//import com.cobblemon.mod.common.api.spawning.influence.ZoneSpawningInfluence
//import com.cobblemon.mod.common.api.spawning.spawner.Spawner
//import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
//import com.cobblemon.mod.common.util.toBlockPos
//import net.minecraft.core.BlockPos
//import net.minecraft.core.Holder
//import net.minecraft.server.level.ServerLevel
//import net.minecraft.world.entity.ai.village.poi.PoiManager
//import net.minecraft.world.entity.ai.village.poi.PoiType
//import net.minecraft.world.level.block.state.BlockState
//
//object GrottoBlockDetector : SpawningInfluenceDetector {
// @JvmField
// val RANGE: Int = 48
//
// override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): List<ZoneSpawningInfluence> {
// val world = input.world
// val searchRange = maxOf(RANGE, input.length * 2, input.height * 2)
// val grottoBlockPositions = world.poiManager.findAll(
// { holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
// { true },
// input.getCenter().toBlockPos(),
// searchRange,
// PoiManager.Occupancy.ANY
// ).toList()
//
// return if (grottoBlockPositions.isEmpty()) {
// emptyList()
// } else {
// listOf(UnconditionalZoneSpawningInfluence(influence = GrottoImpactedSpawningInfluence()))
// }
// }
//
// override fun detectFromBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<ZoneSpawningInfluence>()
//}
package com.cobblemon.mod.common.api.spawning.influence.detector
import com.cobblemon.mod.common.CobblemonPoiTypes
import com.cobblemon.mod.common.api.spawning.influence.GrottoImpactedSpawningInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
import com.cobblemon.mod.common.util.toBlockPos
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.ai.village.poi.PoiManager
import net.minecraft.world.entity.ai.village.poi.PoiType
import net.minecraft.world.level.block.state.BlockState
object GrottoBlockDetector : SpawningInfluenceDetector {
@JvmField
val RANGE: Int = 48
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): List<SpawningZoneInfluence> {
val world = input.world
val searchRange = maxOf(RANGE, input.length * 2, input.height * 2)
val grottoBlockPositions = world.poiManager.findAll(
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
{ true },
input.getCenter().toBlockPos(),
searchRange,
PoiManager.Occupancy.ANY
).toList()
val structureIds = grottoBlockPositions.mapNotNull {
val isLoaded = world.isLoaded(it)
if (isLoaded) {
val blockEntity = world.getBlockEntity(it) as? GrottoBlockEntity ?: return@mapNotNull null
blockEntity.detectedStructure
} else {
null
}
}.toSet()
return if (grottoBlockPositions.isEmpty()) {
emptyList()
} else {
listOf(
UnconditionalSpawningZoneInfluence(
influence = GrottoImpactedSpawningInfluence(
structureIds = structureIds
)
)
)
}
}
override fun detectFromBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
}
@@ -33,7 +33,8 @@ interface SpawningInfluenceDetector {
val detectors = mutableSetOf<SpawningInfluenceDetector>(
LureCakeDetector,
SaccharineHoneyLogDetector,
IncenseSweetDetector
IncenseSweetDetector,
GrottoBlockDetector
)
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.nest
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.world.entity.Entity
/**
* A spawning cause that is embellished with nest information.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class NestSpawnCause(
spawner: Spawner,
entity: Entity?
) : SpawnCause(spawner, entity) {
// Crab Note: todo what do we put here? a reference to the nest entity causing this spawn cause?
}
@@ -0,0 +1,38 @@
/*
* 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.position.NestSpawnablePosition
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.level.ServerLevel
import net.minecraft.core.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Plastered_Crab
* @since February 3rd, 2024
*/
open class NestSpawner(
override val name: String = "nest",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<NestSpawnCause>(name, pool) {
override fun parseSpawnablePosition(cause: NestSpawnCause, world: ServerLevel, pos: BlockPos): NestSpawnablePosition? {
val spawnablePosition = NestSpawnablePosition(
cause = cause,
world = world,
pos = pos,
influences = influences
)
return spawnablePosition
}
}
@@ -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.spawning.position
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.world.level.block.Block
import net.minecraft.server.level.ServerLevel
import net.minecraft.core.BlockPos
import net.minecraft.world.phys.AABB
/**
* A spawnable position that was generated for a nest spawn action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class NestSpawnablePosition(
cause: NestSpawnCause,
world: ServerLevel,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawnablePosition(
cause = cause,
world = world,
position = pos,
light = world.getMaxLocalRawBrightness(pos),
skyLight = world.getMaxLocalRawBrightness(pos.above()),
canSeeSky = world.canSeeSkyFromBelowWater(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(AABB.ofSize(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.map { it.block }.distinct().toList() }
}
@@ -76,7 +76,7 @@ abstract class SpawnablePosition {
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 position. This generally shouldn't be consumed; use [getAllInfluences].*/
/** A list of [SpawningInfluence]s that apply due to this specific position.*/
abstract val influences: MutableList<SpawningInfluence>
/** Gets a cache of structures by block coordinates, grouped by chunk. */
abstract fun getStructureCache(pos: BlockPos): StructureChunkCache
@@ -192,7 +192,7 @@ abstract class SpawnablePosition {
/**
* Gets all influences that apply to this spawnable position, 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
* Technically we could simply 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) {
@@ -0,0 +1,227 @@
/*
* 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.CobblemonPoiTypes
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
import com.cobblemon.mod.common.util.server
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.ai.village.poi.PoiManager
import java.util.concurrent.ConcurrentHashMap
class GrottoSpawner(
name: String,
fallbackSpawns: SpawnPool,
manager: SpawnerManager
) : AreaSpawner(name, fallbackSpawns, manager) {
companion object {
private val cache = ConcurrentHashMap<BlockPos, Pair<SpawnPool, Long>>()
private const val CACHE_DURATION_MS = 10_000
}
override var ticksBetweenSpawns: Float = 120F
// todo use this getArea to try to use a new GrottoSpawnCause and use that to send the structure center so spawns are around the structures
override fun getZoneInput(cause: SpawnCause): SpawningZoneInput? {
// if (cause is GrottoSpawnCause) {
// val center = cause.structureCenter
// return SpawningArea(
// cause = cause,
// world = server()?.getLevel(cause.entityWorldId) ?: return null,
// baseX = center.x - 8,
// baseY = center.y - 4,
// baseZ = center.z - 8,
// length = 16,
// height = 8,
// width = 16
// )
// }
return null
}
override fun run(cause: SpawnCause): List<SpawnAction<*>> {
//println("[GrottoSpawner] run() starting for cause: $cause")
val area = getZoneInput(cause)
//println("[GrottoSpawner] getArea() result: $area")
val constrainedArea = if (area != null) constrainArea(area) else null
//println("[GrottoSpawner] constrainArea() result: $constrainedArea")
if (constrainedArea != null) {
val zone = spawningZoneGenerator.generate(this, constrainedArea)
val spawnablePositions = spawnablePositionResolver.resolve(this, spawnablePositionCalculators, zone)
val worldInfluences = zone.unconditionalInfluences
val bucket = chooseBucket(cause, this.influences + cause + worldInfluences)
val spawnActions = getSpawningSelector().select(this, bucket, spawnablePositions, max = 1)
return spawnActions
}
//println("[GrottoSpawner] Area is null or invalid, skipping spawn")
return emptyList()
}
// todo test tick
override fun tick() {
super.tick()
ticksUntilNextSpawn -= tickTimerMultiplier
if (ticksUntilNextSpawn > 0) return
val server = server() ?: return
val world = server.overworld() ?: return
for (player in world.players()) {
val searchOrigin = player.blockPosition()
val poiManager = world.poiManager
val grottoPOIs = poiManager.findAll(
{ it.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
{ true },
searchOrigin,
128, // Search radius
PoiManager.Occupancy.ANY
)
for (grottoPos in grottoPOIs) {
val blockEntity = world.getBlockEntity(grottoPos) as? GrottoBlockEntity ?: continue
val structureCenter = blockEntity.structureCenter ?: continue
// val cause = GrottoSpawnCause(this, chooseBucket(), player, structureCenter, grottoPos)
// val result = run(cause)
//
// //println("[GrottoSpawner] Grotto at $grottoPos spawn result: $result")
//
// if (result != null) {
// val (context, detail) = result
// val action = detail.doSpawn(context)
// //println("[GrottoSpawner] doSpawn: $detail -> $action")
// val completion = action.complete()
// //println("[GrottoSpawner] spawn complete: $completion")
// }
}
}
ticksUntilNextSpawn = ticksBetweenSpawns
}
// todo we want to make it so that it ignores conditions of the area and just use the SpawnPool of the JSON registry that has been cycled during that moon cycle
// override fun getSpawningSelector(): SpawningSelector {
// return object : SpawningSelector {
// override fun select(spawner: Spawner, bucket: SpawnBucket, contexts: List<SpawningContext>): Pair<SpawningContext, SpawnDetail>? {
// val pool = when {
// spawner is GrottoSpawner && contexts.firstOrNull()?.cause is GrottoSpawnCause ->
// spawner.getSpawnPool(contexts.first().cause)
// else -> spawner.getSpawnPool()
// }
//
// for (spawnablePosition in contexts) {
// val details = pool.details.filterIsInstance<PokemonSpawnDetail>()
// if (details.isEmpty()) continue
//
// val chosen = weightedRandom(details) { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
// //println("[GrottoSpawner] Selected ${chosen.pokemon.species} for spawn")
// return spawnablePosition to chosen
// }
//
// //println("[GrottoSpawner] No spawnable Pokémon found.")
// return null
// }
//
// override fun getTotalWeights(spawner: Spawner, contexts: List<SpawningContext>): Map<SpawnDetail, Float> {
// return spawner.getSpawnPool().details.associateWith { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
// }
//
// private fun <T> weightedRandom(entries: List<T>, weightFn: (T) -> Float): T {
// val totalWeight = entries.sumOf { weightFn(it).toDouble() }
// var roll = Math.random() * totalWeight
// for (entry in entries) {
// roll -= weightFn(entry)
// if (roll <= 0) return entry
// }
// return entries.last()
// }
// }
// }
override fun getSpawnPool(): SpawnPool {
return spawns
}
// todo new getSpawnPool
// fun getSpawnPool(cause: SpawnCause): SpawnPool {
// val server = server() ?: return spawns
// val world = server.overworld() ?: return spawns
// val now = System.currentTimeMillis()
//
// if (cause !is GrottoSpawnCause) return spawns
//
// val center = cause.grottoPos
// val targetEntity = world.getChunk(center)?.getBlockEntity(center) as? GrottoBlockEntity ?: return spawns
//
// val structure = targetEntity.detectedStructure ?: return spawns
// val spawnGroup = targetEntity.currentSpawnGroup
// if (spawnGroup.isEmpty()) return spawns
//
// val cached = cache[center]
// if (cached != null && now - cached.second <= CACHE_DURATION_MS) {
// println("[GrottoSpawner] Cache hit at $center — using group: $spawnGroup")
// return cached.first
// }
//
// val habitat = Habitats.getHabitat(structure)
// if (habitat == null) {
// println("[GrottoSpawner] No habitat found for structure: $structure")
// return spawns
// }
//
// val selectedGroup = habitat.spawnPool.find { group ->
// group.spawns.toSet() == spawnGroup.toSet()
// } ?: return spawns.also {
// /*println("[GrottoSpawner] Could not match spawn group at $center to any group in habitat: $structure")
// println(" ↳ Current spawnGroup: $spawnGroup")
// println(" ↳ Habitat groups:")
// habitat.spawnPool.forEachIndexed { i, g ->
// println(" [$i]: ${g.spawns}")
// }*/
// }
//
// val poolCacheName = "grotto_pool_${center.x}_${center.y}_${center.z}"
// val pool = SpawnPool(poolCacheName)
// for (entry in selectedGroup.spawns) {
// val player = cause.entity as? ServerPlayer
// val party = player?.party()?.filterNotNull()
// val maxLevel = party?.maxOfOrNull { it.level } ?: 20
// val variation = 5
//
// val minLevel = max(1, maxLevel - variation)
// val maxLevelFinal = min(100, maxLevel + variation)
// val level = (minLevel..maxLevelFinal).random()
//
// val detail = PokemonSpawnDetail().apply {
// pokemon.species = entry.species.toString()
// weight = entry.weight.toFloat()
// pokemon.level = level
// }
//
// pool.details.add(detail)
// }
//
// /*println("[GrottoSpawner] Constructing pool for Grotto at ${cause.structureCenter}")
// println(" ↳ Detected structure: $structure")
// println(" ↳ Spawn group: $spawnGroup")
// println(" ↳ Selected group: $selectedGroup")
// println(" ↳ Pool size: ${pool.details.size}")*/
//
// cache[center] = pool to now
// return pool
// }
}
@@ -75,6 +75,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
remove(pokemon)
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
set(position, pokemon)
// sync TM moves here
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
return true
}
@@ -142,7 +142,7 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<CompoundTag> {
}
// TODO - Nicknames and Original Trainer Data
// result.nickname = this.find(nbt, "Nickname", NbtCompound::getString)
// result.nickname = this.find(nbt, "Nickname", CompoundTag::getString)
val ball = this.find(nbt, "CaughtBall", CompoundTag::getString)
result.caughtBall = if(ball != null) PokeBalls.getPokeBall(ResourceLocation.parse(ball)) ?: PokeBalls.POKE_BALL else PokeBalls.POKE_BALL
@@ -27,6 +27,7 @@ data class GeneralPlayerData(
var starterSelected: Boolean,
var starterUUID: UUID?,
var keyItems: MutableSet<ResourceLocation>,
var tmSet: MutableSet<ResourceLocation>,
var battleTheme: ResourceLocation?,
val extraData: MutableMap<String, PlayerDataExtension>,
) : InstancedPlayerData {
@@ -44,6 +45,7 @@ data class GeneralPlayerData(
starterSelected,
starterUUID,
advancementData.totalBattleVictoryCount == 0,
tmSet,
battleTheme
)
}
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.MODID
import com.cobblemon.mod.common.api.pokedex.PokedexManager
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.google.common.util.concurrent.ThreadFactoryBuilder
import java.util.UUID
import java.util.concurrent.Executors
@@ -66,6 +68,14 @@ open class PlayerInstancedDataStoreManager {
.infiniteIterations()
.tracker(ServerTaskTracker)
.build()
saveTasks[PlayerInstancedDataStoreTypes.TM_MOVES] = ScheduledTask.Builder()
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.TM_MOVES) }
.delay(30f)
.interval(120f)
.infiniteIterations()
.tracker(ServerTaskTracker)
.build()
}
open fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
@@ -126,4 +136,16 @@ open class PlayerInstancedDataStoreManager {
open fun getPokedexData(playerId: UUID): PokedexManager {
return get(playerId, PlayerInstancedDataStoreTypes.POKEDEX) as PokedexManager
}
fun getTMData(player: ServerPlayer): TMMoveManager {
return getTMData(player.uuid)
}
fun getTMData(playerId: UUID): TMMoveManager {
val data = get(playerId, PlayerInstancedDataStoreTypes.TM_MOVES)
if (data !is TMMoveManager) {
Cobblemon.LOGGER.warn("getTMData: No TMMoveManager found for $playerId (got ${data?.javaClass?.name})")
}
return data as TMMoveManager
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
@@ -27,11 +28,22 @@ object PlayerInstancedDataStoreTypes {
ClientPokedexManager::runAction,
ClientPokedexManager::runIncremental
))
val TM_MOVES = register(PlayerInstancedDataStoreType(
cobblemonResource("tm_moves"),
ClientTMMoveManager::decode,
ClientTMMoveManager::runAction,
ClientTMMoveManager::runIncremental
))
fun register(type: PlayerInstancedDataStoreType): PlayerInstancedDataStoreType {
println("Registering PlayerInstancedDataStoreType: ${type.id}")
types[type.id] = type
return type
}
fun getTypeById(id: ResourceLocation) = types[id]
fun getTypeById(id: ResourceLocation): PlayerInstancedDataStoreType? {
val type = types[id]
if (type == null) println("Unknown PlayerInstancedDataStoreType ID: $id")
return type
}
}
@@ -36,6 +36,7 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf(),
tmSet = mutableSetOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
)}
@@ -37,6 +37,7 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf(),
tmSet = mutableSetOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
)}
@@ -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.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.util.UUID
class TMMoveJsonBackend :
JsonBackedPlayerDataStoreBackend<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
override val gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
.create()
override val classToken = TypeToken.get(TMMoveManager::class.java)
override val defaultData = defaultDataFunc
override fun initialize(store: TMMoveManager) {
store.initialize()
}
companion object {
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
}
}
@@ -0,0 +1,42 @@
/*
* 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.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mongodb.client.MongoClient
import java.util.UUID
class TMMoveMongoBackend(
mongoClient: MongoClient,
databaseName: String,
collectionName: String
) : MongoBackedPlayerDataStoreBackend<TMMoveManager>(
mongoClient,
databaseName,
collectionName,
PlayerInstancedDataStoreTypes.TM_MOVES
) {
override val gson = GsonBuilder()
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
.create()
override val classToken = TypeToken.get(TMMoveManager::class.java)
override val defaultData = defaultDataFunc
override fun initialize(store: TMMoveManager) {
store.initialize()
}
companion object {
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
}
}
@@ -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.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import java.util.UUID
class TMMoveNbtBackend : NbtBackedPlayerData<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
override val codec = TMMoveManager.CODEC
override val defaultData = TMMoveJsonBackend.defaultDataFunc
}
@@ -34,6 +34,7 @@ data class ClientGeneralPlayerData(
var starterSelected: Boolean = false,
var starterUUID: UUID? = null,
var showChallengeLabel: Boolean = true,
var tmSet: MutableSet<ResourceLocation> = mutableSetOf(),
val battleTheme: ResourceLocation? = null
) : ClientInstancedPlayerData {
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
buf.writeCollection(tmSet) { pb, value -> pb.writeIdentifier(value)}
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
@@ -56,6 +58,7 @@ data class ClientGeneralPlayerData(
val showChallengeLabel = buffer.readBoolean()
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
val tmSet = buffer.readCollection({ mutableSetOf<ResourceLocation>() }) { it.readIdentifier() }
val battleTheme = buffer.readNullable { it.readIdentifier() }
val data = ClientGeneralPlayerData(
resetStarterPrompt,
@@ -64,6 +67,7 @@ data class ClientGeneralPlayerData(
starterSelected,
starterUUID,
showChallengeLabel,
tmSet,
battleTheme
)
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
@@ -0,0 +1,49 @@
/*
* 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.player.client
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class ClientTMMoveManager(
val learnedTMs: MutableSet<ResourceLocation>
) : ClientInstancedPlayerData {
override fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeCollection(learnedTMs) { _, id -> buf.writeString(id.toString()) }
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
val tms = buf.readCollection(
{ mutableSetOf<ResourceLocation>() },
{ buf.readString().let(ResourceLocation::tryParse) ?: ResourceLocation("minecraft", "empty") }
)
return SetClientPlayerDataPacket(
PlayerInstancedDataStoreTypes.TM_MOVES,
ClientTMMoveManager(tms)
)
}
fun runAction(data: ClientInstancedPlayerData) {
if (data !is ClientTMMoveManager) return
CobblemonClient.clientTMMoveData = data
}
fun runIncremental(data: ClientInstancedPlayerData) {
if (data !is ClientTMMoveManager) return
CobblemonClient.clientTMMoveData.learnedTMs.addAll(data.learnedTMs)
}
}
}
@@ -41,6 +41,40 @@ object CobblemonBiomeTags {
@JvmField
val HAS_REVIVAL_HERBS = create("has_feature/revival_herbs")
// Nests
@JvmField
val HAS_STICK_NEST = create("has_nest/nest_stick")
@JvmField
val HAS_STICK_NEST_TREES = create("has_nest/nest_stick_trees")
@JvmField
val HAS_LEAF_NEST_TREES = create("has_nest/nest_leaf_trees")
@JvmField
val HAS_SNOW_NEST = create("has_nest/nest_snow")
@JvmField
val HAS_SAND_NEST = create("has_nest/nest_sand")
@JvmField
val HAS_RED_SAND_NEST = create("has_nest/nest_red_sand")
@JvmField
val HAS_SEAGRASS_NEST = create("has_nest/nest_seagrass")
@JvmField
val HAS_WARM_OCEAN_NEST = create("has_nest/nest_warm_ocean")
@JvmField
val HAS_STONE_NEST = create("has_nest/nest_stone")
@JvmField
val HAS_STONE_NEST_CAVES = create("has_nest/nest_stone_caves")
@JvmField
val HAS_DEEPSLATE_NEST_CAVES = create("has_nest/nest_deepslate_caves")
@JvmField
val HAS_LUSH_NEST_CAVES = create("has_nest/nest_lush_caves")
@JvmField
val HAS_BASALT_NEST = create("has_nest/nest_basalt")
@JvmField
val HAS_NETHER_NEST = create("has_nest/nest_nether")
@JvmField
val HAS_SOUL_SAND_NEST = create("has_nest/nest_soul_sand")
@JvmField
val HAS_CHORUS_NEST = create("has_nest/nest_chorus")
// Has Ore tags
@JvmField
val HAS_DAWN_STONE_ORE = create("has_ore/ore_dawn_stone_normal")
@@ -89,6 +89,7 @@ object CobblemonBlockTags {
@JvmField val TUMBLESTONE_BRICKS = createTag("tumblestone_bricks")
@JvmField val TUMBLESTONES = createTag("tumblestones")
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
@JvmField val TYPE_GEM_BLOCKS = createTag("type_gem_blocks")
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
@JvmField val WALL_SIGNS = createTag("wall_signs")
@JvmField val WATER_STONE_ORES = createTag("water_stone_ores")
@@ -110,6 +110,7 @@ object CobblemonItemTags {
@JvmField val ZINC_INGREDIENTS = create("zinc_ingredients")
// Held Item Tags
@JvmField val DEFAULTING_POKE_BALLS = create("defaulting_poke_balls")
@JvmField val CONSUMED_IN_NPC_BATTLE = create("held/consumed_in_npc_battle")
@JvmField val CONSUMED_IN_PVP_BATTLE = create("held/consumed_in_pvp_battle")
@JvmField val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.tms
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import java.util.*
abstract class AbstractTMMoveManager {
abstract val uuid: UUID
abstract val learnedTMs: MutableSet<ResourceLocation>
abstract val storeType: PlayerInstancedDataStoreType
abstract fun toClientData(): ClientInstancedPlayerData
abstract fun toClientDataFrom(set: Set<ResourceLocation>): ClientInstancedPlayerData
open fun learn(tmId: ResourceLocation): Boolean {
if (learnedTMs.add(tmId)) {
syncClient(setOf(tmId))
sendTMToast(tmId)
return true
}
return false
}
private fun sendTMToast(tmId: ResourceLocation) {
val player = uuid.getPlayer() ?: return
val tm = TechnicalMachines.tmMap[tmId] ?: return
val packet = ToastPacket(
title = Component.literal("New TM Learned"), // todp lang for this
description = tm.translatedMoveName(),
icon = tm.createItemStack(),
frameTexture = ResourceLocation.parse("minecraft:toast/advancement"),
progress = -1F,
progressColor = 0x00FF00,
uuid = UUID.nameUUIDFromBytes(("tm_toast:${tmId}").toByteArray()),
behaviour = ToastPacket.Behaviour.SHOW_OR_UPDATE,
durationMs = 4000L
)
player.sendPacket(packet)
}
protected fun syncClient(updateSet: Set<ResourceLocation> = learnedTMs) {
uuid.getPlayer()?.sendPacket(
SetClientPlayerDataPacket(
type = storeType,
playerData = toClientDataFrom(updateSet),
isIncremental = true
)
)
}
open fun markDirty() {}
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.tms
import com.cobblemon.mod.common.tms.obtain.ImpossibleObtainMethod
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
import com.cobblemon.mod.common.tms.obtain.PokemonHasMoveObtainMethod
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
interface ObtainMethod {
val passive: Boolean
/**
* Checks if this [ObtainMethod] matches the given player.
*/
fun matches(player: ServerPlayer): Boolean
/**
* Serializes this [ObtainMethod] to the buffer.
*/
fun writeToBuffer(buffer: RegistryFriendlyByteBuf)
companion object {
/**
* Deserializes an [ObtainMethod] from the buffer.
*/
fun readFromBuffer(buffer: RegistryFriendlyByteBuf): ObtainMethod {
val variant = buffer.readUtf()
return when (variant) {
"cobblemon:impossible" -> ImpossibleObtainMethod()
"cobblemon:none" -> NoneObtainMethod()
"cobblemon:advancement" -> {
val advancement = buffer.readResourceLocation()
PlayerHasAdvancementObtainMethod(advancement)
}
"cobblemon:y_level" -> {
val yLevel = buffer.readVarInt()
val operator = buffer.readUtf()
PlayerYObtainMethod(yLevel, operator)
}
"cobblemon:pokemon_knows" -> {
val moveId = buffer.readUtf()
PokemonHasMoveObtainMethod(moveId)
}
else -> throw IllegalArgumentException("Unknown ObtainMethod variant: $variant")
}
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.tms
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.resources.ResourceLocation
import kotlin.reflect.KClass
/**
* A type adapter for [ObtainMethod]s.
* For the default implementation see [CobblemonObtainMethodAdapter].
*
* @author Apion
* @since November 22nd, 2023
*/
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
/**
* Register a [ObtainMethod] to be used by this adapter.
*
* @param type The [KClass] of the [ObtainMethod].
* @param identifier The expected [ResourceLocation] in the parsed JSON.
*/
fun register(type: KClass<out ObtainMethod>, identifier: ResourceLocation)
}
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
import com.cobblemon.mod.common.api.tms.AbstractTMMoveManager
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import java.util.UUID
class TMMoveManager(
override val uuid: UUID,
override val learnedTMs: MutableSet<ResourceLocation> = mutableSetOf()
) : AbstractTMMoveManager(), InstancedPlayerData {
override val storeType = PlayerInstancedDataStoreTypes.TM_MOVES
override fun initialize() {}
override fun toClientData(): ClientTMMoveManager {
return ClientTMMoveManager(learnedTMs.toMutableSet())
}
override fun toClientDataFrom(set: Set<ResourceLocation>): ClientTMMoveManager {
return ClientTMMoveManager(set.toMutableSet())
}
fun syncTMsFromPokemon(pokemon: Pokemon) {
val learnableTMs = TechnicalMachines.tmMap.values
.filter { tm -> pokemon.allAccessibleMoves.contains(tm.moveName) }
for (tm in learnableTMs) {
learn(tm.id)
}
}
companion object {
val CODEC: Codec<TMMoveManager> = RecordCodecBuilder.create { instance ->
instance.group(
Codec.STRING.xmap(UUID::fromString, UUID::toString)
.fieldOf("uuid")
.forGetter { it.uuid },
Codec.list(ResourceLocation.CODEC)
.xmap({ it.toMutableSet() }, { it.toList() })
.fieldOf("learnedTMs")
.forGetter { it.learnedTMs }
).apply(instance) { uuid, learnedTMs ->
TMMoveManager(uuid, learnedTMs)
}
}
}
}
@@ -0,0 +1,120 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
import com.cobblemon.mod.common.util.lang
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
class TechnicalMachine(
val moveName: MoveTemplate,
val recipe: List<TechnicalMachineRecipe>?,
val obtainMethods: List<ObtainMethod> = emptyList(),
val type: String
) {
lateinit var id: ResourceLocation
/*
val move: MoveTemplate? = Moves.getByName(moveName.displayName.toString()).also {
if (it == null) {
Cobblemon.LOGGER.error("MoveTemplate not found for moveName: $moveName")
}
}*/
companion object {
/**
* Filters all available [TechnicalMachines] based on three optional arguments
*
* @param search A [String] to check against move names
* @param type An [ElementalType] to check for
* @param pokemon A [Pokemon] to check the [Learnset] of
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
*/
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?, player: Player? = null): MutableSet<TechnicalMachine> {
val tms = TechnicalMachines.tmMap.values.toMutableSet()
type?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (ElementalTypes.get(tm.type) != type) {
iterator.remove()
}
}
}
pokemon?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (!pokemon.species.moves.tmLearnableMoves().contains(tm.moveName)) {
iterator.remove()
}
}
}
search?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (!tm.translatedMoveName().string.contains(search, ignoreCase = true)) {
iterator.remove()
}
}
}
player?.let {
val learnedTMs = CobblemonClient.clientTMMoveData?.learnedTMs ?: return@let
tms.retainAll { tm -> learnedTMs.contains(tm.id) }
}
return tms
}
}
/**
* Unlocks this [TechnicalMachine] for the player.
*
* @param player The [ServerPlayer] to give this [TechnicalMachine] to.
* @return Whether the player was successfully granted the [TechnicalMachine]
*/
fun unlock(player: ServerPlayer): Boolean {
Cobblemon.playerDataManager.getGenericData(player) // .get(player, ).tmSet.add(id)// .playerData.get(player).tmSet.add(id)
if (!obtainMethods.any { it is NoneObtainMethod }) {
player.sendSystemMessage(lang("tms.unlock_tm", moveName.displayName))
}
return true
}
/**
* Returns a [MutableComponent] of the translated move name of this [TechnicalMachine]
*
* @return This [TechnicalMachine]'s move name, translated
*/
fun translatedMoveName(): MutableComponent = moveName.displayName
fun createItemStack(): ItemStack {
val stack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
TMMoveComponent.setTMMove(stack, moveName)
return stack
}
}
@@ -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.tms
import net.minecraft.resources.ResourceLocation
/**
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
*/
data class TechnicalMachineRecipe(
val item: ResourceLocation,
val count: Int
)
@@ -0,0 +1,75 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.item.TechnicalMachineItem
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.registry.ItemTagCondition
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.world.item.ItemStack
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
override val gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
.registerTypeAdapter(MoveTemplate::class.java, MoveTemplateAdapter)
.create()
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
override val resourcePath = "tms"
override val id = cobblemonResource("technical_machines")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<TechnicalMachines>()
val tmMap = mutableMapOf<ResourceLocation, TechnicalMachine>()
val moveToTM = mutableMapOf<MoveTemplate, TechnicalMachine>()
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
val passiveTms = mutableMapOf<ResourceLocation, TechnicalMachine>()
override fun reload(data: Map<ResourceLocation, TechnicalMachine>) {
data.forEach { (id, tm) ->
//TODO remove the logging
Cobblemon.LOGGER.info("Processing TM: $id with moveName: ${tm.moveName}")
if (tm.moveName == null) {
Cobblemon.LOGGER.error("Failed to resolve move for TM: $id with moveName: ${tm.moveName}")
} else {
Cobblemon.LOGGER.info("Resolved Move for TM: $id -> ${tm.moveName.name}")
}
tmMap[id] = tm
tm.id = id
moveToTM[tm.moveName] = tm
// Check for passive ObtainMethods
//if (tm.obtainMethods.any { it.passive }) passiveTms[id] = tm
}
}
override fun sync(player: ServerPlayer) { }
fun checkPassives(player: ServerPlayer) {
val playerTms = Cobblemon.playerDataManager.getGenericData(player).tmSet
passiveTms.forEach { (id, tm) ->
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
}
}
}
@@ -27,7 +27,10 @@ class ElementalType(
val displayName: MutableComponent,
val hue: Int,
val textureXMultiplier: Int,
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png")
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
val primaryColor: Int,
val secondaryColor: Int,
val typeGem: ResourceLocation
) {
companion object {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.types
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
@@ -22,134 +23,174 @@ object ElementalTypes {
name = "normal",
displayName = Component.translatable("cobblemon.type.normal"),
hue = 0xDDDDCF,
textureXMultiplier = 0
textureXMultiplier = 0,
primaryColor = 0xCCCCC1,
secondaryColor = 0xF9FFFC
)
val FIRE = register(
name = "fire",
displayName = Component.translatable("cobblemon.type.fire"),
hue = 0xE55C32,
textureXMultiplier = 1
textureXMultiplier = 1,
primaryColor = 0xFF8766,
secondaryColor = 0xFFD9A5
)
val WATER = register(
name = "water",
displayName = Component.translatable("cobblemon.type.water"),
hue = 0x4A9BE8,
textureXMultiplier = 2
textureXMultiplier = 2,
primaryColor = 0x7F9DFF,
secondaryColor = 0xA5EEFF
)
val GRASS = register(
name = "grass",
displayName = Component.translatable("cobblemon.type.grass"),
hue = 0x4DBC3C,
textureXMultiplier = 3
textureXMultiplier = 3,
primaryColor = 0x66CC99,
secondaryColor = 0xD6FFA5
)
val ELECTRIC = register(
name = "electric",
displayName = Component.translatable("cobblemon.type.electric"),
hue = 0xEFD128,
textureXMultiplier = 4
textureXMultiplier = 4,
primaryColor = 0xFFCF3F,
secondaryColor = 0xFFFFBF
)
val ICE = register(
name = "ice",
displayName = Component.translatable("cobblemon.type.ice"),
hue = 0x6BC3EF,
textureXMultiplier = 5
textureXMultiplier = 5,
primaryColor = 0x8CC7FF,
secondaryColor = 0xD6FFFF
)
val FIGHTING = register(
name = "fighting",
displayName = Component.translatable("cobblemon.type.fighting"),
hue = 0xC44C5C,
textureXMultiplier = 6
textureXMultiplier = 6,
primaryColor = 0xCC6673,
secondaryColor = 0xFFADA5
)
val POISON = register(
name = "poison",
displayName = Component.translatable("cobblemon.type.poison"),
hue = 0xA24BD8,
textureXMultiplier = 7
textureXMultiplier = 7,
primaryColor = 0x8D66CC,
secondaryColor = 0xEDA5FF
)
val GROUND = register(
name = "ground",
displayName = Component.translatable("cobblemon.type.ground"),
hue = 0xD89950,
textureXMultiplier = 8
textureXMultiplier = 8,
primaryColor = 0xE5A27E,
secondaryColor = 0xFFE1A5
)
val FLYING = register(
name = "flying",
displayName = Component.translatable("cobblemon.type.flying"),
hue = 0xBCC1FF,
textureXMultiplier = 9
textureXMultiplier = 9,
primaryColor = 0x8CB6FF,
secondaryColor = 0xE8DDFF
)
val PSYCHIC = register(
name = "psychic",
displayName = Component.translatable("cobblemon.type.psychic"),
hue = 0xD86AD6,
textureXMultiplier = 10
textureXMultiplier = 10,
primaryColor = 0xE48CFF,
secondaryColor = 0xFFCCF6
)
val BUG = register(
name = "bug",
displayName = Component.translatable("cobblemon.type.bug"),
hue = 0xA2C831,
textureXMultiplier = 11
textureXMultiplier = 11,
primaryColor = 0xAFCC66,
secondaryColor = 0xF6FFBF
)
val ROCK = register(
name = "rock",
displayName = Component.translatable("cobblemon.type.rock"),
hue = 0xAA9666,
textureXMultiplier = 12
textureXMultiplier = 12,
primaryColor = 0x997F4C,
secondaryColor = 0xDBC6A4
)
val GHOST = register(
name = "ghost",
displayName = Component.translatable("cobblemon.type.ghost"),
hue = 0x9572E5,
textureXMultiplier = 13
textureXMultiplier = 13,
primaryColor = 0x8E8CFF,
secondaryColor = 0xE8A5FF
)
val DRAGON = register(
name = "dragon",
displayName = Component.translatable("cobblemon.type.dragon"),
hue = 0x535DE8,
textureXMultiplier = 14
textureXMultiplier = 14,
primaryColor = 0xBA8CFF,
secondaryColor = 0xA5E5FF
)
val DARK = register(
name = "dark",
displayName = Component.translatable("cobblemon.type.dark"),
hue = 0x5C6CB2,
textureXMultiplier = 15
textureXMultiplier = 15,
primaryColor = 0x5966B2,
secondaryColor = 0xA5B1FF
)
val STEEL = register(
name = "steel",
displayName = Component.translatable("cobblemon.type.steel"),
hue = 0xC3CCE0,
textureXMultiplier = 16
textureXMultiplier = 16,
primaryColor = 0x858EB2,
secondaryColor = 0xF4FFFD
)
val FAIRY = register(
name = "fairy",
displayName = Component.translatable("cobblemon.type.fairy"),
hue = 0xEA727E,
textureXMultiplier = 17
textureXMultiplier = 17,
primaryColor = 0xFF8CB6,
secondaryColor = 0xFFD0CC
)
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int): ElementalType {
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int, primaryColor: Int, secondaryColor: Int): ElementalType {
return ElementalType(
name = name,
displayName = displayName,
hue = hue,
textureXMultiplier = textureXMultiplier
textureXMultiplier = textureXMultiplier,
primaryColor = primaryColor,
secondaryColor = secondaryColor,
typeGem = cobblemonResource("${name}_gem")
).also {
allTypes.add(it)
}
@@ -14,9 +14,11 @@ 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.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.battles.actor.MultiPokemonBattleActor
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.config.CobblemonConfig
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -297,6 +299,99 @@ object BattleBuilder {
}
}
@JvmOverloads
fun pveDoubles(
player: ServerPlayer,
pokemonEntity1: PokemonEntity,
pokemonEntity2: PokemonEntity,
leadingPokemon: UUID? = null,
battleFormat: BattleFormat = BattleFormat.GEN_9_DOUBLES,
cloneParties: Boolean = false,
healFirst: Boolean = false,
fleeDistance: Float = Cobblemon.config.defaultFleeDistance,
party: PartyStore = player.party()
): BattleStartResult {
// Prepare the player's team
val playerTeam = party.toBattleTeam(
clone = cloneParties,
healPokemon = healFirst,
leadingPokemon = leadingPokemon
).sortedBy { it.health <= 0 }
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
// Combine both wild Pokémon into a single BattleActor
val wildTeam = listOf(
BattlePokemon(pokemonEntity1.pokemon),
BattlePokemon(pokemonEntity2.pokemon)
)
val wildActor = MultiPokemonBattleActor(pokemonList = wildTeam, fleeDistance = Cobblemon.config.defaultFleeDistance)
val errors = ErroredBattleStart()
// Validate player's team
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
actorEntity = player,
requiredCount = battleFormat.battleType.slotsPerActor,
hadCount = playerActor.pokemonList.size
)
}
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
actorEntity = player,
requiredCount = battleFormat.battleType.slotsPerActor,
hadCount = playerActor.pokemonList.size
)
}
// Validate opponent Pokémon entities
if (pokemonEntity1.battleId != null) {
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
}
if (pokemonEntity2.battleId != null) {
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
}
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
}
// Set battle themes
playerActor.battleTheme = pokemonEntity1.getBattleTheme()
return if (errors.isEmpty) {
println("Player actor: ${playerActor.pokemonList.size} Pokémon")
println("WildActor Pokémon: ${wildActor.pokemonList.map { it.effectedPokemon.species.translatedName }}")
val side1 = BattleSide(playerActor)
val side2 = BattleSide(wildActor)
println("Side1 actors: ${side1.actors.map { it.uuid }}")
println("Side2 actors: ${side2.actors.map { it.uuid }}")
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = side1,
side2 = side2
).ifSuccessful { battle ->
println("Battle started with ID: ${battle.battleId}")
if (!cloneParties) {
pokemonEntity1.battleId = battle.battleId
pokemonEntity2.battleId = battle.battleId
}
}.ifErrored {
println("Battle start failed: ${it.errors}")
}
} else {
println("Battle failed due to validation errors: ${errors.errors}")
errors
}
}
/**
* Attempts to create a PvE battle against the given Pokémon.
*
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.battles.actor
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.ai.RandomBattleAI
@@ -17,13 +18,46 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import java.util.UUID
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.phys.Vec3
class MultiPokemonBattleActor(
pokemonList: List<BattlePokemon>,
artificialDecider: BattleAI = RandomBattleAI(),
uuid: UUID = UUID.randomUUID()
) : AIBattleActor(uuid, pokemonList, artificialDecider) {
pokemonList: List<BattlePokemon>,
override val fleeDistance: Float,
artificialDecider: BattleAI = RandomBattleAI(),
uuid: UUID = UUID.randomUUID()
) : AIBattleActor(uuid, pokemonList, artificialDecider), FleeableBattleActor {
override fun getName(): MutableComponent = "Wild Pokémon".text() // TODO probably remove by making it nullable
override fun nameOwned(name: String): MutableComponent = Component.literal(name)
override val type = ActorType.WILD
override fun getWorldAndPosition(): Pair<ServerLevel, Vec3>? {
// Ensure there are Pokémon in the list
if (pokemonList.isEmpty()) return null
// Gather all positions and ensure they share the same world
val positionsAndWorlds = pokemonList.mapNotNull { battlePokemon ->
val entity = battlePokemon.effectedPokemon.entity
val level = entity?.level() as? ServerLevel
val position = entity?.position()
if (level != null && position != null) {
level to position
} else {
null
}
}
if (positionsAndWorlds.isEmpty()) return null
// Ensure all Pokémon are in the same world
val world = positionsAndWorlds.first().first
if (positionsAndWorlds.any { it.first != world }) return null
// Compute the average position
val averagePosition = positionsAndWorlds
.map { it.second }
.reduce { acc, pos -> acc.add(pos) }
.scale(1.0 / positionsAndWorlds.size)
return world to averagePosition
}
}
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
import com.cobblemon.mod.common.block.HealingMachineBlock.Companion
import com.cobblemon.mod.common.block.HealingMachineBlock.Companion.CHARGE_LEVEL
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.block.entity.EggBlockEntity
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.cobblemon.mod.common.item.PokemonEggItem
import com.cobblemon.mod.common.item.components.EggComponent
import com.mojang.serialization.Dynamic
import com.mojang.serialization.MapCodec
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class EggBlock(properties: Properties) : BaseEntityBlock(properties) {
override fun setPlacedBy(
level: Level,
blockPos: BlockPos,
blockState: BlockState,
placer: LivingEntity?,
itemStack: ItemStack
) {
val entity = level.getBlockEntity(blockPos) as? EggBlockEntity
// Retrieve the EGG_COMPONENT from the item stack and set it in the block entity
val eggComponent = itemStack.get(CobblemonItemComponents.EGG_DATA)
eggComponent?.let { component ->
entity?.setEggData(component.egg) // Pass the Egg data to the block entity
println("Set egg in EggBlockEntity: ${component.egg?.species}")
}
super.setPlacedBy(level, blockPos, blockState, placer, itemStack)
}
override fun getShape(
state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
): VoxelShape {
return Shapes.or(
Shapes.box(5.0 / 16.0, 0.0, 5.0 / 16.0, 11.0 / 16.0, 2.0 / 16.0, 11.0 / 16.0), // Bottom section
Shapes.box(4.5 / 16.0, 1.25 / 16.0, 4.5 / 16.0, 11.5 / 16.0, 5.25 / 16.0, 11.5 / 16.0), // Middle section
Shapes.box(5.0 / 16.0, 5.25 / 16.0, 5.0 / 16.0, 11.0 / 16.0, 7.25 / 16.0, 11.0 / 16.0), // Upper section
Shapes.box(6.0 / 16.0, 7.25 / 16.0, 6.0 / 16.0, 10.0 / 16.0, 8.25 / 16.0, 10.0 / 16.0) // Top small section
)
}
override fun useWithoutItem(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
// Ensure this logic only runs on the server
if (level.isClientSide) {
return InteractionResult.SUCCESS // Client-side only needs to show the hand animation
}
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL // Cast safely to ServerPlayer
val entity = level.getBlockEntity(blockPos) as? EggBlockEntity ?: return InteractionResult.FAIL
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
}
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val entity = level.getBlockEntity(pos) as? EggBlockEntity
if (entity != null && !player.isCreative) {
entity.dropEgg(state, level, pos)
}
return super.playerWillDestroy(level, pos, state, player)
}
override fun codec(): MapCodec<out BaseEntityBlock> {
TODO("Not yet implemented")
}
override fun getRenderShape(blockState: BlockState): RenderShape {
return RenderShape.ENTITYBLOCK_ANIMATED
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = EggBlockEntity(pos, state)
}
@@ -32,7 +32,8 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
class FossilAnalyzerBlock(settings: Properties) : MultiblockBlock(settings), WorldlyContainerHolder {
class FossilAnalyzerBlock(settings: Properties) : MultiblockBlock(settings),
WorldlyContainerHolder {
init {
registerDefaultState(stateDefinition.any()
.setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH)
@@ -0,0 +1,129 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.LootParams
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import java.util.*
class GrottoBlock(properties: Properties) : Block(properties), EntityBlock {
var level: Level? = null
var pos: BlockPos? = null
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return GrottoBlockEntity(pos, state)
}
override fun <T : BlockEntity> getTicker(
level: Level,
state: BlockState,
type: BlockEntityType<T>
): BlockEntityTicker<T>? {
return if (!level.isClientSide && type == CobblemonBlockEntities.GROTTO_BLOCK) {
@Suppress("UNCHECKED_CAST")
GrottoBlockEntity.TICKER as BlockEntityTicker<T>
} else {
null
}
}
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
if (!level.isClientSide) {
val be = level.getBlockEntity(pos) as? GrottoBlockEntity ?: return InteractionResult.PASS
return be.onUse(player, hitResult)
}
return InteractionResult.SUCCESS
}
override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList<ItemStack> {
val blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) as? GrottoBlockEntity
val player = params.getOptionalParameter(LootContextParams.THIS_ENTITY) as? Player
if (player?.isCreative == true) return mutableListOf()
val mimic = blockEntity?.mimickedState?.block ?: return mutableListOf()
return mutableListOf(ItemStack(mimic))
}
override fun getRenderShape(state: BlockState): RenderShape {
return RenderShape.INVISIBLE
}
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
}
override fun getCollisionShape(
state: BlockState,
level: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape {
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
}
override fun getVisualShape(
state: BlockState,
world: BlockGetter,
pos: BlockPos,
context: CollisionContext
): VoxelShape {
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
}
override fun getOcclusionShape(state: BlockState, level: BlockGetter, pos: BlockPos): VoxelShape? {
return Shapes.block()
}
override fun useShapeForLightOcclusion(state: BlockState): Boolean {
return true
}
override fun spawnDestroyParticles(level: Level, player: Player, pos: BlockPos, state: BlockState) {
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
val mimicState = blockEntity?.mimickedState ?: return
mimicState.block.spawnDestroyParticles(level, player, pos, mimicState)
}
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, isMoving: Boolean) {
super.onPlace(state, level, pos, oldState, isMoving)
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
blockEntity?.updateLevelAndPos(level, pos)
}
override fun getSoundType(state: BlockState): SoundType {
val level = this.level ?: return super.getSoundType(state)
val pos = this.pos ?: return super.getSoundType(state)
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
val mimicState = blockEntity?.mimickedState ?: return super.getSoundType(state)
return mimicState.soundType
}
}
@@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
@@ -42,6 +43,8 @@ abstract class GrowableStoneBlock(
val nextStage: Block?
) : DirectionalBlock(settings) {
abstract val growthChance: Int
private val upShape: VoxelShape = Block.box(
xzOffset.toDouble(),
0.0,
@@ -95,20 +98,26 @@ abstract class GrowableStoneBlock(
.setValue(FACING, Direction.DOWN))
}
abstract fun canGrow(pos: BlockPos, world: BlockGetter): Boolean
abstract fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
if (world.random.nextInt(growthChance) == 0 && canGrow(state, pos, world)) {
val block = nextStage
if (block != null) {
val newState = block.defaultBlockState().setValue(FACING, state.getValue(FACING)) as BlockState
var newState = block.defaultBlockState()
@Suppress("UNCHECKED_CAST")
for (property in state.properties) {
if (newState.hasProperty(property)) {
newState = newState.setValue(property as Property<Comparable<Any>>, state.getValue(property) as Comparable<Any>)
}
}
world.setBlockAndUpdate(pos, newState)
}
}
@@ -155,14 +164,4 @@ abstract class GrowableStoneBlock(
else -> upShape
}
}
companion object {
const val STAGE_0 = 0
const val STAGE_1 = 1
const val STAGE_2 = 2
const val STAGE_3 = 3
const val MAX_STAGE = STAGE_3
const val MIN_STAGE = STAGE_0
}
}
@@ -0,0 +1,191 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.entity.EggBlockEntity
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.AirItem
import net.minecraft.world.item.EggItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.PickaxeItem
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class IncubatorBlock(settings: Properties) : BaseEntityBlock(settings) {
companion object {
val FACING: DirectionProperty = DirectionProperty.create("facing", { dir -> dir.axis.isHorizontal })
val HAS_EGG: BooleanProperty = BooleanProperty.create("has_egg")
val CODEC: MapCodec<IncubatorBlock> = simpleCodec(::IncubatorBlock) as MapCodec<IncubatorBlock>
}
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, net.minecraft.core.Direction.NORTH)
.setValue(HAS_EGG, false)
)
}
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
return Shapes.or(
// Base
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0),
// Top
Shapes.box(2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0, 1.0, 14.0 / 16.0),
// Unified Glass Section (Covers all four glass sides as one box)
Shapes.box(2.5 / 16.0, 3.0 / 16.0, 2.5 / 16.0, 13.5 / 16.0, 14.0 / 16.0, 13.5 / 16.0)
)
}
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
}
override fun setPlacedBy(
level: Level,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
stack: ItemStack
) {
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
if (placer is Player) {
blockEntity?.ownerUUID = placer.uuid
}
super.setPlacedBy(level, pos, state, placer, stack)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING, HAS_EGG)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState): IncubatorBlockEntity {
return IncubatorBlockEntity(pos, state)
}
override fun useWithoutItem(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
// Ensure this logic only runs on the server
if (level.isClientSide) {
return InteractionResult.SUCCESS // Client-side only needs to show the hand animation
}
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL // Cast safely to ServerPlayer
val entity = level.getBlockEntity(blockPos) as? IncubatorBlockEntity ?: return InteractionResult.FAIL
val egg = entity.getItem(0).get(CobblemonItemComponents.EGG_DATA)?.egg
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
return InteractionResult.FAIL
}
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
}
// if the person interacting is not the owner of one of the parents
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
return InteractionResult.FAIL
}
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
}
override fun hasAnalogOutputSignal(state: BlockState): Boolean {
return true
}
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
return blockEntity?.getComparatorOutput() ?: 0
}
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
// Retrieve the block entity at the given position
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
// Check if the player is the owner of the nest
val isOwner = blockEntity.ownerUUID == player.uuid
// Check if there's an egg present in the nest
val hasEgg = !blockEntity.getItem(0).isEmpty
return if (!isOwner && hasEgg) {
// Reduce destroy speed by 80%, making it 5 times harder to break
super.getDestroyProgress(state, player, level, pos) * 0.2f
} else {
// Use the default destroy speed
super.getDestroyProgress(state, player, level, pos)
}
}
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val entity = level.getBlockEntity(pos) as? IncubatorBlockEntity
val itemStack = player.mainHandItem
entity?.let {
if (!player.isCreative) {
it.dropItem(level, pos, state)
if (itemStack.item is PickaxeItem) {
val itemEntity = ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.INCUBATOR.defaultInstance)
level.addFreshEntity(itemEntity)
}
}
}
return super.playerWillDestroy(level, pos, state, player)
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
override fun codec(): MapCodec<out BaseEntityBlock> {
return CODEC
}
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
return createTickerHelper(blockEntityType, CobblemonBlockEntities.INCUBATOR, IncubatorBlockEntity.TICKER)
}
}
@@ -0,0 +1,397 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
import com.cobblemon.mod.common.block.entity.NestBlockEntity
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.entity.player.Player
import net.minecraft.world.InteractionHand
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.StringRepresentable
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.PickaxeItem
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.SimpleWaterloggedBlock
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.material.Fluid
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class NestBlock(val variant: NestVariant, properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
init {
registerDefaultState(
stateDefinition.any()
.setValue(BlockStateProperties.WATERLOGGED, false)
)
}
enum class NestVariant(val id: String) : StringRepresentable {
STICK("nest_stick"),
LEAF("nest_leaf"),
STONE("nest_stone"),
DEEPSLATE("nest_deepslate"),
BASALT("nest_basalt"),
SNOW("nest_snow"),
NETHER("nest_nether"),
SOUL_SAND("nest_soul_sand"),
WARM_OCEAN("nest_warm_ocean"),
SEAGRASS("nest_seagrass"),
LUSH("nest_lush"),
HAY("nest_hay"),
SAND("nest_sand"),
RED_SAND("nest_red_sand"),
CHORUS("nest_chorus"),
CAVE("cave_nest"),
//NETHER("nether_nest"),
WATER("water_nest"),
BASIC("basic_nest");
override fun getSerializedName() = id
}
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
return when (variant) {
NestVariant.STICK -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base
Shapes.box(2.8 / 16.0, 0.0 / 16.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
Shapes.box(12.0 / 16.0, 3.0 / 16.0, 11.5 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 15.5 / 16.0), // Stick 1
Shapes.box(13.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 12.0 / 16.0), // Stick 2
Shapes.box(1.5 / 16.0, 2.0 / 16.0, 12.0 / 16.0, 4.5 / 16.0, 6.0 / 16.0, 12.0 / 16.0), // Stick 3
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 1.0 / 16.0, 9.0 / 16.0), // Stick 4
Shapes.box(4.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 11.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Stick 5
Shapes.box(9.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 6.0 / 16.0), // Stick 6
Shapes.box(6.0 / 16.0, 3.0 / 16.0, 0.5 / 16.0, 6.0 / 16.0, 6.0 / 16.0, 4.5 / 16.0) // Stick 7
)
NestVariant.LEAF -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base of the nest
Shapes.box(2.8 / 16.0, 0.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
Shapes.box(11.975 / 16.0, 6.0 / 16.0, 12.0 / 16.0, 11.975 / 16.0, 10.0 / 16.0, 15.0 / 16.0), // Leaf 1
Shapes.box(12.025 / 16.0, 5.525 / 16.0, 6.0 / 16.0, 16.025 / 16.0, 5.525 / 16.0, 9.0 / 16.0), // Leaf 2
Shapes.box(12.025 / 16.0, 8.0 / 16.0, 4.0 / 16.0, 12.025 / 16.0, 12.0 / 16.0, 7.0 / 16.0), // Leaf 3
Shapes.box(0.0 / 16.0, 5.0 / 16.0, 11.975 / 16.0, 3.0 / 16.0, 9.0 / 16.0, 11.975 / 16.0), // Leaf 4
Shapes.box(5.25 / 16.0, 7.75 / 16.0, 12.025 / 16.0, 9.25 / 16.0, 10.75 / 16.0, 12.025 / 16.0), // Leaf 5
Shapes.box(6.025 / 16.0, 10.0 / 16.0, 2.25 / 16.0, 6.025 / 16.0, 14.0 / 16.0, 5.25 / 16.0), // Leaf 6
Shapes.box(0.525 / 16.0, 8.0 / 16.0, 6.5 / 16.0, 3.525 / 16.0, 12.0 / 16.0, 6.5 / 16.0), // Leaf 7
Shapes.box(13.975 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 15.975 / 16.0, 3.0 / 16.0, 5.0 / 16.0) // Leaf 8
)
NestVariant.STONE -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
Shapes.box(10.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
Shapes.box(11.0 / 16.0, 0.0 / 16.0, 5.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 9.0 / 16.0),
Shapes.box(1.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 5.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0),
Shapes.box(5.0 / 16.0, -0.1 / 16.0, 11.0 / 16.0, 8.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0)
)
NestVariant.DEEPSLATE -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
Shapes.box(2.0 / 16.0, 0.0 / 16.0, 2.0 / 16.0, 6.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
Shapes.box(1.0 / 16.0, -0.05 / 16.0, 5.0 / 16.0, 5.0 / 16.0, 5.95 / 16.0, 9.0 / 16.0),
Shapes.box(11.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 15.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
Shapes.box(10.0 / 16.0, -0.1 / 16.0, 14.0 / 16.0, 11.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
Shapes.box(8.0 / 16.0, -0.125 / 16.0, 11.0 / 16.0, 11.0 / 16.0, 6.875 / 16.0, 14.0 / 16.0),
Shapes.box(8.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 5.0 / 16.0)
)
NestVariant.BASALT -> Shapes.or(
Shapes.box(4.0 / 16.0, -0.1 / 16.0, 4.0 / 16.0, 12.0 / 16.0, 1.9 / 16.0, 12.0 / 16.0),
Shapes.box(10.55 / 16.0, 0.0 / 16.0, 5.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 10.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 12.95 / 16.0),
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 9.95 / 16.0, 4.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 9.95 / 16.0, 13.55 / 16.0, 8.8 / 16.0, 13.95 / 16.0),
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 0.95 / 16.0, 13.55 / 16.0, 3.8 / 16.0, 4.95 / 16.0),
Shapes.box(0.55 / 16.0, -0.2 / 16.0, 5.95 / 16.0, 4.55 / 16.0, 5.8 / 16.0, 9.95 / 16.0),
Shapes.box(1.55 / 16.0, -0.2 / 16.0, 2.95 / 16.0, 4.55 / 16.0, 8.8 / 16.0, 5.95 / 16.0),
Shapes.box(3.45 / 16.0, -0.2 / 16.0, 11.05 / 16.0, 6.45 / 16.0, 11.8 / 16.0, 14.05 / 16.0),
Shapes.box(11.55 / 16.0, -0.1 / 16.0, 4.95 / 16.0, 14.55 / 16.0, 5.9 / 16.0, 7.95 / 16.0)
)
NestVariant.SNOW -> Shapes.or(
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 4.0 / 16.0, 11.0 / 16.0),
Shapes.box(3.0 / 16.0, 1.1 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 6.1 / 16.0, 13.0 / 16.0),
Shapes.box(5.0 / 16.0, 1.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0),
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 8.0 / 16.0, 5.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
Shapes.box(5.0 / 16.0, 5.0 / 16.0, 11.0 / 16.0, 7.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
Shapes.box(0.25 / 16.0, 7.6 / 16.0, 10.0 / 16.0, 5.25 / 16.0, 14.6 / 16.0, 10.0 / 16.0),
Shapes.box(13.5 / 16.0, 4.6 / 16.0, 6.0 / 16.0, 16.5 / 16.0, 8.6 / 16.0, 6.0 / 16.0),
Shapes.box(11.0 / 16.0, 4.6 / 16.0, 12.75 / 16.0, 11.0 / 16.0, 10.6 / 16.0, 16.75 / 16.0),
Shapes.box(0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 16.0 / 16.0)
)
NestVariant.CAVE -> Shapes.or(
Shapes.box(0.25, 0.0, 0.25, 0.75, 0.375, 0.75),
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 0.875)
)
NestVariant.NETHER -> Shapes.or(
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0), // Main nest base
Shapes.box(0.0 / 16.0, 0.0, 6.0 / 16.0, 4.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0), // Left extension
Shapes.box(7.0 / 16.0, 0.0, 0.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0), // Front extension
Shapes.box(11.0 / 16.0, 0.0, 11.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0) // Back right extension
)
NestVariant.WATER -> Shapes.box(0.2, 0.0, 0.2, 0.8, 0.3, 0.8)
NestVariant.BASIC -> Shapes.box(0.1, 0.0, 0.1, 0.9, 0.45, 0.9)
NestVariant.SOUL_SAND -> Shapes.or(
Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Base layer
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Inner rim
Shapes.box(12.0 / 16.0, 0.0 / 16.0, 10.0 / 16.0, 16.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0), // Side bump 1
Shapes.box(3.0 / 16.0, 0.0 / 16.0, 13.0 / 16.0, 6.0 / 16.0, 7.0 / 16.0, 16.0 / 16.0), // Side bump 2
Shapes.box(1.0 / 16.0, 2.0 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 6.0 / 16.0, 6.0 / 16.0) // Side bump 3
)
NestVariant.WARM_OCEAN -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0)
NestVariant.SEAGRASS -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0), // Main base
Shapes.box(1.0 / 16.0, 0.0, 7.0 / 16.0, 5.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Side protrusion 1
Shapes.box(5.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 9.0 / 16.0), // Side protrusion 2
Shapes.box(6.0 / 16.0, 0.0, 13.0 / 16.0, 16.0 / 16.0, 6.0 / 16.0, 14.0 / 16.0) // Backside bump
)
NestVariant.LUSH -> Shapes.or(
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 12.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Main base
Shapes.box(0.0 / 16.0, 0.0, 0.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 12.0 / 16.0), // Side extension 1
Shapes.box(0.0 / 16.0, 0.0, 12.0 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 16.0 / 16.0), // Side extension 2
Shapes.box(12.0 / 16.0, 0.0, 8.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0), // Side extension 3
Shapes.box(12.0 / 16.0, 0.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 8.0 / 16.0) // Side extension 4
)
NestVariant.HAY -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0)
NestVariant.SAND -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
)
NestVariant.RED_SAND -> Shapes.or(
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
)
NestVariant.CHORUS -> Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 4.0 / 16.0, 15.0 / 16.0)
}
}
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, isMoving: Boolean) {
super.onPlace(state, level, pos, oldState, isMoving)
checkAndBreakIfSubmerged(state, level, pos)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(BlockStateProperties.WATERLOGGED)
}
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
checkAndBreakIfSubmerged(state, level, pos)
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
}
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
block: Block,
fromPos: BlockPos,
isMoving: Boolean
) {
super.neighborChanged(state, level, pos, block, fromPos, isMoving)
// Check if the block below is air
val blockBelowPos = pos.below()
if (fromPos == blockBelowPos && level.getBlockState(blockBelowPos).isAir) {
level.destroyBlock(pos, false)
}
checkAndBreakIfSubmerged(state, level, pos)
}
private fun checkAndBreakIfSubmerged(state: BlockState, levelAccessor: LevelAccessor, pos: BlockPos) {
val fluidState = levelAccessor.getFluidState(pos)
if ((fluidState.type == Fluids.WATER || fluidState.isSource) && !canBeWaterlogged()) {
if (levelAccessor is Level) {
levelAccessor.destroyBlock(pos, true)
}
}
}
private fun canBeWaterlogged(): Boolean {
return variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN
}
override fun useWithoutItem(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
// Ensure this logic only runs on the server
if (level.isClientSide) {
return InteractionResult.SUCCESS
}
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL
val entity = level.getBlockEntity(blockPos) as? NestBlockEntity ?: return InteractionResult.FAIL
val egg = entity.getItem().get(CobblemonItemComponents.EGG_DATA)?.egg
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
return InteractionResult.FAIL
}
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
}
// if the person interacting is not the owner of one of the parents
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
return InteractionResult.FAIL
}
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
}
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
// Retrieve the block entity at the given position
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
// Check if the player is the owner of the nest
val isOwner = blockEntity.ownerUUID == player.uuid
// Check if there's an egg present in the nest
val hasEgg = !blockEntity.getItem(0).isEmpty
return if (!isOwner && hasEgg) {
// Reduce destroy speed making it 5 times harder to break
super.getDestroyProgress(state, player, level, pos) * 0.2f
} else {
super.getDestroyProgress(state, player, level, pos)
}
}
// todo we want destroying to take longer if it is not their nest
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val entity = level.getBlockEntity(pos) as? NestBlockEntity
// todo we do silk touch now
/*entity?.let {
if (!player.isCreative) {
val itemEntity = when (this.variant) {
NestVariant.BASIC -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.BASE_NEST.defaultInstance)
NestVariant.WATER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.WATER_NEST.defaultInstance)
NestVariant.CAVE -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.CAVE_NEST.defaultInstance)
NestVariant.NETHER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.NETHER_NEST.defaultInstance)
}
level.addFreshEntity(itemEntity)
}
it.dropItem( level, pos, state)
}*/
return super.playerWillDestroy(level, pos, state, player)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = NestBlockEntity(pos, state)
override fun getRenderShape(blockState: BlockState) = RenderShape.MODEL
override fun canPlaceLiquid(
player: Player?,
level: BlockGetter,
pos: BlockPos,
state: BlockState,
fluid: Fluid
): Boolean {
return (variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN) && super.canPlaceLiquid(player, level, pos, state, fluid)
}
override fun getFluidState(state: BlockState): FluidState {
return if (state.getValue(BlockStateProperties.WATERLOGGED)) {
Fluids.WATER.getSource(false)
} else super.getFluidState(state)
}
override fun setPlacedBy(
level: Level,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
stack: ItemStack
) {
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity
if (placer is Player) {
blockEntity?.ownerUUID = placer.uuid
}
super.setPlacedBy(level, pos, state, placer, stack)
}
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
val fluidState = ctx.level.getFluidState(ctx.clickedPos)
// If the nest variant can be waterlogged, set it accordingly
if (canBeWaterlogged()) {
return defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, fluidState.type == Fluids.WATER)
}
// Prevent placement if the block is submerged in water
if (!canBeWaterlogged() && fluidState.type == Fluids.WATER) {
return null
}
return defaultBlockState()
}
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
return createTickerHelper(blockEntityType, CobblemonBlockEntities.NEST, NestBlockEntity.TICKER)
}
override fun codec(): MapCodec<NestBlock> {
return RecordCodecBuilder.mapCodec { instance ->
instance.group(
Codec.STRING.fieldOf("variant").forGetter { block -> block.variant.id }, // Serializes the variant
Codec.BOOL.optionalFieldOf("waterlogged", false).forGetter { block ->
block.defaultBlockState().getValue(BlockStateProperties.WATERLOGGED)
}
).apply(instance) { variant, waterlogged ->
val block = NestBlock(NestVariant.valueOf(variant.uppercase()), Properties.of().noOcclusion())
block.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, waterlogged)
block
}
}
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape
class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
companion object {
val CODEC: MapCodec<TMShelfBlock> = simpleCodec(::TMShelfBlock)
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
val SLOT_OCCUPIED_PROPERTIES = (0 until 14).map {
BooleanProperty.create("slot_${it}_occupied")
}
}
init {
val baseState = stateDefinition.any().setValue(FACING, Direction.NORTH)
registerDefaultState(SLOT_OCCUPIED_PROPERTIES.fold(baseState) { state, prop ->
state.setValue(prop, false)
})
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return TMShelfBlockEntity(pos, state)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
SLOT_OCCUPIED_PROPERTIES.forEach { builder.add(it) }
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState {
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
}
override fun useWithoutItem(
state: BlockState, level: Level, pos: BlockPos,
player: Player, hit: BlockHitResult
): InteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return InteractionResult.PASS
return entity.handleUseWithoutItem(state, level, pos, player, hit)
}
override fun useItemOn(
stack: ItemStack, state: BlockState, level: Level, pos: BlockPos,
player: Player, hand: InteractionHand, hit: BlockHitResult
): ItemInteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
return entity.handleUseItem(stack, state, level, pos, player, hit)
}
override fun hasAnalogOutputSignal(state: BlockState) = true
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
return entity?.lastInteractedSlot?.plus(1) ?: 0
}
override fun getShape(
state: BlockState, level: BlockGetter,
pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
): VoxelShape = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
}
@@ -44,15 +44,25 @@ class TumblestoneBlock(
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
).apply(it, ::TumblestoneBlock) }
const val STAGE_0 = 0
const val STAGE_1 = 1
const val STAGE_2 = 2
const val STAGE_3 = 3
const val MAX_STAGE = STAGE_3
const val MIN_STAGE = STAGE_0
}
override val growthChance = 5
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.DOWN)
.setValue(WATERLOGGED, false))
}
override fun canGrow(pos: BlockPos, world: BlockGetter): Boolean {
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
if (stage == MAX_STAGE) return false
val iterator: Iterator<BlockPos> =
BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))
@@ -66,6 +76,8 @@ class TumblestoneBlock(
return true
}
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
override fun codec(): MapCodec<out DirectionalBlock> {
return CODEC
}
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
class TypeGemBlock(
settings: Properties,
stage: Int,
height: Int,
xzOffset: Int,
nextStage: Block?
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
companion object {
val CODEC: MapCodec<TypeGemBlock> = RecordCodecBuilder.mapCodec { it.group(
propertiesCodec(),
PrimitiveCodec.INT.fieldOf("stage").forGetter { it.stage },
PrimitiveCodec.INT.fieldOf("height").forGetter { it.height },
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
).apply(it, ::TypeGemBlock) }
const val STAGE_0 = 0
const val STAGE_1 = 1
const val MAX_STAGE = STAGE_1
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
val FACING: DirectionProperty = DirectionalBlock.FACING
}
override val growthChance = 1
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.DOWN) // Ensure facing is properly registered
.setValue(SHOULD_GROW, true)
)
}
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
}
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
if (canGrow(state, pos, level)) {
var nextState = nextStage?.defaultBlockState()
if (nextState != null) {
if (nextState.hasProperty(FACING)) {
nextState = nextState.setValue(FACING, state.getValue(FACING)) // Preserve FACING for core
}
level.setBlockAndUpdate(pos, nextState)
}
}
}
override fun codec(): MapCodec<out DirectionalBlock?>? = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(FACING, SHOULD_GROW) // Ensure FACING is registered
}
}

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