Compare commits

...
Author SHA1 Message Date
tmetcalfe89 8621315fc9 merged main in 2024-05-28 22:49:27 -07:00
tmetcalfe89 5a37693101 you weren't supposed to be in there 2024-05-24 14:41:18 -07:00
tmetcalfe89 6c7cd05f10 updated baits 2024-05-24 14:36:24 -07:00
tmetcalfe89 79627395c2 multiple ivs are now possible 2024-05-24 14:28:42 -07:00
tmetcalfe89 280bcb5bf3 semantics and cleanup 2024-05-24 14:21:21 -07:00
tmetcalfe89 b2bc425fc9 final cleanup 2024-05-24 14:11:59 -07:00
tmetcalfe89 a025d852af a little cleanup for the new format 2024-05-24 13:58:09 -07:00
tmetcalfe89 20076d0d73 moved bait effects to obj vs arr 2024-05-24 12:54:08 -07:00
tmetcalfe89 290a549911 housekeeping 2024-05-24 12:23:07 -07:00
tmetcalfe89 c878fbdade timnote 2024-05-24 12:23:00 -07:00
tmetcalfe89 f9217ac284 readability 2024-05-24 12:22:49 -07:00
tmetcalfe89 cf88c88d1d typo 2024-05-24 11:58:27 -07:00
tmetcalfe89 21df4a71ec licenses 2024-05-23 23:54:32 -07:00
tmetcalfe89 34cb074fa8 moved things to influences 2024-05-23 23:19:41 -07:00
tmetcalfe89 c9639c681c a little cleanup 2024-05-23 18:20:28 -07:00
tmetcalfe89 58f8edb4a2 refactored to use a single influence 2024-05-23 18:19:03 -07:00
tmetcalfe89 4e2d6abbee removed old shiny logic 2024-05-23 17:41:49 -07:00
tmetcalfe89 4b492fca2e shiny bait 2024-05-23 17:38:00 -07:00
tmetcalfe89 92e32b3857 start the move toward influences 2024-05-23 17:25:41 -07:00
tmetcalfe89 d68f62a808 moved nocatch to lang 2024-05-23 16:20:38 -07:00
tmetcalfe89 b8a6bb2650 influence initialization area 2024-05-23 15:46:04 -07:00
tmetcalfe89 55c9c9aa9c it's a surprise tool that will help us later 2024-05-23 15:39:45 -07:00
tmetcalfe89 bec467f32e no reason to be that specific 2024-05-23 15:31:27 -07:00
tmetcalfe89 0f794690ea ocd 2024-05-23 15:27:29 -07:00
tmetcalfe89 cb27ae8449 a little simplification by using Kotlin stuff 2024-05-23 14:43:01 -07:00
tmetcalfe89 21033f6527 exception cases simplify tests 2024-05-23 14:40:26 -07:00
tmetcalfe89 e95159f9f9 no nibble? bye 2024-05-23 14:35:05 -07:00
Paul 7ebe0551e1 Fixed build issue caused by unnecessary AW 2024-05-11 18:54:29 +10:00
Paul 6e1d4e0c4d Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2 2024-05-11 18:32:10 +10:00
Paul 7aef56c610 license 2024-04-27 14:28:30 +10:00
Paul 27e1a34097 Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/resources/assets/cobblemon/textures/item/poke_balls/beast_ball.png
2024-04-27 12:43:42 +10:00
Paul b25dc6610e Fixed bobber spawning not working 2024-04-22 11:20:54 +10:00
Paul fa67bba631 Merge remote-tracking branch 'origin/main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/advancement/CobblemonCriteria.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/DisplayCaseRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/MagikarpModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/resources/assets/cobblemon/bedrock/pokemon/animations/0129_magikarp/magikarp.animation.json
2024-04-22 11:07:52 +10:00
Lily e6590bef08 moved most baits to the new system 2024-03-19 15:00:59 +00:00
Lily 60b0e70651 updated some baits to use the new system 2024-03-19 14:40:17 +00:00
Lily 0d9f8bbb36 refactored the fishing bait system to allow for multiple effects per item 2024-03-18 22:24:24 -04:00
Sterrezo e75dcd63d1 Merge branch 'sega_bass_pro_fishing_2' of https://gitlab.com/cable-mc/cobblemon into sega_bass_pro_fishing_2 2024-03-04 17:46:49 +01:00
Sterrezo a4fc1442bf Updated fishing sounds 2024-03-04 17:46:06 +01:00
Plastered_Crab fb85ceacb0 - More adjustments to the reel-in trajectory 2024-03-01 12:40:14 -08:00
jeffw773 7d9644ba51 - tweak Gyarados spawn for testing 2024-02-29 08:58:05 -08:00
Plastered_Crab 39c856b69a - Added in all Ancient Rod predicates so they look right when casting 2024-02-28 15:49:56 -08:00
Plastered_Crab 4a4c375f66 - Added in new test sounds for fishing 2024-02-28 14:56:28 -08:00
Plastered_Crab d22df3e554 - tweaked reel in logic and trajectory 2024-02-27 16:36:09 -08:00
Plastered_Crab ed4dadc3f6 - cleaned up code 2024-02-26 16:21:44 -08:00
Plastered_Crab c2e5e39019 - Tweaked IV bait logic 2024-02-26 16:11:35 -08:00
Plastered_Crab f383e5a52f - Fixed some bait logic 2024-02-26 15:33:28 -08:00
Plastered_Crab 914b7c63c9 - Hooking entities no longer crashes the game :D 2024-02-26 14:37:57 -08:00
Plastered_Crab 5a17a5ed68 - Glow/Sweet Berries can be used as bait + fixes for particles for fishing trails 2024-02-26 12:04:54 -08:00
jeffw773 bb1a46950f - added comment to bobber entity 2024-02-26 06:27:39 -08:00
jeffw773 679d3eae58 - edit to vanilla bait list 2024-02-26 06:08:04 -08:00
jeffw773 a9cd80e496 - remove junk test fishing loot table. Finalize % chance to get Smithing Template odds 2024-02-26 05:38:50 -08:00
apion a96cc307bb Fixed loot table injector 2024-02-25 23:39:06 -05:00
jeffw773 f2b2a282e5 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-25 19:56:20 -08:00
jeffw773 5bd7695ce8 - players can define their own bait now 2024-02-25 19:56:05 -08:00
Lily b42387a23f Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-25 17:40:43 -05:00
Lily 6aa1e6f9ad advancement + tag changes 2024-02-25 17:39:55 -05:00
jeffw773 414fcf8278 - Removed additional crafting recipe 2024-02-24 21:27:26 -08:00
jeffw773 a3c26484a4 - Inejected Pokerod Smithing Template into Treasure Fishing table 2024-02-24 21:25:06 -08:00
Lily fbc8883c96 advancement fixes, removed crafting table recipes, fixed fishing bait not registering 2024-02-23 23:57:50 -05:00
jeffw773 baba3f669d Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-23 17:45:38 -08:00
jeffw773 2fe098adac - added in recipes for ancient rods 2024-02-23 17:45:23 -08:00
Lily 1c770049b4 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/PokerodItem.kt
2024-02-23 20:21:53 -05:00
Lily 71b854f36a ancient rods + advancements 2024-02-23 20:21:26 -05:00
jeffw773 61b944a293 - reworked fishing baits to be datapackable (now you can fish with steak if you wanted) 2024-02-23 16:16:48 -08:00
jeffw773 46ed6d0492 - more bait swapping logic 2024-02-23 08:53:46 -08:00
jeffw773 72058bb56f - Fixed some bait swapping logic 2024-02-22 23:57:05 -08:00
jeffw773 a7b28ed936 - Added some wip fishing sounds 2024-02-22 23:19:25 -08:00
jeffw773 96fce55688 - added some additional fishing spawn pool pokemon 2024-02-22 22:15:27 -08:00
Lily 85daf496d8 changes to pokerod item lang 2024-02-22 23:16:09 -05:00
Plastered_Crab 7b4b498877 - All but 6 berries are implemented for bait (save me) 2024-02-22 15:52:17 -08:00
Plastered_Crab 0ee32c7fd3 - Most of the baits work now (I am so tired) 2024-02-22 14:44:47 -08:00
Plastered_Crab 2c2c1b0cf4 - More groundwork for bait 2024-02-22 09:30:46 -08:00
jeffw773 218edf680b - Ground work for baits having effects on fishing 2024-02-21 21:35:21 -08:00
Plastered_Crab 7598a0dacc - Updated assets for rods 2024-02-21 18:33:25 -08:00
Plastered_Crab 7327a4ddb5 - Bait can be removed and swapped out on the bobber 2024-02-21 11:44:21 -08:00
Plastered_Crab f1d3e7b12b - Bait can now be applied to the fishing bobbers 2024-02-21 11:09:53 -08:00
jeffw773 410ea6be87 - Tweaked line colors and started on bait testing 2024-02-20 23:08:48 -08:00
Plastered_Crab 399dfc33c5 - Added Beast Rod (blue string variant) 2024-02-20 17:05:20 -08:00
Plastered_Crab 26e39bb37d - Altered the trajectory of reeled in pokemon 2024-02-20 15:17:11 -08:00
Plastered_Crab 4ff7df63c0 - Fixed colored fishing lines for cosmetic rods and fixed line not breaking when swapping to another rod 2024-02-20 14:45:39 -08:00
jeffw773 1d1f749d6d - Added cast predicates and JSON features to each remaining cosmetic rod 2024-02-20 01:36:14 -08:00
Plastered_Crab d6e4b9c1a6 - Added other rods to the CobblemonItems list 2024-02-19 17:19:56 -08:00
apion 15d3ee0d49 Data-packable fishing rods kind of 2024-02-19 19:56:22 -05:00
Plastered_Crab de0ed1ab41 - Added crafting table recipes for cosmetic rods 2024-02-19 15:12:47 -08:00
Lily 81d930afae poke rod sync stuff (not fully working atm) 2024-02-19 15:37:30 -05:00
Plastered_Crab b83d75f30f - More Attempts at setting ball used for bobber and line color for rod during registry 2024-02-19 11:42:04 -08:00
Plastered_Crab 5814c1a5d6 - Attempts to get each ball rendered correctly for each rod (still needs work) 2024-02-19 10:10:57 -08:00
jeffw773 a45fec50a4 - Added fancy cosmetic Rods for each pokeball type 2024-02-18 22:51:05 -08:00
jeffw773 576b6ea767 - implemented test Magikarp animations + added test hook to Bobber 2024-02-18 18:05:16 -08:00
jeffw773 792287cb5a - added new Magikarp models/textures/animations 2024-02-18 14:44:11 -08:00
Lily 42ad084dbe pokerod assets 2024-02-18 16:24:20 -05:00
jeffw773 b9bd61f3dc - fishing line connects to bobber much better now 2024-02-18 01:04:41 -08:00
jeffw773 c6d6f98d03 - fishing line connects to bobber much better now 2024-02-18 01:03:28 -08:00
jeffw773 abbd17faf7 - Pokeball Bobber now casts with more controlled random pitch/yaw amounts and also rotates upright when it hits water 2024-02-18 00:29:21 -08:00
jeffw773 c159137a70 Merge remote-tracking branch 'origin/display_case' into sega_bass_pro_fishing_2 2024-02-17 22:54:08 -08:00
jeffw773 03628631f7 - Pokeball bobber now rotates upwards when bobbing in water 2024-02-17 11:50:37 -08:00
jeffw773 e5e61df4b2 Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/entity/PokeBobberEntityRenderer.kt
2024-02-17 10:31:26 -08:00
Lily f07c4bf5b1 refactored fishing bobber variable names to be more readable instead of the single-letter names that mc gives us 2024-02-17 11:31:38 -05:00
Lily 0e3fd520f0 fixed naming to be more consistent with vanilla textures 2024-02-17 11:23:08 -05:00
jeffw773 935f3100d2 - Pokeball Bobber now slows down fast in water (due to water friction) and has a random spin applied when casting 2024-02-16 22:12:07 -08:00
Lily 8b32764ce3 pokerod smithing template assets 2024-02-17 00:14:26 -05:00
Lily 3e5a57ea20 pokerod now slowly stops spinning 2024-02-16 23:35:34 -05:00
Plastered_Crab 6ee94aa55b - Pokeball Bobber now spins when cast and stops spinning on land, needs fixed for water surface 2024-02-16 17:01:07 -08:00
Lily bd5185e69e Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-16 19:31:40 -05:00
Lily 5e8df4609b started bobber rendering 2024-02-16 19:31:21 -05:00
Plastered_Crab 289d1474a6 - Beeeeg Whiscash has been added to fishing spawn chances 2024-02-16 14:54:52 -08:00
Plastered_Crab e4604c0d13 - Wooper (Heart variant) is now fully implemented for fishing 2024-02-16 14:24:31 -08:00
Tyzillion d4f91fa945 Wooper Heart Fix. <3 2024-02-16 16:53:53 -05:00
Plastered_Crab 5c52a9a31a - Started Implemented Wooper (heart form) to be added to the fishing chance pool 2024-02-16 13:19:57 -08:00
Plastered_Crab db2efcb48e - added new spawn conditional for lureLevel. Variants of Karp can now only have a chance to appear at certain Lure levels 2024-02-16 10:32:55 -08:00
Plastered_Crab 3bcbe2fb45 - Incorporated Pokerod Treasure loot table and player will physically reel in pokemon under 200 lbs 2024-02-16 09:32:01 -08:00
Paul c7202cc869 Fixed magikarp jump pattern spawn file 2024-02-16 11:34:40 +10:00
Lily 269e18786b Merge remote-tracking branch 'origin/sega_bass_pro_fishing_2' into sega_bass_pro_fishing_2 2024-02-15 19:40:14 -05:00
Lily bd79a4ecfb pokerod smithing template (needs some more assets but should be functional) 2024-02-15 19:39:49 -05:00
Plastered_Crab 3c3a8b1af9 - Pokerod now cannot stack and has same durability as vanilla fishing rod 2024-02-15 16:07:54 -08:00
Lily 11e2ed34e5 Merge branch 'main' into sega_bass_pro_fishing_2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/client/CobblemonFabricClient.kt
#	forge/src/main/kotlin/com/cobblemon/mod/forge/client/CobblemonForgeClient.kt
2024-02-15 18:54:42 -05:00
Plastered_Crab 1500d6cdf1 - All Magikarp Jump variants implemented and new item loot tables started 2024-02-15 14:59:18 -08:00
Plastered_Crab acbbe2a1ba - Start to JUMP variants spawning properly when fishing 2024-02-14 16:42:58 -08:00
Plastered_Crab 0fe6bc918c - Adding Magikarp JUMP addon variants into Base mod 2024-02-14 14:57:27 -08:00
Plastered_Crab d399b3e29a - Magikarps now get reeled in at good distances in front of player 2024-02-14 14:20:32 -08:00
Plastered_Crab acc6f119e1 - Magikarps getting reeled in now land in front of the player instead of behind the player (for battle reasons) 2024-02-13 16:45:22 -08:00
Plastered_Crab 932a2c375c - Begin different reel-in trajectories for fishing up Magikarps 2024-02-13 14:46:42 -08:00
Nick 142eebe9e6 Apply model predicates, starting with Pokerod 2024-02-09 22:07:43 -08:00
jeffw773 36f19c9180 - Xp is rewarded to player for fishing up Pokemon 2024-02-09 21:15:10 -08:00
jeffw773 beb8fed6bd - Pokeball Bobber now renders correctly when cast out with Pokerod
- Luck of the Sea and Lure get read correctly from the rod and factor into the spawn bucket chosen
2024-02-09 14:17:13 -08:00
jeffw773 142284a17c - Pokemon rarity is altered by Luck of the Sea 2024-02-08 21:18:41 -08:00
jeffw773 f1ea86362a - Fixed fishing spawns being able to check if sky is actually visible.
- added isOcean to biomes to Magikarp
2024-02-07 18:28:42 -08:00
Paul ec3361ad91 Added basic fishing spawn detail for Magikarp and use the world pool for fishing because ehhh 2024-02-03 12:16:53 +10:00
Paul ca7c36ce15 Added trigger spawning with a fishing implementation 2024-02-03 12:13:38 +10:00
jeffw773 f7c73728d7 - Added some newer spawning stuff and added battle initiation after fished up mon 2024-02-02 16:38:15 -08:00
jeffw773 1992a23edc Merge branch 'main' into sega_bass_pro_fishing_2 2024-02-02 13:39:02 -08:00
jeffw773 dad28135cc - Brief changes to particle count based on rarity reeled in 2024-01-18 14:32:43 -08:00
jeffw773 e41d13dd53 - Pokerod now uses config values for rarity buckets and access biome spawn pools 2024-01-14 23:02:33 -08:00
jeffw773 64cae53d82 - Pokerod foundation laid out for having chance to spawn Pokemon instead of getting item as well as rarity chance started 2024-01-14 00:11:12 -08:00
dhi_holo c9bf0bd8d7 Merge branch 'sega_bass_pro_fishing_2' of https://gitlab.com/cable-mc/cobblemon into sega_bass_pro_fishing_2 2024-01-14 13:36:53 +10:00
dhi_holo 97d2047aee It's a Tool, It's a Rod
makes pokerod be held correctly, and adds second model file for the cast variation, when that is implemented.
2024-01-14 13:36:04 +10:00
jeffw773 811f8abed7 - Added override for model of pokerod when cast 2024-01-13 19:27:22 -08:00
jeffw773 ab02a54f28 - Finally the Pokerod is starting to work! (it is backwards tho) 2024-01-13 19:08:36 -08:00
jeffw773 6e0c1aefcc - More testing fishing code for pokerod 2024-01-13 13:55:02 -08:00
jeffw773 e887b6c43c - Some code for fishing testing again 2024-01-13 12:16:38 -08:00
jeffw773 25172c2bc9 - Some code for fishing testing 2024-01-13 11:23:38 -08:00
jeffw773 929b90173a - Some code for fishing testing 2024-01-12 11:58:02 -08:00
jeffw773 70a65f6ffe - Testing code 2024-01-12 07:39:21 -08:00
jeffw773 db7d3f01d8 - Starting implementation on fishing 2024-01-12 07:39:20 -08:00
457 changed files with 6385 additions and 22 deletions
@@ -49,8 +49,13 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.WorldSlice
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -104,13 +109,7 @@ import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -125,6 +124,7 @@ import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -66,6 +67,15 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(CHEST_BOAT_KEY.toString())
)
@JvmField
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
@JvmField
val POKE_BOBBER: EntityType<PokeRodFishingBobberEntity> = this.create(
POKE_BOBBER_KEY.path,
EntityType.Builder.create(::PokeRodFishingBobberEntity, SpawnGroup.MISC).setDimensions(0.5F, 0.5F).maxTrackingRange(10)
.build(POKE_BOBBER_KEY.toString())
)
@JvmField
val GENERIC_BEDROCK_ENTITY_KEY = cobblemonResource("generic_bedrock")
@JvmField
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
@@ -45,6 +47,7 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.world.World
@@ -932,12 +935,121 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
Text.translatable("item.minecraft.fishing_rod").blue(),
Text.translatable("item.cobblemon.smithing_template.pokerod.ingredients").blue(),
Text.translatable("upgrade.cobblemon.pokerod").gray(),
Text.translatable("item.cobblemon.smithing_template.pokerod.base_slot_description"),
Text.translatable("item.cobblemon.smithing_template.pokerod.additions_slot_description"),
listOf(cobblemonResource("item/empty_slot_fishing_rod")),
listOf(cobblemonResource("item/empty_slot_pokeball"))
))
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
)
val pokeRods = mutableListOf<PokerodItem>()
@JvmField
val POKE_ROD = pokerodItem(cobblemonResource("poke_rod"))
@JvmField
val CITRINE_ROD = pokerodItem(cobblemonResource("citrine_rod"))
@JvmField
val VERDANT_ROD = pokerodItem(cobblemonResource("verdant_rod"))
@JvmField
val AZURE_ROD = pokerodItem(cobblemonResource("azure_rod"))
@JvmField
val ROSEATE_ROD = pokerodItem(cobblemonResource("roseate_rod"))
@JvmField
val SLATE_ROD = pokerodItem(cobblemonResource("slate_rod"))
@JvmField
val PREMIER_ROD = pokerodItem(cobblemonResource("premier_rod"))
@JvmField
val GREAT_ROD = pokerodItem(cobblemonResource("great_rod"))
@JvmField
val ULTRA_ROD = pokerodItem(cobblemonResource("ultra_rod"))
@JvmField
val SAFARI_ROD = pokerodItem(cobblemonResource("safari_rod"))
@JvmField
val FAST_ROD = pokerodItem(cobblemonResource("fast_rod"))
@JvmField
val LEVEL_ROD = pokerodItem(cobblemonResource("level_rod"))
@JvmField
val LURE_ROD = pokerodItem(cobblemonResource("lure_rod"))
@JvmField
val HEAVY_ROD = pokerodItem(cobblemonResource("heavy_rod"))
@JvmField
val LOVE_ROD = pokerodItem(cobblemonResource("love_rod"))
@JvmField
val FRIEND_ROD = pokerodItem(cobblemonResource("friend_rod"))
@JvmField
val MOON_ROD = pokerodItem(cobblemonResource("moon_rod"))
@JvmField
val SPORT_ROD = pokerodItem(cobblemonResource("sport_rod"))
@JvmField
val PARK_ROD = pokerodItem(cobblemonResource("park_rod"))
@JvmField
val NET_ROD = pokerodItem(cobblemonResource("net_rod"))
@JvmField
val DIVE_ROD = pokerodItem(cobblemonResource("dive_rod"))
@JvmField
val NEST_ROD = pokerodItem(cobblemonResource("nest_rod"))
@JvmField
val REPEAT_ROD = pokerodItem(cobblemonResource("repeat_rod"))
@JvmField
val TIMER_ROD = pokerodItem(cobblemonResource("timer_rod"))
@JvmField
val LUXURY_ROD = pokerodItem(cobblemonResource("luxury_rod"))
@JvmField
val DUSK_ROD = pokerodItem(cobblemonResource("dusk_rod"))
@JvmField
val HEAL_ROD = pokerodItem(cobblemonResource("heal_rod"))
@JvmField
val QUICK_ROD = pokerodItem(cobblemonResource("quick_rod"))
@JvmField
val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
@JvmField
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"))
@JvmField
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"))
@JvmField
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"))
@JvmField
val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
@JvmField
val ANCIENT_CITRINE_ROD = pokerodItem(cobblemonResource("ancient_citrine_rod"))
@JvmField
val ANCIENT_VERDANT_ROD = pokerodItem(cobblemonResource("ancient_verdant_rod"))
@JvmField
val ANCIENT_AZURE_ROD = pokerodItem(cobblemonResource("ancient_azure_rod"))
@JvmField
val ANCIENT_ROSEATE_ROD = pokerodItem(cobblemonResource("ancient_roseate_rod"))
@JvmField
val ANCIENT_SLATE_ROD = pokerodItem(cobblemonResource("ancient_slate_rod"))
@JvmField
val ANCIENT_IVORY_ROD = pokerodItem(cobblemonResource("ancient_ivory_rod"))
@JvmField
val ANCIENT_GREAT_ROD = pokerodItem(cobblemonResource("ancient_great_rod"))
@JvmField
val ANCIENT_ULTRA_ROD = pokerodItem(cobblemonResource("ancient_ultra_rod"))
@JvmField
val ANCIENT_FEATHER_ROD = pokerodItem(cobblemonResource("ancient_feather_rod"))
@JvmField
val ANCIENT_WING_ROD = pokerodItem(cobblemonResource("ancient_wing_rod"))
@JvmField
val ANCIENT_JET_ROD = pokerodItem(cobblemonResource("ancient_jet_rod"))
@JvmField
val ANCIENT_HEAVY_ROD = pokerodItem(cobblemonResource("ancient_heavy_rod"))
@JvmField
val ANCIENT_LEADEN_ROD = pokerodItem(cobblemonResource("ancient_leaden_rod"))
@JvmField
val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
@JvmField
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"))
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@@ -1003,6 +1115,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
private fun pokerodItem(pokeRodId: Identifier): PokerodItem {
val settings = Item.Settings().maxCount(1).maxDamage(Items.FISHING_ROD.maxDamage)
val item = create(pokeRodId.path, PokerodItem(pokeRodId, settings))
pokeRods.add(item)
return item
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Settings()).also {
@@ -62,15 +62,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.*
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
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.data.*
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
@@ -308,6 +300,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PokeRodRegistrySyncPacket.ID, PokeRodRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -134,6 +134,15 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val RELIC_COIN_POUCH_PLACE = this.create("relic_coin_pouch.place")
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val FISHING_NOTIFICATION = this.create("fishing.notification")
@JvmField
val FISHING_SPLASH_BIG = this.create("fishing.splash_big")
@JvmField
val FISHING_SPLASH_SMALL = this.create("fishing.splash_small")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -32,7 +32,14 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6))))
VillagerProfession.FARMER -> listOf(
VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
)
VillagerProfession.FISHERMAN -> listOf(
VillagerTradeOffer(VillagerProfession.FISHERMAN, 5, listOf(
TradeOffers.SellItemFactory(CobblemonItems.POKEROD_SMITHING_TEMPLATE, 12, 3, 30)
))
)
else -> emptyList()
}
@@ -113,6 +113,13 @@ object CobblemonCriteria {
)
)
val CAST_POKE_ROD = this.create(
SimpleCriterionTrigger(
cobblemonResource("cast_poke_rod"),
CastPokeRodCriterionCondition::class.java
)
)
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create(
SimpleCriterionTrigger(
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class CastPokeRodCriterionCondition(id: Identifier, predicate: LootContextPredicate): SimpleCriterionCondition<Boolean>(id, predicate) {
var hasBait: Boolean = false
override fun toJson(json: JsonObject) {
json.addProperty("hasBait", hasBait)
}
override fun fromJson(json: JsonObject) {
hasBait = json.get("hasBait").asBoolean
}
override fun matches(player: ServerPlayerEntity, context: Boolean): Boolean {
return hasBait == context
}
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
data class FishingBait(
val item: Identifier,
val effects: Map<Identifier, Effect>,
) {
data class Effect(
val nature: Identifier?,
val ivs: List<Identifier>?,
val useAll: Boolean?,
val gender: Identifier?,
val tera: Identifier?,
val chance: Double = 0.0,
val value: Double = 0.0
)
object Effects {
val NATURE = cobblemonResource("nature")
val IV = cobblemonResource("iv")
val EV = cobblemonResource("ev")
val BITE_TIME = cobblemonResource("bite_time")
val GENDER_CHANCE = cobblemonResource("gender_chance")
val LEVEL_RAISE = cobblemonResource("level_raise")
val TERA = cobblemonResource("tera")
val SHINY_REROLL = cobblemonResource("shiny_reroll")
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fishing.FishingBaitRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object FishingBaits : JsonDataRegistry<FishingBait>{
override val id = cobblemonResource("fishing_baits")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<FishingBaits>()
override val typeToken: TypeToken<FishingBait> = TypeToken.get(FishingBait::class.java)
override val resourcePath = "fishing_baits"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
private val itemMap = mutableMapOf<Identifier, FishingBait>()
override fun sync(player: ServerPlayerEntity) {
FishingBaitRegistrySyncPacket(this.itemMap.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<Identifier, FishingBait>) {
itemMap.clear()
data.forEach { id, bait ->
itemMap[id] = bait
}
}
fun getFromItemStack(stack: ItemStack): FishingBait? {
return itemMap[Registries.ITEM.getId(stack.item)]
}
fun isFishingBait(stack: ItemStack) = itemMap.containsKey(Registries.ITEM.getId(stack.item))
//
//
// fun getBaitSubcategory(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.subcategory
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.subcategory
// }
// return null
// }
//
// fun getBaitSuccessChance(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.chance
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.chance
// }
// return null
// }
//
// fun getBaitEffectValue(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.value
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.value
// }
// return null
// }
//
//
// fun getBaitNote(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.note
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.note
// }
// return null
// }
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.pokeball.PokeBall
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
/**
* Base poke rod object
* It is intended that there is one poke rod object initialized for a given poke rod type.
*
* @property name the poke rod registry name
* @property pokeBallId The [Identifier] of the pokeball that is used as the bobber for this rod
* @property lineColor list of [RGB] values that apply to the fishing line of the Pokérod
*/
data class PokeRod(
val pokeBallId: Identifier,
//Hex string of color
val lineColor: String,
var name: Identifier?
) {
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(name)
buffer.writeIdentifier(pokeBallId)
buffer.writeString(lineColor)
}
companion object {
internal fun decode(buffer: PacketByteBuf): PokeRod {
val name = buffer.readIdentifier()
val pokeBallId = buffer.readIdentifier()
val lineColor = buffer.readString()
return PokeRod(pokeBallId, lineColor, name)
}
}
fun getPokeBall(): PokeBall? {
return PokeBalls.getPokeBall(pokeBallId)
}
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokeRodRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
/**
* The data registry for [PokeRod]s.
* All the pokerod fields are guaranteed to exist
*/
object PokeRods : JsonDataRegistry<PokeRod> {
override val id = cobblemonResource("pokerods")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<PokeRods>()
// ToDo once datapack pokerod is implemented add required adapters here
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<PokeRod> = TypeToken.get(PokeRod::class.java)
override val resourcePath = "pokerods"
private val rods = mutableMapOf<Identifier, PokeRod>()
override fun reload(data: Map<Identifier, PokeRod>) {
{
data.forEach {
it.value.name = it.key
rods[it.key] = it.value
}
}
rods.putAll(data)
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
PokeRodRegistrySyncPacket(rods.values).sendToPlayer(player)
}
/**
* Gets a Pokerod from registry name.
* @return the pokerod object if found otherwise null.
*/
fun getPokeRod(name : Identifier): PokeRod? = rods[name]
}
@@ -168,7 +168,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
createDefault("level_ball", CatchRateModifiers.LEVEL)
// ToDo we will need fishing context here once fishing is implemented for a multiplier
createDefault("lure_ball", CatchRateModifiers.typeBoosting(2F, ElementalTypes.WATER))
createDefault("lure_ball", CatchRateModifiers.LURE)
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
createDefault("love_ball", CatchRateModifiers.LOVE)
createDefault("friend_ball", effects = listOf(CaptureEffects.friendshipSetter(150)))
@@ -121,6 +121,18 @@ object CatchRateModifiers {
1F
}
/**
* Used by [PokeBalls.LURE_BALL].
* Checks if the entity has an aspect for being spawned from fishing.
* If yes boosts the catch rate by *4
*/
val LURE = WorldStateModifier { _, entity ->
if (entity.pokemon.aspects.contains("fished")) // todo make aspect about this
4f
else
1F
}
/**
* Used by [PokeBalls.HEAVY_BALL].
* The base implementation of the heavy ball modifier mechanics.
@@ -12,11 +12,13 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.FishingSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
@@ -32,6 +34,7 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawning
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
@@ -83,6 +86,7 @@ object BestSpawner {
var config = BestSpawnerConfig()
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun loadConfig() {
LOGGER.info("Starting the Best Spawner...")
@@ -91,6 +95,7 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -104,6 +109,7 @@ object BestSpawner {
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
@@ -118,5 +124,7 @@ object BestSpawner {
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
fishingSpawner.onServerStarted()
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
import net.minecraft.item.Item
/**
* A spawning condition that applies to [FishingSpawningContext]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
override fun contextClass() = FishingSpawningContext::class.java
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
return false
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
}
companion object {
const val NAME = "fishing"
}
}
@@ -11,12 +11,16 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.Merger
import com.cobblemon.mod.common.util.math.orMax
import com.cobblemon.mod.common.util.math.orMin
import com.mojang.datafixers.util.Either
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.world.biome.Biome
@@ -56,6 +60,9 @@ abstract class SpawningCondition<T : SpawningContext> {
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var structures: MutableList<Either<Identifier, TagKey<Structure>>>? = null
var lureLevel: Int? = null
var bait: String? = null
var rodType: String? = null
@Transient
var appendages = mutableListOf<AppendageCondition>()
@@ -108,8 +115,32 @@ abstract class SpawningCondition<T : SpawningContext> {
}
) {
return false
} else if (lureLevel != null && ctx is FishingSpawningContext) { // check for the lureLevel of the rod
val pokerodStack = (ctx as FishingSpawningContext).rodStack
if (EnchantmentHelper.getLure(pokerodStack) < lureLevel!!)
return false
} else if (bait != null && ctx is FishingSpawningContext) { // check for the bait on the bobber
val pokerodItem = (ctx as FishingSpawningContext).rodItem
if (Registries.ITEM.getId(pokerodItem?.bait?.item).path != bait)
return false
} else if (rodType != null && ctx is FishingSpawningContext) { // check for the type of pokerod being used
val pokerodItem = (ctx as FishingSpawningContext).rodItem
if (pokerodItem?.pokeRodId?.path != rodType)
return false
}
/*else if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
val pokerodItem = (ctx as FishingSpawningContext).rodItem
// todo check if the EV yield of the berry matches the bait EV attract maybe?
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
return false
}*/
return true
}
@@ -76,4 +76,51 @@ interface AreaContextResolver {
return contexts
}
// fun resolveFishing(
// spawner: Spawner,
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
// slice: WorldSlice
// ): List<AreaSpawningContext> {
// var pos = BlockPos.Mutable(1, 2, 3)
// val input = AreaSpawningInput(spawner, pos, slice)
// val contexts = mutableListOf<AreaSpawningContext>()
//
// var x = slice.baseX
// var y = slice.baseY
// var z = slice.baseZ
//
// while (x < slice.baseX + slice.length) {
// while (y < slice.baseY + slice.height) {
// while (z < slice.baseZ + slice.width) {
// pos.set(x, y, z)
// val vec = pos.toVec3d()
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
// val fittedContextCalculator = contextCalculators
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
// if (fittedContextCalculator != null) {
// val context = fittedContextCalculator.calculate(input)
// if (context != null) {
// contexts.add(context)
// // The position BlockPos has been used in a context, editing the same one
// // will cause entities to spawn at the wrong location (buried in walls, usually)
// // I made it so that our context calculators specifically take a copy of the
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// pos = BlockPos.Mutable(1, 2, 3)
// input.position = pos
// }
// }
// }
// z++
// }
// y++
// z = slice.baseZ
// }
// x++
// y = slice.baseY
// z = slice.baseZ
// }
//
// return contexts
// }
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.util.getBlockStates
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
/**
* A spawning context that was generated for a fishing action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningContext(
cause: FishingSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(Box.of(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.mapNotNull { it.block }.distinct() }
val rodStack = cause.rodStack
val rodItem = cause.rodItem
val baitStack = cause.baitStack
val baitItem = cause.baitItem
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawning context relating to triggered spawning. Uses a single-point structure cache which is
* the only contribution of this implementation.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class TriggerSpawningContext(
override val cause: SpawnCause,
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>
) : SpawningContext() {
val triggerStructureCache = StructureChunkCache()
override fun getStructureCache(pos: BlockPos) = triggerStructureCache
}
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.FishedFeatureHandler
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
import com.cobblemon.mod.common.util.weightedSelection
import net.minecraft.world.gen.GenerationStep.Feature
/**
* A [SpawnAction] that will spawn a single [PokemonEntity].
@@ -45,6 +47,8 @@ class PokemonSpawnAction(
null
}?.createStack(ctx)
val entity = props.createEntity(ctx.world)
// add fishing aspect if fished with a pokerod
FishedFeatureHandler.updateFished(entity.pokemon, ctx.spawner.name == "fishing")
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(ctx.world, ctx.position))
if (heldItem != null) {
entity.pokemon.swapHeldItem(heldItem)
@@ -0,0 +1,123 @@
/*
* 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.Cobblemon
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
import kotlin.math.min
import kotlin.random.Random.Default.nextInt
class FishingBaitInfluence : SpawningInfluence {
override fun affectAction(action: SpawnAction<*>) {
if (action !is PokemonSpawnAction || action.ctx !is FishingSpawningContext) return
val bait = FishingBaits.getFromItemStack(action.ctx.baitStack) ?: return
bait.effects.entries.forEach { (type, effect) ->
if (Math.random() > effect.chance) return
when (type) {
FishingBait.Effects.SHINY_REROLL -> shinyReroll(action, effect)
FishingBait.Effects.NATURE -> alterNatureAttempt(action, effect)
FishingBait.Effects.IV -> alterIVAttempt(action, effect)
FishingBait.Effects.GENDER_CHANCE -> alterGenderAttempt(action, effect)
FishingBait.Effects.LEVEL_RAISE -> alterLevelAttempt(action, effect)
FishingBait.Effects.TERA -> alterTeraAttempt(action, effect)
FishingBait.Effects.HIDDEN_ABILITY_CHANCE -> alterHAAttempt(action)
}
}
}
private fun shinyReroll(action: PokemonSpawnAction, effect: FishingBait.Effect) {
if (action.props.shiny != null) return
val shinyOdds = Cobblemon.config.shinyRate.toInt()
val randomNumber = nextInt(0, shinyOdds + 1)
if (randomNumber <= (effect.value).toInt()) {
action.props.shiny = true
}
}
private fun alterNatureAttempt(action: PokemonSpawnAction, effect: FishingBait.Effect) {
if (action.props.nature != null) return
if (effect.nature == null) return
// TIMNOTE: This replaces the static lists. It's less performant because it's being reviewed every time,
// but also it's not something that goes off too often.
val possibleNatures = Natures.all().filter { it.increasedStat?.identifier == effect.nature }
if (possibleNatures.isEmpty()) return
val takenNature = possibleNatures.random()
action.props.nature = takenNature.name.namespace
}
private fun alterIVAttempt(action: PokemonSpawnAction, effect: FishingBait.Effect) {
val ivs = effect.ivs ?: return
val useAll = effect.useAll ?: return
if (action.props.ivs == null) {
action.props.ivs = IVs.createRandomIVs()
}
if (useAll) {
ivs.forEach { iv -> boostIv(iv, action, effect) }
} else {
boostIv(ivs.random(), action, effect)
}
}
private fun boostIv(iv: Identifier, action: PokemonSpawnAction, effect: FishingBait.Effect) {
val targetedStat = Stats.getStat(iv.path)
val currentIv = action.props.ivs!![targetedStat] ?: 0
action.props.ivs!![targetedStat] = min(currentIv + effect.value.toInt(), IVs.MAX_VALUE)
}
private fun alterGenderAttempt(action: PokemonSpawnAction, effect: FishingBait.Effect) {
if (effect.gender == null) return
if (action.props.gender != null) return
when (effect.gender) {
cobblemonResource("male") -> if (action.props.gender != Gender.MALE) action.props.gender = Gender.MALE
cobblemonResource("female") -> if (action.props.gender != Gender.FEMALE) action.props.gender = Gender.FEMALE
}
}
private fun alterLevelAttempt(action: PokemonSpawnAction, effect: FishingBait.Effect) {
action.props.level =
(action.props.level ?: action.detail.getDerivedLevelRange().random()) + effect.value.toInt()
}
private fun alterTeraAttempt(action: PokemonSpawnAction, effect: FishingBait.Effect) {
if (effect.tera == null) return
if (action.props.teraType != null) return
action.props.teraType = effect.tera.path
}
private fun alterHAAttempt(action: PokemonSpawnAction) {
if (action.props.ability != null) return
val species = action.props.species?.let { PokemonSpecies.getByName(it) } ?: return
val ability = species.abilities.mapping[Priority.LOW]?.first()?.template?.name ?: return
action.props.ability = ability
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.item.interactive.PokerodItem
import net.minecraft.entity.Entity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
/**
* A spawning cause that is embellished with fishing information. Could probably also
* have the bobber entity or something.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?,
val rodStack: ItemStack,
val baitStack: ItemStack
) : SpawnCause(spawner, bucket, entity) {
val rodItem = rodStack.item as? PokerodItem
val baitItem = baitStack.item as Item
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class FishingSpawner(
override val name: String = "fishing",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
override fun parseContext(cause: FishingSpawnCause, world: ServerWorld, pos: BlockPos): FishingSpawningContext? {
// Maybe confirm that it's water we're fishing in?
val ctx = FishingSpawningContext(
cause,
world,
pos,
influences
)
return ctx
}
fun onServerStarted() {
// TODO: Register influences here
influences.add(FishingBaitInfluence())
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A type of spawner that occurs at a single point and provides its own means of generating a
* context from a particular cause and position. The trigger spawner must be directly called to
* spawn things (almost as if it was being triggered) as opposed to being scheduled on a ticker.
*
* @author Hiroku
* @since February 3rd, 2024
*/
abstract class TriggerSpawner<T : SpawnCause>(override val name: String, var spawns: SpawnPool) : Spawner {
private var selector: SpawningSelector = FlatContextWeightedSelector()
override val influences = mutableListOf<SpawningInfluence>()
open fun run(cause: T, world: ServerWorld, pos: BlockPos): CompletableFuture<*>? {
val context = parseContext(cause, world, pos) ?: return null
selector.select(this, listOf(context))?.let { (_, spawn) ->
val action = spawn.doSpawn(context)
action.complete()
return action.future
}
return null
}
/** Parses a context, if possible, from the given cause and position. */
abstract fun parseContext(cause: T, world: ServerWorld, pos: BlockPos): SpawningContext?
override fun canSpawn() = true
override fun getSpawningSelector() = selector
override fun setSpawningSelector(selector: SpawningSelector) { this.selector = selector }
override fun getSpawnPool() = spawns
override fun setSpawnPool(spawnPool: SpawnPool) { spawns = spawnPool }
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
@@ -266,6 +267,8 @@ object CobblemonClient {
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
LOGGER.info("Registering Generic Bedrock Entity renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering PokeRod Bobber renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.POKE_BOBBER) { ctx -> PokeBobberEntityRenderer(ctx) }
}
fun reloadCodedAssets(resourceManager: ResourceManager) {
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.block.DisplayCaseBlock
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
@@ -136,7 +137,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEntity
mobHeads.contains(stack.item) -> PositioningType.MOB_HEAD
stack.item is BedItem -> PositioningType.BED
stack.item is BannerItem -> PositioningType.BANNER
stack.item is PokeBallItem -> PositioningType.POKE_BALL
stack.isIn(CobblemonItemTags.POKE_BALLS) -> PositioningType.POKE_BALL
stack.item == CobblemonItems.RELIC_COIN_POUCH -> PositioningType.COIN_POUCH
stack.item == CobblemonItems.PASTURE -> PositioningType.PASTURE
stack.item == CobblemonItems.POKEMON_MODEL -> PositioningType.ITEM_MODEL
@@ -0,0 +1,311 @@
/*
* 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.render.entity
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.util.cobblemonResource
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.OverlayTexture
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.VertexConsumer
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.entity.EntityRenderer
import net.minecraft.client.render.entity.EntityRendererFactory
import net.minecraft.client.render.model.json.ModelTransformationMode
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.util.Arm
import net.minecraft.util.Identifier
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.RotationAxis
import org.joml.Matrix3f
import org.joml.Matrix4f
import org.joml.Quaterniond
import org.joml.Quaternionf
import org.joml.Vector3f
import java.awt.Color
@Environment(value = EnvType.CLIENT)
class PokeBobberEntityRenderer(context: EntityRendererFactory.Context?) : EntityRenderer<PokeRodFishingBobberEntity>(context) {
private var lastSpinAngle: Float = 0f
private var randomPitch: Float = 0f
private var randomYaw: Float = 0f
override fun render(fishingBobberEntity: PokeRodFishingBobberEntity, elapsedPartialTicks: Float, tickDelta: Float, matrixStack: MatrixStack, vertexConsumerProvider: VertexConsumerProvider, light: Int) {
var playerPosXWorld: Double
val eyeHeightOffset: Float
val playerPosZWorld: Double
val playerPosYWorld: Double
val playerEyeYWorld: Double
val playerEntity = fishingBobberEntity.playerOwner ?: return
val berryAdjustY = 0.0 // for tweaking height pos of some berry baits
val berryAdjustX = 0.0 // for tweaking width pos of some berry baits
matrixStack.push() // prepare for overall rendering transforms
// Generate controlled random pitch and yaw for each cast, with constraints
if (fishingBobberEntity.age <= 1) {
// Random yaw within a constrained range to allow for limited tilting
randomYaw = (-180 + Math.random() * 360).toFloat() // Example: -180 to +180 degrees
// Random pitch to ensure the top of the Poke Ball leans towards the string
randomPitch = (-40 + Math.random() * 80).toFloat() // Example: -40 to +40 degrees
}
matrixStack.push() // prepare for bobber rendering transforms
// Apply spinning effect only if the bobber is in the air
// Modify spinning effect based on whether the bobber is in open water
if (!fishingBobberEntity.isOnGround) {
// You might not need to change stopRotationAge if you want the slowing to happen faster
val stopRotationAge = 220 // This could remain as your baseline for when rotation completely stops
// Adjust ageFactor calculation for faster slowing in open water
val ageFactor = if (fishingBobberEntity.age < stopRotationAge) {
if (fishingBobberEntity.inOpenWater) {
1 + fishingBobberEntity.age / 15.0 // Slows down much faster in open water
} else {
1 + fishingBobberEntity.age / 100.0 // Standard slowing rate
}
} else {
Double.POSITIVE_INFINITY // Stops spinning
}
// Ensure ageFactor does not reduce the rotation speed to 0 or negative
val adjustedAgeFactor = if (ageFactor > 0) ageFactor else 1.0
// Update and apply the spinning effect, incorporating the slowing factor
// Stop increasing lastSpinAngle once the bobber reaches the stopRotationAge
if (fishingBobberEntity.age < stopRotationAge) {
lastSpinAngle = (((fishingBobberEntity.age + tickDelta) * 20 / adjustedAgeFactor) % 360).toFloat()
}
// After reaching stopRotationAge, lastSpinAngle remains constant, effectively stopping the rotation
}
// When in water, gradually make the Poke Ball upright
if (fishingBobberEntity.state == PokeRodFishingBobberEntity.State.BOBBING && !fishingBobberEntity.isOnGround) {
// Assuming randomPitch is the rotation around X axis that needs to be adjusted
// Simple linear interpolation towards 0 degrees; adjust 'lerpFactor' as needed for speed
val lerpFactor = 0.04f // This controls the speed of the adjustment
randomPitch = MathHelper.lerp(lerpFactor, randomPitch, 0f) // Adjust towards 0 degrees within range
// Consider similar logic for yaw if needed to maintain visual consistency
}
// Apply random pitch and yaw before rendering the Poke Ball
matrixStack.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(randomPitch))
matrixStack.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(randomYaw))
// Apply rotation based on last spin angle
matrixStack.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(lastSpinAngle))
// Scale down the Poke Ball to 70% of its original size
matrixStack.scale(0.7f, 0.7f, 0.7f) // Apply the scaling transformation
// try to render the bobber sprite as well to have hook pop out the bottom of the Bobber model
val entry = matrixStack.peek()
val matrix4f = entry.positionMatrix
val matrix3f = entry.normalMatrix
// Scale factor
// Scale factor
val scale = 0.75f
// Additional shift to the leeft
val shiftHook = 0.045f // Adjust this value as needed to shift the geometry
val vertexConsumer = vertexConsumerProvider.getBuffer(PokeBobberEntityRenderer.Companion.LAYER)
/*// Original vertices for one side
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 0.0f, 0, 0, 1)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 1.0f, 0, 1, 1)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 1.0f, 1, 1, 0)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 0.0f, 1, 0, 0)
// Add vertices for the opposite side in reverse order
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 0.0f, 1, 0, 0)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 1.0f, 1, 1, 0)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 1.0f, 0, 1, 1)
PokeBobberEntityRenderer.Companion.vertex(vertexConsumer, matrix4f, matrix3f, light, 0.0f, 0, 0, 1)*/
// Adjusted and flipped vertices for one side, scaled by 75% and shifted
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 0.0f) * scale + 0.125f + shiftHook, (0.0f * scale + 0.125f), 0, 1)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 1.0f) * scale + 0.125f + shiftHook, (0.0f * scale + 0.125f), 1, 1)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 1.0f) * scale + 0.125f + shiftHook, (1.0f * scale + 0.125f), 1, 0)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 0.0f) * scale + 0.125f + shiftHook, (1.0f * scale + 0.125f), 0, 0)
// Adjusted and flipped vertices for the opposite side in reverse order, scaled by 75% and shifted
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 0.0f) * scale + 0.125f + shiftHook, (1.0f * scale + 0.125f), 0, 0)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 1.0f) * scale + 0.125f + shiftHook, (1.0f * scale + 0.125f), 1, 0)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 1.0f) * scale + 0.125f + shiftHook, (0.0f * scale + 0.125f), 1, 1)
vertex(vertexConsumer, matrix4f, matrix3f, light, (1.0f - 0.0f) * scale + 0.125f + shiftHook, (0.0f * scale + 0.125f), 0, 1)
val pokeRodIdStr = fishingBobberEntity.dataTracker.get(PokeRodFishingBobberEntity.POKEROD_ID)
val pokeBobberBaitItemStack = fishingBobberEntity.dataTracker.get(PokeRodFishingBobberEntity.POKEBOBBER_BAIT)
val pokeRodId = Identifier.tryParse(pokeRodIdStr)
val pokeRod = PokeRods.getPokeRod(pokeRodId!!)
val ballItem = PokeBalls.getPokeBall(pokeRod?.pokeBallId!!)!!.item
// render the pokebobber
MinecraftClient.getInstance().itemRenderer.renderItem(
ballItem.defaultStack,
ModelTransformationMode.GROUND,
light,
OverlayTexture.DEFAULT_UV,
matrixStack,
vertexConsumerProvider,
fishingBobberEntity.world,
0
)
matrixStack.push() // prepare for transforms for Bait rendering
// check if any adjustments are needed for the certain kind of bait
val berryAdjust = adjustBerry(pokeBobberBaitItemStack)
// Scale down the bait to 70% of its original size
matrixStack.scale(0.8f, 0.8f, 0.8f) // Apply the scaling transformation
// Translate the bait downwards to be on the hook
matrixStack.translate(0.20 + berryAdjust.first, -0.5 + berryAdjust.second, 0.0); // Move the berry down
// Rotate the bait 90 degrees around the Y-axis
val rotation = Quaternionf().rotateY(Math.toRadians(0.0).toFloat())
matrixStack.multiply(rotation)
// render the bait
MinecraftClient.getInstance().itemRenderer.renderItem(
pokeBobberBaitItemStack,
ModelTransformationMode.GROUND,
light,
OverlayTexture.DEFAULT_UV,
matrixStack,
vertexConsumerProvider,
fishingBobberEntity.world,
0
)
matrixStack.pop() // close bait rendering transforms
matrixStack.pop() // close bobber rendering transforms
var armOffset = if (playerEntity.mainArm == Arm.RIGHT) 1 else -1
val itemStack = playerEntity.mainHandStack
if (itemStack.item !is PokerodItem) {
armOffset = -armOffset
}
val handSwingProgress = playerEntity.getHandSwingProgress(tickDelta)
val swingAngle = MathHelper.sin(MathHelper.sqrt(handSwingProgress) * Math.PI.toFloat())
val bodyYawRadians = MathHelper.lerp(tickDelta, playerEntity.prevBodyYaw, playerEntity.bodyYaw) * (Math.PI.toFloat() / 180)
val sinBodyYaw = MathHelper.sin(bodyYawRadians).toDouble()
val cosBodyYaw = MathHelper.cos(bodyYawRadians).toDouble()
val horizontalOffset = armOffset.toDouble() * 0.35
if (dispatcher.gameOptions != null && !dispatcher.gameOptions.perspective.isFirstPerson || playerEntity !== MinecraftClient.getInstance().player) {
playerEyeYWorld = MathHelper.lerp(tickDelta.toDouble(), playerEntity.prevX, playerEntity.x) - cosBodyYaw * horizontalOffset - sinBodyYaw * 0.8
playerPosYWorld = playerEntity.prevY + playerEntity.standingEyeHeight.toDouble() + (playerEntity.y - playerEntity.prevY) * tickDelta.toDouble() - 0.45
playerPosZWorld = MathHelper.lerp(tickDelta.toDouble(), playerEntity.prevZ, playerEntity.z) - sinBodyYaw * horizontalOffset + cosBodyYaw * 0.8
eyeHeightOffset = if (playerEntity.isInSneakingPose) -0.1875f else 0.0f
} else {
playerPosXWorld = 960.0 / dispatcher.gameOptions.fov.value.toDouble()
var vec3d = dispatcher.camera.projection.getPosition(armOffset.toFloat() * 0.525f, -0.1f)
vec3d = vec3d.multiply(playerPosXWorld)
vec3d = vec3d.rotateY(swingAngle * 0.5f)
vec3d = vec3d.rotateX(-swingAngle * 0.7f)
playerEyeYWorld = MathHelper.lerp(tickDelta.toDouble(), playerEntity.prevX, playerEntity.getX()) + vec3d.x
playerPosYWorld = MathHelper.lerp(tickDelta.toDouble(), playerEntity.prevY, playerEntity.getY()) + vec3d.y
playerPosZWorld = MathHelper.lerp(tickDelta.toDouble(), playerEntity.prevZ, playerEntity.getZ()) + vec3d.z
eyeHeightOffset = playerEntity.getStandingEyeHeight()
}
playerPosXWorld = MathHelper.lerp(tickDelta.toDouble(), fishingBobberEntity.prevX, fishingBobberEntity.x)
val bobberPosY = MathHelper.lerp(tickDelta.toDouble(), fishingBobberEntity.prevY, fishingBobberEntity.y) + 0.25
val bobberPosZ = MathHelper.lerp(tickDelta.toDouble(), fishingBobberEntity.prevZ, fishingBobberEntity.z)
val deltaX = (playerEyeYWorld - playerPosXWorld).toFloat()
val deltaY = (playerPosYWorld - bobberPosY).toFloat() + eyeHeightOffset
val deltaZ = (playerPosZWorld - bobberPosZ).toFloat()
val vertexConsumer2 = vertexConsumerProvider.getBuffer(RenderLayer.getLineStrip())
val entry2 = matrixStack.peek()
for (lineIndex in 0..16) {
renderFishingLine(pokeRod.lineColor, deltaX, deltaY, deltaZ, vertexConsumer2, entry2, percentage(lineIndex, 16), percentage(lineIndex + 1, 16))
}
matrixStack.pop() // close main rendering transforms
super.render(fishingBobberEntity, elapsedPartialTicks, tickDelta, matrixStack, vertexConsumerProvider, light)
}
fun adjustBerry(berryBait: ItemStack): Pair<Double, Double> {
return when (Registries.ITEM.getId(berryBait.item).toString()) {
"wacan_berry" -> Pair(0.03, 0.0)
"cheri_berry" -> Pair(0.03, 0.0)
else -> Pair(0.0, 0.0)
}
}
companion object {
private val TEXTURE = cobblemonResource("textures/item/fishing/bobber_hook.png")
private val LAYER = RenderLayer.getEntityCutout(TEXTURE)
private fun vertex(buffer: VertexConsumer, matrix: Matrix4f, normalMatrix: Matrix3f, light: Int, x: Float, y: Float, u: Int, v: Int) {
buffer.vertex(matrix, x - 0.5f, y.toFloat() - 0.5f, 0.0f).color(255, 255, 255, 255).texture(u.toFloat(), v.toFloat()).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMatrix, 0.0f, 1.0f, 0.0f).next()
}
@JvmStatic
private fun percentage(value: Int, max: Int): Float {
return value.toFloat() / max.toFloat()
}
@JvmStatic
private fun renderFishingLine(color: String, deltaX: Float, deltaY: Float, deltaZ: Float, vertexBuffer: VertexConsumer, matrixEntry: MatrixStack.Entry, segmentStartFraction: Float, segmentEndFraction: Float) {
val colorObj = Color.decode(color)
// Calculate the starting X position of the current segment based on the start fraction
val startX = deltaX * segmentStartFraction
// Calculate the starting Y position of the current segment, adding a curvature effect and a base offset
var startY = deltaY * (segmentStartFraction * segmentStartFraction + segmentStartFraction) * 0.5f + 0.25f
// Calculate the starting Z position of the current segment based on the start fraction
val startZ = deltaZ * segmentStartFraction
// Calculate the change in X position from the start to the end of the current segment
var deltaXSegment = deltaX * segmentEndFraction - startX
// Calculate the change in Y position from the start to the end of the current segment, adjusting for curvature and base offset
var deltaYSegment = deltaY * (segmentEndFraction * segmentEndFraction + segmentEndFraction) * 0.5f + 0.25f - startY
// Calculate the change in Z position from the start to the end of the current segment
var deltaZSegment = deltaZ * segmentEndFraction - startZ
// Normalize the segment vector to use it for the normal vector in the vertex data
val length = MathHelper.sqrt(deltaXSegment * deltaXSegment + deltaYSegment * deltaYSegment + deltaZSegment * deltaZSegment)
deltaXSegment /= length
deltaYSegment /= length
deltaZSegment /= length
// New factor to adjust the curve so it ends lower
// This could be a simple linear adjustment or more complex based on your needs
val loweringFactor = 1.0f - segmentEndFraction // Becomes 0 at the start and approaches 1 towards the end
val additionalLowering = loweringFactor * 0.1f // Adjust this factor to control how much lower the end goes
// Apply the lowering adjustment
startY -= additionalLowering
// Add the vertex for the start of this segment
vertexBuffer.vertex(matrixEntry.positionMatrix, startX, startY, startZ).color(colorObj.red, colorObj.green, colorObj.blue, 255).normal(matrixEntry.normalMatrix, deltaXSegment, deltaYSegment, deltaZSegment).next()
}
}
override fun getTexture(entity: PokeRodFishingBobberEntity): Identifier {
return cobblemonResource("textures/item/fishing/bobber_hook.png")
}
}
@@ -0,0 +1,95 @@
/*
* 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.render.item
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.interactive.PokerodItem
import net.minecraft.client.item.ClampedModelPredicateProvider
import net.minecraft.client.item.ModelPredicateProviderRegistry
import net.minecraft.client.world.ClientWorld
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier
object CobblemonModelPredicateRegistry {
fun registerPredicates() {
val rods = listOf(
CobblemonItems.AZURE_ROD,
CobblemonItems.BEAST_ROD,
CobblemonItems.CHERISH_ROD,
CobblemonItems.CITRINE_ROD,
CobblemonItems.DIVE_ROD,
CobblemonItems.DREAM_ROD,
CobblemonItems.DUSK_ROD,
CobblemonItems.FAST_ROD,
CobblemonItems.FRIEND_ROD,
CobblemonItems.GREAT_ROD,
CobblemonItems.HEAL_ROD,
CobblemonItems.HEAVY_ROD,
CobblemonItems.LEVEL_ROD,
CobblemonItems.LOVE_ROD,
CobblemonItems.LURE_ROD,
CobblemonItems.LUXURY_ROD,
CobblemonItems.MASTER_ROD,
CobblemonItems.MOON_ROD,
CobblemonItems.NEST_ROD,
CobblemonItems.NET_ROD,
CobblemonItems.PARK_ROD,
CobblemonItems.POKE_ROD,
CobblemonItems.PREMIER_ROD,
CobblemonItems.QUICK_ROD,
CobblemonItems.REPEAT_ROD,
CobblemonItems.ROSEATE_ROD,
CobblemonItems.SAFARI_ROD,
CobblemonItems.SLATE_ROD,
CobblemonItems.SPORT_ROD,
CobblemonItems.TIMER_ROD,
CobblemonItems.ULTRA_ROD,
CobblemonItems.VERDANT_ROD,
CobblemonItems.ANCIENT_AZURE_ROD,
CobblemonItems.ANCIENT_CITRINE_ROD,
CobblemonItems.ANCIENT_FEATHER_ROD,
CobblemonItems.ANCIENT_GIGATON_ROD,
CobblemonItems.ANCIENT_GREAT_ROD,
CobblemonItems.ANCIENT_HEAVY_ROD,
CobblemonItems.ANCIENT_IVORY_ROD,
CobblemonItems.ANCIENT_JET_ROD,
CobblemonItems.ANCIENT_LEADEN_ROD,
CobblemonItems.ANCIENT_ORIGIN_ROD,
CobblemonItems.ANCIENT_POKE_ROD,
CobblemonItems.ANCIENT_ROSEATE_ROD,
CobblemonItems.ANCIENT_SLATE_ROD,
CobblemonItems.ANCIENT_ULTRA_ROD,
CobblemonItems.ANCIENT_VERDANT_ROD,
CobblemonItems.ANCIENT_WING_ROD
)
rods.forEach { rod ->
ModelPredicateProviderRegistry.register(rod, Identifier("cast"), ClampedModelPredicateProvider { stack, world, entity, seed ->
if (entity == null) {
0.0f
} else {
val isMainHand = entity.mainHandStack == stack
var isOffHand = entity.offHandStack == stack
if (entity.mainHandStack.item is PokerodItem) {
isOffHand = false
}
if ((isMainHand || isOffHand) && entity is PlayerEntity && entity.fishHook != null) 1.0f else 0.0f
}
})
}
}
}
@@ -17,6 +17,8 @@ import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.dialogue.Dialogues
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fossil.Fossils
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.moves.Moves
@@ -74,7 +76,9 @@ object CobblemonDataProvider : DataProvider {
this.register(Fossils)
CobblemonSpawnPools.load()
this.register(PokeRods)
this.register(Berries)
this.register(FishingBaits)
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
synchronizedPlayerIds.remove(it.player.uuid)
@@ -0,0 +1,721 @@
/*
* 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.entity.fishing
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.loot.CobblemonLootTables
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.toBlockPos
import kotlin.math.sqrt
import net.minecraft.advancement.criterion.Criteria
import net.minecraft.block.Blocks
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityType
import net.minecraft.entity.ExperienceOrbEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.MovementType
import net.minecraft.entity.data.DataTracker
import net.minecraft.entity.data.TrackedData
import net.minecraft.entity.data.TrackedDataHandlerRegistry
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.projectile.FishingBobberEntity
import net.minecraft.entity.projectile.ProjectileUtil
import net.minecraft.item.ItemStack
import net.minecraft.loot.context.LootContextParameterSet
import net.minecraft.loot.context.LootContextParameters
import net.minecraft.loot.context.LootContextTypes
import net.minecraft.particle.DefaultParticleType
import net.minecraft.particle.ParticleTypes
import net.minecraft.registry.Registries
import net.minecraft.registry.tag.FluidTags
import net.minecraft.registry.tag.ItemTags
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.stat.Stats
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.Vec3d
import net.minecraft.util.math.random.Random
import net.minecraft.world.World
import kotlin.math.cos
import kotlin.math.sin
class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity>, world: World) : FishingBobberEntity(type, world) {
private val velocityRandom = Random.create()
private var caughtFish = false
private var outOfOpenWaterTicks = 0
// todo is this needed?
//val HOOK_ENTITY_ID = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.INTEGER)
//private val CAUGHT_FISH = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.BOOLEAN)
// todo if so replace these later
//var HOOK_ENTITY_ID = 0
//var CAUGHT_FISH = false
private var removalTimer = 0
private var hookCountdown = 0
private var waitCountdown = 0
private var fishTravelCountdown = 0
private var fishAngle = 0f
var inOpenWater = true
private var hookedEntity: Entity? = null
var state = State.FLYING
private var luckOfTheSeaLevel = 0
private var lureLevel = 0
private var typeCaught= "ITEM"
private var chosenBucket = Cobblemon.bestSpawner.config.buckets[0] // default to first rarity bucket
private val pokemonSpawnChance = 85 // chance a Pokemon will be fished up % out of 100
var pokeRodId: Identifier? = null
var lineColor: String = "000000" // default line color is black
var usedRod: Identifier? = null
var bobberBait: ItemStack = ItemStack.EMPTY
constructor(thrower: PlayerEntity, pokeRodId: Identifier, bait: ItemStack, world: World, luckOfTheSea: Int, lure: Int) : this(CobblemonEntities.POKE_BOBBER, world) {
owner = thrower
luckOfTheSeaLevel = luckOfTheSea
lureLevel = lure
this.pokeRodId = pokeRodId
this.bobberBait = bait
dataTracker.set(POKEROD_ID, pokeRodId.toString())
dataTracker.set(POKEBOBBER_BAIT, bobberBait)
dataTracker.set(HOOK_ENTITY_ID, 0)
dataTracker.set(CAUGHT_FISH, false)
this.usedRod = pokeRodId
val throwerPitch = thrower.pitch
val throwerYaw = thrower.yaw
val cosYaw = MathHelper.cos(-throwerYaw * 0.017453292f - 3.1415927f)
val sinYaw = MathHelper.sin(-throwerYaw * 0.017453292f - 3.1415927f)
val cosPitch = -MathHelper.cos(-throwerPitch * 0.017453292f)
val sinPitch = MathHelper.sin(-throwerPitch * 0.017453292f)
val posX = thrower.x - sinYaw.toDouble() * 0.3
val posY = thrower.eyeY
val posZ = thrower.z - cosYaw.toDouble() * 0.3
this.refreshPositionAndAngles(posX, posY, posZ, throwerYaw, throwerPitch)
var vec3d = Vec3d((-sinYaw).toDouble(), MathHelper.clamp(-(sinPitch / cosPitch), -5.0f, 5.0f).toDouble(), (-cosYaw).toDouble())
val m = vec3d.length()
vec3d = vec3d.multiply(0.6 / m + random.nextTriangular(0.5, 0.0103365), 0.6 / m + random.nextTriangular(0.5, 0.0103365), 0.6 / m + random.nextTriangular(0.5, 0.0103365))
velocity = vec3d
yaw = (MathHelper.atan2(vec3d.x, vec3d.z) * 57.2957763671875).toFloat()
pitch = (MathHelper.atan2(vec3d.y, vec3d.horizontalLength()) * 57.2957763671875).toFloat()
prevYaw = yaw
prevPitch = pitch
}
override fun initDataTracker() {
this.dataTracker.startTracking(HOOK_ENTITY_ID, 0)
this.dataTracker.startTracking(CAUGHT_FISH, false)
this.dataTracker.startTracking(POKEROD_ID, "")
this.dataTracker.startTracking(POKEBOBBER_BAIT, ItemStack.EMPTY)
}
override fun onTrackedDataSet(data: TrackedData<*>) {
if (HOOK_ENTITY_ID == data) {
val i = getDataTracker().get(HOOK_ENTITY_ID) as Int
this.hookedEntity = if (i > 0) world.getEntityById(i - 1) else null
}
if (CAUGHT_FISH == data) {
this.caughtFish = (getDataTracker().get(CAUGHT_FISH) as Boolean)
if (this.caughtFish) {
this.setVelocity(velocity.x, (-0.4f * MathHelper.nextFloat(this.velocityRandom, 0.3f, 0.5f)).toDouble(), velocity.z)
}
}
super.onTrackedDataSet(data)
}
fun calculateMinMaxCountdown(weight: Float): Pair<Int, Int> {
// Constants for the target min and max values at weight extremes
val minAtMaxWeight = 20
val maxAtMaxWeight = 40
val minAtMinWeight = 15
val maxAtMinWeight = 20
// Calculate factors for min and max based on the weight
val minFactor = ((minAtMaxWeight - minAtMinWeight) / 100f) * weight + minAtMinWeight
val maxFactor = ((maxAtMaxWeight - maxAtMinWeight) / 100f) * weight + maxAtMinWeight
// Ensure min and max are within the needed bounds
val min = minFactor.toInt().coerceIn(minAtMinWeight, minAtMaxWeight)
val max = maxFactor.toInt().coerceIn(maxAtMinWeight, maxAtMaxWeight)
return Pair(min, max)
}
fun chooseAdjustedSpawnBucket(buckets: List<SpawnBucket>, luckOfTheSeaLevel: Int): SpawnBucket {
val baseIncreases = listOf(5.0F, 1.0F, 0.2F) // Base increases for the first three buckets beyond the first
val adjustedWeights = buckets.mapIndexed { index, bucket ->
if (index == 0) {
// Placeholder, will be recalculated
0.0F
} else {
val increase = if (index < baseIncreases.size) baseIncreases[index] else baseIncreases.last() + (index - baseIncreases.size + 1) * 0.15F
bucket.weight + increase * luckOfTheSeaLevel
}
}.toMutableList()
// Recalculate the first bucket's weight to ensure the total is 100%
val totalAdjustedWeight = adjustedWeights.sum() - adjustedWeights[0] // Corrected to ensure the list contains Floats
adjustedWeights[0] = 100.0F - totalAdjustedWeight + buckets[0].weight
// Random selection based on adjusted weights
val weightSum = adjustedWeights.sum()
val chosenSum = kotlin.random.Random.nextDouble(weightSum.toDouble()).toFloat() // Ensure usage of Random from kotlin.random package
var sum = 0.0F
adjustedWeights.forEachIndexed { index, weight ->
sum += weight
if (sum >= chosenSum) {
return buckets[index]
}
}
return buckets.first() // Fallback
}
/*// make more generic version for other enchants. Pass in id of enchant
fun getEnchantLevel(enchantment: Enchantment): Int {
var pokerodEnchantments: NbtList? = null
if (owner?.isPlayer == true) {
pokerodEnchantments = (owner as PlayerEntity).mainHandStack?.enchantments
}
val luckOfTheSeaEnchantment = pokerodEnchantments?.filter {
if (it is NbtCompound)
if (it.getString("id") == Registries.ENCHANTMENT.getEntry(enchantment).key.toString())
return@filter true
return@filter false
}?.first()
return (luckOfTheSeaEnchantment as? NbtCompound)?.getShort("lvl")?.toInt() ?: 0
}*/
fun isOpenOrWaterAround(pos: BlockPos): Boolean {
var positionType = PositionType.INVALID
for (i in -1..2) {
val positionType2 = this.getPositionType(pos.add(-2, i, -2), pos.add(2, i, 2))
when (positionType2) {
PositionType.INVALID -> return false
PositionType.ABOVE_WATER -> if (positionType == PositionType.INVALID) {
return false
}
PositionType.INSIDE_WATER -> if (positionType == PositionType.ABOVE_WATER) {
return false
}
else -> return false
}
positionType = positionType2
}
return true
}
/*private fun getPositionType(start: BlockPos, end: BlockPos): PokeRodFishingBobberEntity.PositionType? {
return BlockPos.stream(start, end).map { pos: BlockPos? -> this.getPositionType(pos!!) }.reduce { positionType: PokeRodFishingBobberEntity.PositionType, positionType2: PokeRodFishingBobberEntity.PositionType -> if (positionType == positionType2) positionType else PokeRodFishingBobberEntity.PositionType.INVALID } as ((PositionType?, PositionType?) -> PositionType?)?.orElse(PokeRodFishingBobberEntity.PositionType.INVALID) as PokeRodFishingBobberEntity.PositionType
}*/
private fun getPositionType(start: BlockPos, end: BlockPos): PositionType? {
return BlockPos.stream(start, end)
.map { pos -> this.getPositionType(pos) }
.reduce { positionType, positionType2 ->
if (positionType == positionType2) positionType else PositionType.INVALID
}.orElse(PositionType.INVALID)
}
private fun getPositionType(pos: BlockPos): PositionType {
val blockState = world.getBlockState(pos)
return if (!blockState.isAir && !blockState.isOf(Blocks.LILY_PAD)) {
val fluidState = blockState.fluidState
if (fluidState.isIn(FluidTags.WATER) && fluidState.isStill && blockState.getCollisionShape(world, pos).isEmpty) PositionType.INSIDE_WATER else PositionType.INVALID
} else {
PositionType.ABOVE_WATER
}
}
enum class PositionType {
ABOVE_WATER,
INSIDE_WATER,
INVALID
}
private fun setPlayerFishHook(fishingBobber: FishingBobberEntity?) {
val playerEntity = this.playerOwner
if (playerEntity != null) {
playerEntity.fishHook = fishingBobber
}
}
enum class State {
FLYING,
HOOKED_IN_ENTITY,
BOBBING
}
// todo custom behavior for fishing logic
private fun tickFishingLogic(pos: BlockPos) {
val serverWorld = world as ServerWorld
var i = 1
val blockPos = pos.up()
if (random.nextFloat() < 0.25f && world.hasRain(blockPos)) {
++i
}
if (random.nextFloat() < 0.5f && !world.isSkyVisible(blockPos)) {
--i
}
if (this.hookCountdown > 0) {
--this.hookCountdown
if (this.hookCountdown <= 0) {
this.waitCountdown = 0
this.fishTravelCountdown = 0
//this.CAUGHT_FISH = false
getDataTracker().set(CAUGHT_FISH, false)
}
} else if (this.fishTravelCountdown > 0) { // create a fish trail that leads to the bobber visually
this.fishTravelCountdown -= i
if (this.fishTravelCountdown > 0) {
this.fishAngle += random.nextTriangular(0.0, 9.188).toFloat()
val f = this.fishAngle * (Math.PI.toFloat() / 180)
val g = MathHelper.sin(f)
val h = MathHelper.cos(f)
val offsetX = this.x + (g * this.fishTravelCountdown.toFloat() * 0.1f).toDouble()
val offsetY = (MathHelper.floor(this.y).toFloat() + 1.0f).toDouble()
val j = this.z + (h * this.fishTravelCountdown.toFloat() * 0.1f).toDouble()
val blockState = serverWorld.getBlockState(BlockPos.ofFloored(offsetX, offsetY - 1.0, j))
//val blockState = serverWorld.getBlockState(BlockPos.ofFloored(offsetX, (MathHelper.floor(this.y).toFloat() + 1.0f).toDouble().also { offsetY = it } - 1.0, this.z + (h * this.fishTravelCountdown.toFloat() * 0.1f).toDouble().also { j = it }))
if (blockState.isOf(Blocks.WATER)) {
if (random.nextFloat() < 0.15f) {
//serverWorld.spawnParticles(ParticleTypes.BUBBLE, this.x, this.y, this.z, 3, g.toDouble(), 0.1, h.toDouble(), 0.0)
serverWorld.spawnParticles(ParticleTypes.BUBBLE, offsetX, offsetY - 0.1, j, 1, g.toDouble(), 0.1, h.toDouble(), 0.0)
}
val k = g * 0.04f
val l = h * 0.04f
serverWorld.spawnParticles(ParticleTypes.FISHING, offsetX, offsetY, j, 0, l.toDouble(), 0.01, -k.toDouble(), 1.0)
serverWorld.spawnParticles(ParticleTypes.FISHING, offsetX, offsetY, j, 0, -l.toDouble(), 0.01, k.toDouble(), 1.0)
}
} else {
//playSound(SoundEvents.ENTITY_FISHING_BOBBER_SPLASH, 0.25f, 1.0f + (random.nextFloat() - random.nextFloat()) * 0.4f)
// play bobber hook notification sound
world.playSound(null, this.blockPos, CobblemonSounds.FISHING_NOTIFICATION, SoundCategory.BLOCKS, 1.0F, 1.0F)
val m = this.y + 0.5
serverWorld.spawnParticles(ParticleTypes.BUBBLE, this.x, m, this.z, (1.0f + this.width * 20.0f).toInt(), this.width.toDouble(), 0.0, this.width.toDouble(), 0.2)
serverWorld.spawnParticles(ParticleTypes.FISHING, this.x, m, this.z, (1.0f + this.width * 20.0f).toInt(), this.width.toDouble(), 0.0, this.width.toDouble(), 0.2)
// check for chance to catch pokemon based on the bait
if (MathHelper.nextInt(random, 0, 100) < getPokemonSpawnChance(bobberBait)) {
this.typeCaught = "POKEMON"
val buckets = Cobblemon.bestSpawner.config.buckets
// choose a spawn bucket according to weights no matter how many there are
chosenBucket = chooseAdjustedSpawnBucket(buckets, luckOfTheSeaLevel)
val reactionMinMax = calculateMinMaxCountdown(chosenBucket.weight)
// set the hook reaction time to be based off the rarity of the bucket chosen
this.hookCountdown = MathHelper.nextInt(random, reactionMinMax.first, reactionMinMax.second)
println("Player hooked a Pokemon from the bucket: " + chosenBucket.name)
}
else {
// todo caught item
this.typeCaught = "ITEM"
this.hookCountdown = MathHelper.nextInt(random, 20, 40)
}
//this.hookCountdown = MathHelper.nextInt(random, 20, 40)
getDataTracker().set(CAUGHT_FISH, true)
//this.CAUGHT_FISH = true
}
} else if (this.waitCountdown > 0) {
this.waitCountdown -= i
var f = 0.15f
if (this.waitCountdown < 20) {
f += (20 - this.waitCountdown).toFloat() * 0.05f
} else if (this.waitCountdown < 40) {
f += (40 - this.waitCountdown).toFloat() * 0.02f
} else if (this.waitCountdown < 60) {
f += (60 - this.waitCountdown).toFloat() * 0.01f
}
if (random.nextFloat() < f) {
val g = MathHelper.nextFloat(this.random, 0.0f, 360.0f) * 0.017453292f
val h = MathHelper.nextFloat(this.random, 25.0f, 60.0f)
val d = this.x + (MathHelper.sin(g) * h).toDouble() * 0.1 // X
val e = (MathHelper.floor(this.y).toFloat() + 1.0f).toDouble() // Y
val j = this.z + (MathHelper.cos(g) * h).toDouble() * 0.1 // randomized Z value
val blockState = serverWorld.getBlockState(BlockPos.ofFloored(d, e - 1.0, j))
if (blockState.isOf(Blocks.WATER)) {
serverWorld.spawnParticles<DefaultParticleType>(ParticleTypes.SPLASH, d, e, j, 2 + random.nextInt(2), 0.10000000149011612, 0.0, 0.10000000149011612, 0.0)
}
}
if (this.waitCountdown <= 0) {
this.fishAngle = MathHelper.nextFloat(random, 0.0f, 360.0f)
this.fishTravelCountdown = MathHelper.nextInt(random, 20, 80)
}
} else {
// set the time it takes to wait for a hooked item or pokemon
this.waitCountdown = MathHelper.nextInt(random, 100, 600)
this.waitCountdown -= this.lureLevel * 20 * 5
// check for the bait on the hook and see if the waitCountdown is reduced
this.waitCountdown = alterBiteTimeAttempt(this.waitCountdown, this.bobberBait)
}
}
private fun removeIfInvalid(player: PlayerEntity): Boolean {
val itemStack = player.mainHandStack
val itemStack2 = player.offHandStack
val bl = Registries.ITEM[this.usedRod] == itemStack.item //(itemStack.item is PokerodItem) // todo make this work again so the line breaks when you swap items
val bl2 = Registries.ITEM[this.usedRod] == itemStack2.item //(itemStack2.item is PokerodItem) // todo make this work again so the line breaks when you swap items
if (player.isRemoved || !player.isAlive || !bl && !bl2 || this.squaredDistanceTo(player) > 1024.0) {
discard()
return true
}
return false
}
private fun checkForCollision() {
val hitResult = ProjectileUtil.getCollision(this) { entity: Entity -> this.canHit(entity) }
onCollision(hitResult)
}
override fun canHit(entity: Entity): Boolean {
return super.canHit(entity) || entity.isAlive && entity is ItemEntity
}
override fun onEntityHit(entityHitResult: EntityHitResult) {
//super.onEntityHit(entityHitResult) // this calls the function in FishingBobberEntity which has null values which causes crash I think
// ProjectileEntity.onEntityHit(entityHitResult) // this is protected and cannot be reached
if (!world.isClient) {
this.updateHookedEntityId(entityHitResult.entity)
}
}
override fun onBlockHit(blockHitResult: BlockHitResult) {
super.onBlockHit(blockHitResult)
this.velocity = velocity.normalize().multiply(blockHitResult.squaredDistanceTo(this))
}
private fun updateHookedEntityId(entity: Entity?) {
this.hookedEntity = entity
getDataTracker().set(HOOK_ENTITY_ID, if (entity == null) 0 else entity.id + 1)
//this.HOOK_ENTITY_ID = if (entity == null) 0 else entity.id + 1
}
override fun tick() {
velocityRandom.setSeed(getUuid().leastSignificantBits xor world.time)
//super.tick()
val playerEntity = this.playerOwner
if (playerEntity == null) {
discard()
} else if (world.isClient || !removeIfInvalid(playerEntity)) {
if (this.isOnGround) {
++removalTimer
if (removalTimer >= 1200) {
discard()
return
}
} else {
removalTimer = 0
}
var f = 0.0f
val blockPos = blockPos
val fluidState = world.getFluidState(blockPos)
if (fluidState.isIn(FluidTags.WATER)) {
f = fluidState.getHeight(world, blockPos)
}
val bl = f > 0.0f
if (state == State.FLYING) {
if (hookedEntity != null) {
velocity = Vec3d.ZERO
state = State.HOOKED_IN_ENTITY
return
}
if (bl) {
velocity = velocity.multiply(0.3, 0.2, 0.3)
state = State.BOBBING
return
}
checkForCollision()
} else {
if (state == State.HOOKED_IN_ENTITY) {
if (hookedEntity != null) {
if (!hookedEntity!!.isRemoved && hookedEntity!!.world.registryKey === world.registryKey) {
this.setPosition(hookedEntity!!.x, hookedEntity!!.getBodyY(0.8), hookedEntity!!.z)
} else {
updateHookedEntityId(null as Entity?)
state = State.FLYING
}
}
return
}
if (state == State.BOBBING) {
val vec3d = velocity
var d = this.y + vec3d.y - blockPos.y.toDouble() - f.toDouble()
if (Math.abs(d) < 0.01) {
d += Math.signum(d) * 0.1
}
this.setVelocity(vec3d.x * 0.9, vec3d.y - d * random.nextFloat().toDouble() * 0.2, vec3d.z * 0.9)
inOpenWater = if (hookCountdown <= 0 && fishTravelCountdown <= 0) {
true
} else {
inOpenWater && outOfOpenWaterTicks < 10 && isOpenOrWaterAround(blockPos)
}
if (bl) {
outOfOpenWaterTicks = Math.max(0, outOfOpenWaterTicks - 1)
if (caughtFish) {
velocity = velocity.add(0.0, -0.1 * velocityRandom.nextFloat().toDouble() * velocityRandom.nextFloat().toDouble(), 0.0)
}
if (!world.isClient) {
tickFishingLogic(blockPos)
}
} else {
outOfOpenWaterTicks = Math.min(10, outOfOpenWaterTicks + 1)
}
}
}
if (!fluidState.isIn(FluidTags.WATER)) {
velocity = velocity.add(0.0, -0.03, 0.0)
}
move(MovementType.SELF, velocity)
this.updateRotation()
if (state == State.FLYING && (this.isOnGround || horizontalCollision)) {
velocity = Vec3d.ZERO
}
velocity = velocity.multiply(0.92)
refreshPosition()
}
}
override fun use(usedItem: ItemStack): Int {
val playerEntity = this.playerOwner
return if (!world.isClient && playerEntity != null && !removeIfInvalid(playerEntity)) {
var i = 0
if (this.hookedEntity != null) {
pullHookedEntity(this.hookedEntity)
Criteria.FISHING_ROD_HOOKED.trigger(playerEntity as ServerPlayerEntity?, usedItem, this, emptyList())
world.sendEntityStatus(this, 31.toByte())
i = if (this.hookedEntity is ItemEntity) 3 else 5
} else if (this.hookCountdown > 0) {
// check if thing caught was an item
if (this.typeCaught == "ITEM") {
val lootContextParameterSet = LootContextParameterSet.Builder(world as ServerWorld).add(LootContextParameters.ORIGIN, pos).add(LootContextParameters.TOOL, usedItem).add(LootContextParameters.THIS_ENTITY, this).luck(this.luckOfTheSeaLevel.toFloat() + playerEntity.luck).build(LootContextTypes.FISHING)
val lootTable = world.server!!.lootManager.getLootTable(CobblemonLootTables.FISHING_GAMEPLAY)
val list: List<ItemStack> = lootTable.generateLoot(lootContextParameterSet)
Criteria.FISHING_ROD_HOOKED.trigger(playerEntity as ServerPlayerEntity?, usedItem, this, list)
val var7: Iterator<*> = list.iterator()
while (var7.hasNext()) {
val itemStack = var7.next() as ItemStack
val itemEntity = ItemEntity(world, this.x, this.y, this.z, itemStack)
val d = playerEntity.getX() - this.x
val e = playerEntity.getY() - this.y
val f = playerEntity.getZ() - this.z
itemEntity.setVelocity(d * 0.1, e * 0.1 + sqrt(sqrt(d * d + e * e + f * f)) * 0.08, f * 0.1)
world.spawnEntity(itemEntity)
playerEntity.getWorld().spawnEntity(ExperienceOrbEntity(playerEntity.getWorld(), playerEntity.getX(), playerEntity.getY() + 0.5, playerEntity.getZ() + 0.5, random.nextInt(6) + 1))
if (itemStack.isIn(ItemTags.FISHES)) {
playerEntity.increaseStat(Stats.FISH_CAUGHT, 1)
}
}
i = 1
}
else { // logic for spawning Pokemon using rarity
println("Player reeled in a Pokemon from the bucket: " + chosenBucket.name)
val bobberOwner = playerOwner as ServerPlayerEntity
// spawn the pokemon from the chosen bucket at the bobber's location
spawnPokemonFromFishing(bobberOwner, chosenBucket, bobberBait)
val serverWorld = world as ServerWorld
val g = MathHelper.nextFloat(random, 0.0f, 360.0f) * (Math.PI.toFloat() / 180)
val h = MathHelper.nextFloat(random, 25.0f, 60.0f)
val partX = this.x + (MathHelper.sin(g) * h).toDouble() * 0.1
serverWorld.spawnParticles(ParticleTypes.SPLASH, partX, this.y, this.z, 6 + random.nextInt(4), 0.0, 0.2, 0.0, 0.0)
// spawn test particle when reeling in pokemon
val particle = Identifier("cobblemon:impact_water")
//SpawnSnowstormParticlePacket(particle, this.pos)
SpawnSnowstormEntityParticlePacket(particle,this.id)
playerEntity.world.spawnEntity(ExperienceOrbEntity(playerEntity.world, playerEntity.x, playerEntity.y + 0.5, playerEntity.z + 0.5, random.nextInt(6) + 1))
}
}
if (this.isOnGround) {
i = 2
}
discard()
i
} else {
0
}
}
// calculate the trajectory for the reeled in pokemon
fun lobPokemonTowardsTarget(player: PlayerEntity, entity: Entity) {
val rad = Math.toRadians(player.yaw.toDouble())
val targetDirection = Vec3d(-sin(rad), 0.0, cos(rad))
val targetPos = player.pos.add(targetDirection.multiply(5.0))
val delta = targetPos.subtract(entity.pos)
val horizontalDistance = sqrt(delta.x * delta.x + delta.z * delta.z)
// Introduce a damping factor that reduces the velocity as the distance increases
val dampingFactor = 1 - (horizontalDistance / 80).coerceIn(0.0, 0.8) // increase end of coerceIn to dampen more
val verticalVelocity = 0.30 // Base vertical velocity for a gentle arc
val horizontalVelocityFactor = 0.13 // Base horizontal velocity factor
// Apply the damping factor to both horizontal and vertical velocities
val adjustedHorizontalVelocity = horizontalDistance * horizontalVelocityFactor * dampingFactor
val adjustedVerticalVelocity = (verticalVelocity + (horizontalDistance * 0.05)) * dampingFactor
// Calculate the final velocities
val velocityX = delta.x / horizontalDistance * adjustedHorizontalVelocity
val velocityZ = delta.z / horizontalDistance * adjustedHorizontalVelocity
val tossVelocity = Vec3d(velocityX, adjustedVerticalVelocity, velocityZ)
entity.velocity = tossVelocity
}
fun spawnPokemonFromFishing(player: PlayerEntity, chosenBucket: SpawnBucket, bobberBait: ItemStack) {
var hookedEntityID: Int? = null
val spawner = BestSpawner.fishingSpawner
//val spawnCause = SpawnCause(spawner = spawner, bucket = spawner.chooseBucket(), entity = spawner.getCauseEntity())
val spawnCause = FishingSpawnCause(
spawner,
chosenBucket,
player,
player.mainHandStack, // Crab, you should probably parse in the rod item connected to the bobber so we can check enchants in spawn conditions
bobberBait
)
val result = spawner.run(spawnCause, world as ServerWorld, pos.toBlockPos())
if (result == null) {
// This has a chance to fail, if the position has no suitability for a fishing context
// it could also just be a miss which
// means two attempts to spawn in the same location can have differing results (which is expected for
// randomness).
player.sendMessage(lang("fishing.nocatch").red())
return
}
var spawnedPokemon: PokemonEntity? = null
val resultingSpawn = result.get()
if (resultingSpawn !is EntitySpawnResult) {
return
}
for (entity in resultingSpawn.entities) {
//pokemon.uuid = it.uuid
hookedEntityID = entity.id
spawnedPokemon = (entity as PokemonEntity)
// todo Check if there was a berry used on the bobber and if so then see how it may affect the pokemon
if (bobberBait != ItemStack.EMPTY) {
// look up how mints affect the pokemon
// remove the bait from the bobber
val playerPokerod = if (this.playerOwner?.getStackInHand(Hand.MAIN_HAND)?.item is PokerodItem) this.playerOwner!!.getStackInHand(Hand.MAIN_HAND).item else this.playerOwner!!.getStackInHand(Hand.OFF_HAND).item
(playerPokerod as PokerodItem).bait = ItemStack.EMPTY
}
if (spawnedPokemon.pokemon.species.weight < 900) { // if weight value of Pokemon is less than 200 lbs (in hectograms) which we store weight as) then reel it in to the player
// play sound for small splash when this weight class is fished up
world.playSound(null, this.blockPos, CobblemonSounds.FISHING_SPLASH_SMALL, SoundCategory.BLOCKS, 1.0F, 1.0F)
// Example of applying the new velocity
lobPokemonTowardsTarget(player, entity)
}
else { // it is a big lad and you cannot reel it in
world.playSound(null, this.blockPos, CobblemonSounds.FISHING_SPLASH_BIG, SoundCategory.BLOCKS, 1.0F, 1.0F)
}
}
// What if it isn't an entity though
hookedEntity = world.getEntityById(hookedEntityID!!)
//val spawnedPokemon = spawnAction.entity
if (spawnedPokemon != null) {
BattleBuilder.pve((player as ServerPlayerEntity), spawnedPokemon).ifErrored { it.sendTo(player) { it.red() } }
}
}
fun checkBaitSuccessRate(successChance: Double): Boolean {
return Math.random() <= successChance
}
// check if the bite time is reduced based on the bait bonus
fun alterBiteTimeAttempt(waitCountdown: Int, stack: ItemStack): Int {
val bait = FishingBaits.getFromItemStack(stack) ?: return waitCountdown
val effect = bait.effects[FishingBait.Effects.BITE_TIME] ?: return waitCountdown
if (!checkBaitSuccessRate(effect.chance)) return waitCountdown
return if (waitCountdown * (effect.value) <= 0)
1 // return min value
else
(waitCountdown * (effect.value)).toInt()
}
// check the chance of a pokemon to spawn and if it is affected by bait
fun getPokemonSpawnChance(stack: ItemStack): Int {
val bait = FishingBaits.getFromItemStack(stack) ?: return this.pokemonSpawnChance
val effect = bait.effects[FishingBait.Effects.POKEMON_CHANCE] ?: return this.pokemonSpawnChance
return if (checkBaitSuccessRate(effect.chance)) {
((effect.chance) * 100).toInt()
} else this.pokemonSpawnChance
}
companion object {
val POKEROD_ID = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.STRING)
val POKEBOBBER_BAIT = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.ITEM_STACK)
val HOOK_ENTITY_ID = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.INTEGER)
private val CAUGHT_FISH = DataTracker.registerData(PokeRodFishingBobberEntity::class.java, TrackedDataHandlerRegistry.BOOLEAN)
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.item.group
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.item.ItemConvertible
import net.minecraft.item.ItemGroup
@@ -210,6 +211,8 @@ object CobblemonItemGroups {
entries.add(CobblemonItems.DARK_GEM)
entries.add(CobblemonItems.STEEL_GEM)
entries.add(CobblemonItems.FAIRY_GEM)
entries.add(CobblemonItems.POKEROD_SMITHING_TEMPLATE)
}
private fun blockEntries(displayContext: DisplayContext, entries: Entries) {
@@ -534,6 +537,7 @@ object CobblemonItemGroups {
private fun pokeballentries(displayContext: DisplayContext, entries: Entries) {
CobblemonItems.pokeBalls.forEach(entries::add)
CobblemonItems.pokeRods.forEach(entries::add)
}
private fun foodInjections(injector: Injector) {
@@ -558,6 +562,7 @@ object CobblemonItemGroups {
injector.putAfter(CobblemonItems.SUSPICIOUS_SHERD, CobblemonItems.NOSTALGIC_SHERD)
injector.putAfter(CobblemonItems.AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE, Items.SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE)
injector.putAfter(CobblemonItems.POKEROD_SMITHING_TEMPLATE, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE)
}
/**
@@ -0,0 +1,109 @@
/*
* 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.item.interactive
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.item.BerryItem
import net.minecraft.client.item.TooltipContext
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.FishingRodItem
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvents
import net.minecraft.stat.Stats
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
class PokerodItem(val pokeRodId: Identifier, settings: Settings?) : FishingRodItem(settings) {
var bait: ItemStack = ItemStack.EMPTY
override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult<ItemStack> {
// if item in mainhand is berry item then don't do anything
if (user.getStackInHand(Hand.MAIN_HAND).item is BerryItem)
return TypedActionResult(ActionResult.FAIL, user.getStackInHand(hand))
val itemStack = user.getStackInHand(hand)
val offHandItem = user.getStackInHand(Hand.OFF_HAND)
if (!world.isClient && user.fishHook == null && FishingBaits.isFishingBait(offHandItem) && !ItemStack.areItemsEqual(offHandItem, bait) && !user.isSneaking) {
// swap baits if one is already on the hook
if (bait != ItemStack.EMPTY) {
user.dropStack(bait) // drop old bait
bait = itemStack // apply new bait
}
// apply it to the rod as bait
bait = offHandItem.item.defaultStack
// decrement 1 stack of that item from the other hand
offHandItem.decrement(1)
}
if (!world.isClient && user.fishHook == null && user.isSneaking) {
// remove bait if one is already on the hook
if (bait != ItemStack.EMPTY) {
user.dropStack(bait) // drop old bait on the ground
bait = ItemStack.EMPTY
}
}
val i: Int
if (user.fishHook != null) { // if the bobber is out yet
if (!world.isClient) {
i = user.fishHook!!.use(itemStack)
itemStack.damage(i, user) { p: PlayerEntity -> p.sendToolBreakStatus(hand) }
}
world.playSound(null as PlayerEntity?, user.x, user.y, user.z, SoundEvents.ENTITY_FISHING_BOBBER_RETRIEVE, SoundCategory.NEUTRAL, 1.0f, 0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f))
user.emitGameEvent(GameEvent.ITEM_INTERACT_FINISH)
} else { // if the bobber is not out yet
world.playSound(null as PlayerEntity?, user.x, user.y, user.z, SoundEvents.ENTITY_FISHING_BOBBER_THROW, SoundCategory.NEUTRAL, 0.5f, 0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f))
if (!world.isClient) {
i = EnchantmentHelper.getLure(itemStack)
val j = EnchantmentHelper.getLuckOfTheSea(itemStack)
val bobberEntity = PokeRodFishingBobberEntity(user, pokeRodId, bait/*Registries.ITEM.getId(bait?.item)*/, world, j, i)
world.spawnEntity(bobberEntity)
CobblemonCriteria.CAST_POKE_ROD.trigger(user as ServerPlayerEntity, !bait.isEmpty)
}
user.incrementStat(Stats.USED.getOrCreateStat(this))
user.emitGameEvent(GameEvent.ITEM_INTERACT_START)
}
return TypedActionResult.success(itemStack, world.isClient())
}
override fun getEnchantability(): Int {
return 1
}
override fun appendTooltip(
stack: ItemStack,
world: World?,
tooltip: MutableList<Text>,
context: TooltipContext
) {
val rod = PokeRods.getPokeRod((stack.item as PokerodItem).pokeRodId) ?: return
val ball = PokeBalls.getPokeBall(rod.pokeBallId) ?: return
tooltip.add(ball.item.name.copy().gray())
super.appendTooltip(stack, world, tooltip, context)
}
override fun getTranslationKey(): String {
return "item.cobblemon.poke_rod"
}
}
@@ -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.loot
import com.cobblemon.mod.common.util.cobblemonResource
object CobblemonLootTables {
val FISHING_GAMEPLAY = cobblemonResource("fishing/pokerod")
}
@@ -54,7 +54,9 @@ object LootInjector {
LootTables.SIMPLE_DUNGEON_CHEST,
LootTables.SPAWN_BONUS_CHEST,
LootTables.STRONGHOLD_CORRIDOR_CHEST,
LootTables.WOODLAND_MANSION_CHEST
LootTables.WOODLAND_MANSION_CHEST,
LootTables.FISHING_TREASURE_GAMEPLAY,
LootTables.FISHING_JUNK_GAMEPLAY
).apply { addAll(villageHouseLootTables) }
/**
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fishing.PokeRod
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
class PokeRodRegistrySyncPacket(rods: Collection<PokeRod>) : DataRegistrySyncPacket<PokeRod, PokeRodRegistrySyncPacket>(rods) {
companion object {
val ID = cobblemonResource("pokerod_sync")
fun decode(buffer: PacketByteBuf) = PokeRodRegistrySyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
override val id = ID
override fun encodeEntry(buffer: PacketByteBuf, entry: PokeRod) {
entry.encode(buffer)
}
override fun decodeEntry(buffer: PacketByteBuf) = PokeRod.decode(buffer)
override fun synchronizeDecoded(entries: Collection<PokeRod>) {
PokeRods.reload(entries.associateBy { it.name!! })
}
}
@@ -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.net.messages.client.fishing
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.net.messages.client.data.DataRegistrySyncPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
class FishingBaitRegistrySyncPacket(fishingBaits: List<FishingBait>) : DataRegistrySyncPacket<FishingBait, FishingBaitRegistrySyncPacket>(fishingBaits) {
companion object {
val ID = cobblemonResource("fishing_baits")
fun decode(buffer: PacketByteBuf) = FishingBaitRegistrySyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
override val id = ID
override fun encodeEntry(buffer: PacketByteBuf, entry: FishingBait) {
buffer.writeIdentifier(entry.item)
}
override fun decodeEntry(buffer: PacketByteBuf): FishingBait {
// server handles the effects so client doesnt need to know them
return FishingBait(buffer.readIdentifier(), emptyMap())
}
override fun synchronizeDecoded(entries: Collection<FishingBait>) {
FishingBaits.reload(entries.associateBy { it.item })
}
}
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.feature
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* A fishing. You know the one.
*
* @author Hiroku
* @since November 25th, 2022
*/
const val FISHED = "fished"
object FishedFeatureHandler {
fun updateFished(pokemon: Pokemon, fished: Boolean?) {
val feature = pokemon.getFeature<FlagSpeciesFeature>(FISHED) ?: return
val currentFished = feature.enabled
if (currentFished != fished && fished != null) {
feature.enabled = fished
pokemon.updateAspects()
pokemon.markFeatureDirty(feature)
}
}
}
@@ -0,0 +1,24 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.pokerod_hook",
"texture_width": 16,
"texture_height": 8,
"visible_bounds_width": 2,
"visible_bounds_height": 1.5,
"visible_bounds_offset": [0, 0.25, 0]
},
"bones": [
{
"name": "pokerod_hook",
"pivot": [0, 0, 0],
"cubes": [
{"origin": [-5, -8, 0], "size": [6, 8, 0], "uv": [0, 0]}
]
}
]
}
]
}
@@ -0,0 +1,155 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.wooper_heart_female",
"texture_width": 64,
"texture_height": 64,
"visible_bounds_width": 3,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "wooper",
"pivot": [0, 0, 0]
},
{
"name": "body",
"parent": "wooper",
"pivot": [0, 4, 0]
},
{
"name": "foot_left",
"parent": "body",
"pivot": [1.75, 1, 0.25],
"cubes": [
{"origin": [0.25, 0, -4.25], "size": [3, 1, 6], "uv": [12, 31]}
]
},
{
"name": "foot_right",
"parent": "body",
"pivot": [-1.75, 1, 0.25],
"cubes": [
{"origin": [-3.25, 0, -4.25], "size": [3, 1, 6], "uv": [0, 27]}
]
},
{
"name": "torso",
"parent": "body",
"pivot": [0, 4, 0],
"cubes": [
{"origin": [0.2, 5.5, -2.01], "size": [1, 1, 0], "inflate": 0.01, "uv": [0, 9]},
{"origin": [-0.5, 5.1, -2.01], "size": [1, 1, 0], "uv": [4, 9]},
{"origin": [-1.2, 5.5, -2.01], "size": [1, 1, 0], "inflate": 0.01, "uv": [2, 9]},
{"origin": [-2.5, 5, -2], "size": [5, 2, 4], "uv": [24, 31]},
{"origin": [-3, 1, -2.5], "size": [6, 4, 5], "uv": [17, 22]},
{"origin": [-2.5, 1.25, -2.525], "size": [5, 2, 0], "uv": [0, 4]},
{"origin": [-1.5, 3.5, -2.525], "size": [3, 1, 0], "uv": [4, 6]}
]
},
{
"name": "tail",
"parent": "torso",
"pivot": [0, 2.25, 2.5],
"cubes": [
{"origin": [-1.5, 1.25, 2], "size": [3, 2, 2], "uv": [0, 0]}
]
},
{
"name": "tail2",
"parent": "tail",
"pivot": [0, 2.25, 4],
"cubes": [
{"origin": [-2.5, 0.75, 4], "size": [5, 3, 6], "uv": [0, 18]}
]
},
{
"name": "head",
"parent": "torso",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-5.5, 7, -5], "size": [11, 8, 10], "uv": [0, 0]}
]
},
{
"name": "mouth_open_wide",
"parent": "head",
"pivot": [0, -3, -3.4],
"cubes": [
{"origin": [-4.5, 7.5, -4.93], "size": [9, 3, 0], "uv": [16, 18]}
]
},
{
"name": "mouth_open_small",
"parent": "head",
"pivot": [-3, -3.25, -3.4],
"cubes": [
{"origin": [-1, 8, -4.93], "size": [2, 3, 0], "uv": [0, 6]}
]
},
{
"name": "mouth_closed",
"parent": "head",
"pivot": [0, -3, -3.5],
"cubes": [
{"origin": [-3.5, 9, -5.025], "size": [7, 1, 0], "uv": [32, 7]},
{"origin": [-4.5, 9.5, -5.025], "size": [9, 1, 0], "uv": [16, 21]}
]
},
{
"name": "lefteye",
"parent": "head",
"pivot": [3.25, 12.25, -5.025],
"cubes": [
{"origin": [2, 11, -5.025], "size": [2, 2, 0], "uv": [0, 22]},
{"origin": [2.5, 11.5, -5.026], "size": [1, 1, 0], "uv": [0, 0]}
]
},
{
"name": "righteye",
"parent": "head",
"pivot": [-3.25, 12.25, -5.025],
"cubes": [
{"origin": [-4, 11, -5.025], "size": [2, 2, 0], "uv": [0, 20]},
{"origin": [-3.5, 11.5, -5.026], "size": [1, 1, 0], "uv": [0, 1]}
]
},
{
"name": "lefteyelid",
"parent": "head",
"pivot": [3.25, 11.75, -4.475],
"cubes": [
{"origin": [2, 11, -4.93], "size": [2, 2, 0], "uv": [0, 18]}
]
},
{
"name": "righteyelid",
"parent": "head",
"pivot": [-3.25, 11.75, -4.425],
"cubes": [
{"origin": [-4, 11, -4.93], "size": [2, 2, 0], "uv": [4, 7]}
]
},
{
"name": "gills_right",
"parent": "head",
"pivot": [-5.5, 10.25, 0],
"cubes": [
{"origin": [-11.5, 6.75, 0], "size": [6, 7, 0], "uv": [12, 38]}
]
},
{
"name": "gills_left",
"parent": "head",
"pivot": [5.5, 10.25, 0],
"cubes": [
{"origin": [5.5, 6.75, 0], "size": [6, 7, 0], "uv": [12, 45]}
]
}
]
}
]
}
@@ -0,0 +1,155 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.wooper_heart_male",
"texture_width": 64,
"texture_height": 64,
"visible_bounds_width": 3,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "wooper",
"pivot": [0, 0, 0]
},
{
"name": "body",
"parent": "wooper",
"pivot": [0, 4, 0]
},
{
"name": "foot_left",
"parent": "body",
"pivot": [1.75, 1, 0.25],
"cubes": [
{"origin": [0.25, 0, -4.25], "size": [3, 1, 6], "uv": [12, 31]}
]
},
{
"name": "foot_right",
"parent": "body",
"pivot": [-1.75, 1, 0.25],
"cubes": [
{"origin": [-3.25, 0, -4.25], "size": [3, 1, 6], "uv": [0, 27]}
]
},
{
"name": "torso",
"parent": "body",
"pivot": [0, 4, 0],
"cubes": [
{"origin": [-2.5, 5, -2], "size": [5, 2, 4], "uv": [24, 31]},
{"origin": [-3, 1, -2.5], "size": [6, 4, 5], "uv": [17, 22]},
{"origin": [0.2, 5.5, -2.01], "size": [1, 1, 0], "inflate": 0.01, "uv": [0, 9]},
{"origin": [-1.2, 5.5, -2.01], "size": [1, 1, 0], "inflate": 0.01, "uv": [2, 9]},
{"origin": [-0.5, 5.1, -2.01], "size": [1, 1, 0], "uv": [4, 9]},
{"origin": [-2.5, 1.25, -2.525], "size": [5, 2, 0], "uv": [0, 4]},
{"origin": [-1.5, 3.5, -2.525], "size": [3, 1, 0], "uv": [4, 6]}
]
},
{
"name": "tail",
"parent": "torso",
"pivot": [0, 2.25, 2.5],
"cubes": [
{"origin": [-1.5, 1.25, 2], "size": [3, 2, 2], "uv": [0, 0]}
]
},
{
"name": "tail2",
"parent": "tail",
"pivot": [0, 2.25, 4],
"cubes": [
{"origin": [-2.5, 0.75, 4], "size": [5, 3, 6], "uv": [0, 18]}
]
},
{
"name": "head",
"parent": "torso",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-5.5, 7, -5], "size": [11, 8, 10], "uv": [0, 0]}
]
},
{
"name": "mouth_open_wide",
"parent": "head",
"pivot": [0, -3, -3.4],
"cubes": [
{"origin": [-4.5, 7.5, -4.93], "size": [9, 3, 0], "uv": [16, 18]}
]
},
{
"name": "mouth_open_small",
"parent": "head",
"pivot": [-3, -3.25, -3.4],
"cubes": [
{"origin": [-1, 8, -4.93], "size": [2, 3, 0], "uv": [0, 6]}
]
},
{
"name": "mouth_closed",
"parent": "head",
"pivot": [0, -3, -3.5],
"cubes": [
{"origin": [-3.5, 9, -5.025], "size": [7, 1, 0], "uv": [32, 7]},
{"origin": [-4.5, 9.5, -5.025], "size": [9, 1, 0], "uv": [16, 21]}
]
},
{
"name": "lefteye",
"parent": "head",
"pivot": [3.25, 12.25, -5.025],
"cubes": [
{"origin": [2, 11, -5.025], "size": [2, 2, 0], "uv": [0, 22]},
{"origin": [2.5, 11.5, -5.026], "size": [1, 1, 0], "uv": [0, 0]}
]
},
{
"name": "righteye",
"parent": "head",
"pivot": [-3.25, 12.25, -5.025],
"cubes": [
{"origin": [-4, 11, -5.025], "size": [2, 2, 0], "uv": [0, 20]},
{"origin": [-3.5, 11.5, -5.026], "size": [1, 1, 0], "uv": [0, 1]}
]
},
{
"name": "lefteyelid",
"parent": "head",
"pivot": [3.25, 11.75, -4.475],
"cubes": [
{"origin": [2, 11, -4.93], "size": [2, 2, 0], "uv": [0, 18]}
]
},
{
"name": "righteyelid",
"parent": "head",
"pivot": [-3.25, 11.75, -4.425],
"cubes": [
{"origin": [-4, 11, -4.93], "size": [2, 2, 0], "uv": [4, 7]}
]
},
{
"name": "gills_right",
"parent": "head",
"pivot": [-5.5, 10.25, 0],
"cubes": [
{"origin": [-11.5, 6.75, 0], "size": [6, 7, 0], "uv": [0, 38]}
]
},
{
"name": "gills_left",
"parent": "head",
"pivot": [5.5, 10.25, 0],
"cubes": [
{"origin": [5.5, 6.75, 0], "size": [6, 7, 0], "uv": [0, 45]}
]
}
]
}
]
}
@@ -0,0 +1,286 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.whiscash",
"texture_width": 256,
"texture_height": 128,
"visible_bounds_width": 12,
"visible_bounds_height": 3.5,
"visible_bounds_offset": [0, 1.25, 0]
},
"bones": [
{
"name": "whiscash",
"pivot": [0, 0, -13]
},
{
"name": "body",
"parent": "whiscash",
"pivot": [0, 12, 0]
},
{
"name": "torso",
"parent": "body",
"pivot": [0, 10.75, 8.75],
"cubes": [
{"origin": [-19, 0, -24], "size": [38, 24, 34], "uv": [0, 0]},
{"origin": [-18, 8, -27], "size": [36, 3, 3], "inflate": 0.01, "uv": [74, 64]}
]
},
{
"name": "whisker_left",
"parent": "torso",
"pivot": [19, 10, -19],
"cubes": [
{"origin": [19, 9, -19], "size": [12, 2, 0], "uv": [232, 56]}
]
},
{
"name": "whisker_left2",
"parent": "whisker_left",
"pivot": [30.5, 10, -19],
"cubes": [
{"origin": [30.5, 9, -19], "size": [12, 2, 0], "uv": [232, 54]}
]
},
{
"name": "whisker_left3",
"parent": "whisker_left2",
"pivot": [42, 10, -19],
"cubes": [
{"origin": [42, 9, -19], "size": [12, 2, 0], "uv": [232, 52]}
]
},
{
"name": "whisker_left4",
"parent": "whisker_left3",
"pivot": [53.5, 10, -19],
"cubes": [
{"origin": [53.5, 9, -19], "size": [12, 2, 0], "uv": [232, 50]}
]
},
{
"name": "whisker_left5",
"parent": "whisker_left4",
"pivot": [65, 10, -19],
"cubes": [
{"origin": [65, 9, -19], "size": [12, 2, 0], "uv": [110, 92]}
]
},
{
"name": "whisker_left6",
"parent": "whisker_left5",
"pivot": [76.5, 10, -19],
"cubes": [
{"origin": [76.5, 2, -19], "size": [11, 9, 0], "uv": [200, 64]}
]
},
{
"name": "whisker_right",
"parent": "torso",
"pivot": [-19, 10, -19],
"cubes": [
{"origin": [-31, 9, -19], "size": [12, 2, 0], "uv": [110, 90]}
]
},
{
"name": "whisker_right2",
"parent": "whisker_right",
"pivot": [-30.5, 10, -19],
"cubes": [
{"origin": [-42.5, 9, -19], "size": [12, 2, 0], "uv": [110, 32]}
]
},
{
"name": "whisker_right3",
"parent": "whisker_right2",
"pivot": [-42, 10, -19],
"cubes": [
{"origin": [-54, 9, -19], "size": [12, 2, 0], "uv": [163, 21]}
]
},
{
"name": "whisker_right4",
"parent": "whisker_right3",
"pivot": [-53.5, 10, -19],
"cubes": [
{"origin": [-65.5, 9, -19], "size": [12, 2, 0], "uv": [0, 31]}
]
},
{
"name": "whisker_right5",
"parent": "whisker_right4",
"pivot": [-65, 10, -19],
"cubes": [
{"origin": [-77, 9, -19], "size": [12, 2, 0], "uv": [0, 11]}
]
},
{
"name": "whisker_right6",
"parent": "whisker_right5",
"pivot": [-76.5, 10, -19],
"cubes": [
{"origin": [-87.5, 2, -19], "size": [11, 9, 0], "uv": [178, 61]}
]
},
{
"name": "fin_left",
"parent": "torso",
"pivot": [19, 5, -7.5],
"cubes": [
{"origin": [19, 5, -13], "size": [7, 0, 11], "uv": [99, 11]}
]
},
{
"name": "fin_left2",
"parent": "fin_left",
"pivot": [26, 5, -7.5],
"cubes": [
{"origin": [26, 5, -13], "size": [7, 0, 11], "uv": [99, 0]}
]
},
{
"name": "fin_right",
"parent": "torso",
"pivot": [-18.5, 5, -7.5],
"cubes": [
{"origin": [-26, 5, -13], "size": [7, 0, 11], "uv": [207, 46]}
]
},
{
"name": "fin_right2",
"parent": "fin_right",
"pivot": [-26, 5, -7.5],
"cubes": [
{"origin": [-33, 5, -13], "size": [7, 0, 11], "uv": [0, 0]}
]
},
{
"name": "jaw",
"parent": "torso",
"pivot": [0, 6.5, -25.5],
"cubes": [
{"origin": [-18, 5, -27], "size": [36, 3, 3], "inflate": 0.01, "uv": [74, 58]}
]
},
{
"name": "dangle_left",
"parent": "jaw",
"pivot": [12.5, 5.5, -25.5],
"cubes": [
{"origin": [9.5, -5.5, -25.5], "size": [6, 12, 0], "inflate": 0.01, "uv": [167, 23]}
]
},
{
"name": "dangle_right",
"parent": "jaw",
"pivot": [-12.5, 5.5, -25.5],
"cubes": [
{"origin": [-15.5, -5.5, -25.5], "size": [6, 12, 0], "inflate": 0.01, "uv": [0, 58]}
]
},
{
"name": "mouth_opening",
"parent": "jaw",
"pivot": [0, -1, -13.1],
"cubes": [
{"origin": [-18, 5, -24.1], "size": [36, 6, 0], "uv": [88, 84]},
{"origin": [15, 5, -27], "size": [3, 6, 3], "uv": [179, 25]},
{"origin": [-18, 5, -27], "size": [3, 6, 3], "uv": [182, 70]}
]
},
{
"name": "eye_left",
"parent": "torso",
"pivot": [16.51, 14.5, -22.01],
"cubes": [
{"origin": [14.01, 11.5, -24.01], "size": [5, 6, 4], "uv": [110, 22]}
]
},
{
"name": "eyelid_left",
"parent": "eye_left",
"pivot": [16.41, 14.5, -21.91],
"cubes": [
{"origin": [13.91, 11.5, -23.91], "size": [5, 6, 4], "inflate": 0.01, "uv": [149, 21]}
]
},
{
"name": "eye_right",
"parent": "torso",
"pivot": [-16.51, 14.5, -22.01],
"cubes": [
{"origin": [-19.01, 11.5, -24.01], "size": [5, 6, 4], "uv": [131, 21]}
]
},
{
"name": "eyelid_right",
"parent": "eye_right",
"pivot": [-16.41, 14.5, -21.91],
"cubes": [
{"origin": [-18.91, 11.5, -23.91], "size": [5, 6, 4], "inflate": 0.01, "uv": [228, 60]}
]
},
{
"name": "fin",
"parent": "torso",
"pivot": [0, 22, 6.5],
"rotation": [-25, 0, 0],
"cubes": [
{"origin": [0, 20, 0], "size": [0, 18, 13], "uv": [0, 0]}
]
},
{
"name": "tail",
"parent": "torso",
"pivot": [0, 9.5, 9],
"cubes": [
{"origin": [-15, 1, 7], "size": [30, 17, 14], "uv": [0, 58]}
]
},
{
"name": "tail2",
"parent": "tail",
"pivot": [0, 7.5, 20],
"cubes": [
{"origin": [-9, 2, 20], "size": [18, 11, 10], "uv": [131, 0]}
]
},
{
"name": "tail3",
"parent": "tail2",
"pivot": [0, 6.5, 29],
"cubes": [
{"origin": [-5, 3.5, 29], "size": [10, 6, 10], "uv": [178, 45]}
]
},
{
"name": "tail4",
"parent": "tail3",
"pivot": [0, 6.5, 38],
"cubes": [
{"origin": [-9.5, 6.5, 36], "size": [19, 0, 14], "uv": [74, 70]}
]
},
{
"name": "fin_left3",
"parent": "tail2",
"pivot": [9, 5, 22],
"cubes": [
{"origin": [9, 5, 22], "size": [7, 0, 7], "uv": [211, 57]}
]
},
{
"name": "fin_right3",
"parent": "tail2",
"pivot": [-9, 5, 22],
"cubes": [
{"origin": [-16, 5, 22], "size": [7, 0, 7], "uv": [161, 45]}
]
}
]
}
]
}
@@ -9,6 +9,198 @@
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp.png",
"layers": []
},
{
"aspects": [
"magikarp-jump-standard"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp.png"
},
{
"aspects": [
"magikarp-jump-apricot-striped"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_apricot_striped.png"
},
{
"aspects": [
"magikarp-jump-apricot-tiger"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_apricot_tiger.png"
},
{
"aspects": [
"magikarp-jump-apricot-zebra"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_apricot_zebra.png"
},
{
"aspects": [
"magikarp-jump-black-forehead"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_black_forehead.png"
},
{
"aspects": [
"magikarp-jump-black-masked"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_black_masked.png"
},
{
"aspects": [
"magikarp-jump-blue-raindrops"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_blue_raindrops.png"
},
{
"aspects": [
"magikarp-jump-blue-saucy"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_blue_saucy.png"
},
{
"aspects": [
"magikarp-jump-brown-striped"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_brown_striped.png"
},
{
"aspects": [
"magikarp-jump-brown-tiger"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_brown_tiger.png"
},
{
"aspects": [
"magikarp-jump-brown-zebra"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_brown_zebra.png"
},
{
"aspects": [
"magikarp-jump-calico-1"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_calico_1.png"
},
{
"aspects": [
"magikarp-jump-calico-2"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_calico_2.png"
},
{
"aspects": [
"magikarp-jump-calico-3"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_calico_3.png"
},
{
"aspects": [
"magikarp-jump-calico-4"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_calico_4.png"
},
{
"aspects": [
"magikarp-jump-dapples"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_dapples.png"
},
{
"aspects": [
"magikarp-jump-gray-bubbles"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_gray_bubbles.png"
},
{
"aspects": [
"magikarp-jump-gray-diamonds"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_gray_diamonds.png"
},
{
"aspects": [
"magikarp-jump-gray-patches"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_gray_patches.png"
},
{
"aspects": [
"magikarp-jump-orange-forehead"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_orange_forehead.png"
},
{
"aspects": [
"magikarp-jump-orange-masked"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_orange_masked.png"
},
{
"aspects": [
"magikarp-jump-orca"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_orca.png"
},
{
"aspects": [
"magikarp-jump-pink-dapples"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_pink_dapples.png"
},
{
"aspects": [
"magikarp-jump-pink-orca"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_pink_orca.png"
},
{
"aspects": [
"magikarp-jump-pink-two_tone"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_pink_two_tone.png"
},
{
"aspects": [
"magikarp-jump-purple-bubbles"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_purple_bubbles.png"
},
{
"aspects": [
"magikarp-jump-purple-diamonds"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_purple_diamonds.png"
},
{
"aspects": [
"magikarp-jump-purple-patches"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_purple_patches.png"
},
{
"aspects": [
"magikarp-jump-skelly"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_skelly.png"
},
{
"aspects": [
"magikarp-jump-two-tone"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_two_tone.png"
},
{
"aspects": [
"magikarp-jump-violet-raindrops"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_violet_raindrops.png"
},
{
"aspects": [
"magikarp-jump-violet-saucy"
],
"texture": "cobblemon:textures/pokemon/0129_magikarp/magikarp_violet_saucy.png"
},
{
"aspects": [
"shiny"
@@ -9,6 +9,12 @@
"texture": "cobblemon:textures/pokemon/0194_wooper/wooper.png",
"layers": []
},
{
"aspects": [
"wooper_heart"
],
"model": "cobblemon:wooper_heart_male.geo"
},
{
"aspects": [
"shiny"
@@ -7,6 +7,12 @@
"female"
],
"model": "cobblemon:wooper_female.geo"
},
{
"aspects": [
"wooper_heart", "female"
],
"model": "cobblemon:wooper_heart_female.geo"
}
]
}
@@ -9,6 +9,16 @@
"texture": "cobblemon:textures/pokemon/0340_whiscash/whiscash.png",
"layers": []
},
{
"aspects": ["whiscash_nero"],
"model": "cobblemon:whiscash_nero.geo",
"texture": "cobblemon:textures/pokemon/0340_whiscash/whiscash_nero.png"
},
{
"aspects": ["whiscash_nero", "shiny"],
"model": "cobblemon:whiscash_nero.geo",
"texture": "cobblemon:textures/pokemon/0340_whiscash/whiscash_nero_shiny.png"
},
{
"aspects": ["shiny"],
"texture": "cobblemon:textures/pokemon/0340_whiscash/whiscash_shiny.png"
@@ -48,6 +48,13 @@
"item.cobblemon.ancient_jet_ball": "Ancient Jet Ball",
"item.cobblemon.ancient_origin_ball": "Ancient Origin Ball",
"item.cobblemon.pokerod_smithing_template": "Poké Rod Smithing Template",
"item.cobblemon.smithing_template.pokerod.ingredients": "Poké Balls",
"item.cobblemon.smithing_template.pokerod.base_slot_description": "Add fishing rod",
"item.cobblemon.smithing_template.pokerod.additions_slot_description": "Add Poké Ball",
"upgrade.cobblemon.pokerod": "Poké Rod Upgrade",
"item.cobblemon.poke_rod": "Poké Rod",
"item.cobblemon.black_apricorn_seed": "Black Apricorn Sprout",
"item.cobblemon.blue_apricorn_seed": "Blue Apricorn Sprout",
"item.cobblemon.green_apricorn_seed": "Green Apricorn Sprout",
@@ -942,7 +949,12 @@
"advancements.cobblemon.grow_tumblestone.description": "Place a Tumblestone near Lava or Magma to grow it over time",
"advancements.cobblemon.craft_ancient_balls": "A true Antiquarian",
"advancements.cobblemon.craft_ancient_balls.description": "Utilize Tumblestones to craft all fifteen Ancient Balls",
"advancements.cobblemon.craft_rod": "Gone fishing",
"advancements.cobblemon.craft_rod.description": "Craft a Poké Rod using a Poké Rod Smithing Template",
"advancements.cobblemon.use_bait": "A Berry Good Lure",
"advancements.cobblemon.use_bait.description": "Use a berry as bait for your Poké Rod",
"advancements.cobblemon.craft_master_rod": "Masterful Dedication",
"advancements.cobblemon.craft_master_rod.description": "Craft a Poké Rod using a Master Ball",
"cobblemon.battle.crash": "A battle error has occurred. If playing single-player, report this to the mod team. Otherwise, report this to the servers administrator.",
"cobblemon.battle.damage_dealt": "%1$s took %2$s damage!",
@@ -1416,6 +1428,8 @@
"cobblemon.battle.mega": "%1$s has Mega Evolved!",
"cobblemon.fishing.nocatch": "Not even a nibble...",
"cobblemon.experience.gained": "%1$s received %2$s experience.",
"cobblemon.experience.level_up": "%1$s is now level %2$s!",
"cobblemon.experience.learned_move": "%1$s has learned %2$s.",
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_azure_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_azure_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_azure_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_azure_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_citrine_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_citrine_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_citrine_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_citrine_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_feather_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_feather_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_feather_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_feather_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_gigaton_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_gigaton_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_gigaton_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_gigaton_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_great_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_great_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_great_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_great_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_heavy_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_heavy_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_heavy_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_heavy_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_ivory_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_ivory_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_ivory_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_ivory_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_jet_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_jet_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_jet_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_jet_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_leaden_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_leaden_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_leaden_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_leaden_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_origin_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_origin_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_origin_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_origin_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_poke_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_poke_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_poke_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_poke_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_roseate_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_roseate_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_roseate_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_roseate_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_slate_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_slate_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_slate_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_slate_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_ultra_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_ultra_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_ultra_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_ultra_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_verdant_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_verdant_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_verdant_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_verdant_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_wing_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/ancient_wing_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/ancient_wing_rod",
"textures": {
"layer0": "cobblemon:item/fishing/ancient_wing_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/azure_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/azure_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/azure_rod",
"textures": {
"layer0": "cobblemon:item/fishing/azure_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/beast_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/beast_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/beast_rod",
"textures": {
"layer0": "cobblemon:item/fishing/beast_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/cherish_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/cherish_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/cherish_rod",
"textures": {
"layer0": "cobblemon:item/fishing/cherish_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/citrine_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/citrine_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/citrine_rod",
"textures": {
"layer0": "cobblemon:item/fishing/citrine_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dive_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/dive_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/dive_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dive_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dream_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/dream_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/dream_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dream_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dusk_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/dusk_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/dusk_rod",
"textures": {
"layer0": "cobblemon:item/fishing/dusk_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/fast_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/fast_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/fast_rod",
"textures": {
"layer0": "cobblemon:item/fishing/fast_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/friend_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/friend_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/friend_rod",
"textures": {
"layer0": "cobblemon:item/fishing/friend_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/great_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/great_rod_cast"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:item/great_rod",
"textures": {
"layer0": "cobblemon:item/fishing/great_rod_cast"
}
}
@@ -0,0 +1,14 @@
{
"parent": "item/handheld_rod",
"textures": {
"layer0": "cobblemon:item/fishing/heal_rod"
},
"overrides": [
{
"predicate": {
"cast": 1
},
"model": "cobblemon:item/heal_rod_cast"
}
]
}

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