mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
319
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
891f984d08 | ||
|
|
bb379764f7 | ||
|
|
3ec78d9d7e | ||
|
|
12973dd66a | ||
|
|
ed30e4cd6f | ||
|
|
32c260446e | ||
|
|
41f1eb699f | ||
|
|
e68a593e9e | ||
|
|
903ffe2143 | ||
|
|
1d6845d595 | ||
|
|
f8eb72e437 | ||
|
|
28a3b8edfe | ||
|
|
96ee23b7d1 | ||
|
|
d3ce829e5b | ||
|
|
4010f138ae | ||
|
|
5100d6c028 | ||
|
|
154ff75a9d | ||
|
|
678c98a5b6 | ||
|
|
d9143788bf | ||
|
|
7e1956f165 | ||
|
|
994a41af78 | ||
|
|
2d5f15a4db | ||
|
|
8442000708 | ||
|
|
695ac93f99 | ||
|
|
e5e5c29c12 | ||
|
|
893277fdec | ||
|
|
60dc881e3d | ||
|
|
f98acf0bc4 | ||
|
|
6e104fdb01 | ||
|
|
05c559e203 | ||
|
|
1323caeeec | ||
|
|
6dc5809622 | ||
|
|
d4416e50b2 | ||
|
|
520bcda956 | ||
|
|
d10c082e67 | ||
|
|
72d608b300 | ||
|
|
2b4dc467ff | ||
|
|
b9390838d0 | ||
|
|
e36bbd98f2 | ||
|
|
c4aa925f39 | ||
|
|
ad5e85ad97 | ||
|
|
5279603432 | ||
|
|
583fba4e77 | ||
|
|
9ee983b2a2 | ||
|
|
16ffbbbd9e | ||
|
|
f23495fe99 | ||
|
|
f61448c333 | ||
|
|
406770eaf1 | ||
|
|
2513fab317 | ||
|
|
3a79e49edb | ||
|
|
87dad0143b | ||
|
|
bdc91fea71 | ||
|
|
eb64666229 | ||
|
|
f7ed0c8799 | ||
|
|
9fde7adb7b | ||
|
|
8b8c07065b | ||
|
|
6d761fef4f | ||
|
|
737ce1314c | ||
|
|
7b89f82d45 | ||
|
|
2203d543a1 | ||
|
|
c129157687 | ||
|
|
276c49ac5c | ||
|
|
f517a63cc9 | ||
|
|
1e2f685ee5 | ||
|
|
5245614804 | ||
|
|
8b97c3cb85 | ||
|
|
d607463116 | ||
|
|
07cc92eb18 | ||
|
|
9665eba7b0 | ||
|
|
9f83c7c72c | ||
|
|
9bb74358f7 | ||
|
|
e2219730e6 | ||
|
|
4daaf50b1a | ||
|
|
b79792ebbf | ||
|
|
7a0b294d3d | ||
|
|
ad1312d077 | ||
|
|
d65d7b779f | ||
|
|
7ce25d8158 | ||
|
|
178d7293fb | ||
|
|
ba0bba2202 | ||
|
|
29d5ce5f0e | ||
|
|
b9ebfc17ed | ||
|
|
2e28792c92 | ||
|
|
648d15403f | ||
|
|
3fe3f521ee | ||
|
|
0ff638a0ca | ||
|
|
485b5f46f6 | ||
|
|
b7c2063510 | ||
|
|
94d7ae5290 | ||
|
|
d75d5da250 | ||
|
|
d3cd1be24e | ||
|
|
d1f11316ec | ||
|
|
1e50bd999b | ||
|
|
0257f6c6a0 | ||
|
|
a3bc1cf72b | ||
|
|
e236632c67 | ||
|
|
4730d97898 | ||
|
|
36994a134d | ||
|
|
97899b3fa9 | ||
|
|
713bd6d6ef | ||
|
|
48736fad1f | ||
|
|
5a47deb9a7 | ||
|
|
4b01facb4f | ||
|
|
158d4d6aed | ||
|
|
ab91776d97 | ||
|
|
286ffe7219 | ||
|
|
79da4cb8a8 | ||
|
|
32f3c75d45 | ||
|
|
acc81d60ad | ||
|
|
4b093a0072 | ||
|
|
e0c57007db | ||
|
|
66b3579a9b | ||
|
|
0981bb71ce | ||
|
|
6b762cd6d3 | ||
|
|
581c466c0e | ||
|
|
5622c65fca | ||
|
|
51c6ea576b | ||
|
|
b7b8fe1fb9 | ||
|
|
1634a20464 | ||
|
|
7190c33dab | ||
|
|
8ea20925d3 | ||
|
|
d4c7ce4eb9 | ||
|
|
625237457a | ||
|
|
0f21173f5f | ||
|
|
7fda4570c6 | ||
|
|
e0aae17f37 | ||
|
|
32b4459e19 | ||
|
|
9072a7ac3c | ||
|
|
0e39874f9a | ||
|
|
aa5d116910 | ||
|
|
1f49d0d21b | ||
|
|
05a612bf79 | ||
|
|
ab0d839006 | ||
|
|
56b061c93d | ||
|
|
ec97e16609 | ||
|
|
e4ce58414c | ||
|
|
b07856ed58 | ||
|
|
b7d2f38490 | ||
|
|
295185c1cf | ||
|
|
f0d8ee21d9 | ||
|
|
b203020b4d | ||
|
|
5962149da8 | ||
|
|
24101bed08 | ||
|
|
321736657e | ||
|
|
e50309871a | ||
|
|
36cd7e426a | ||
|
|
6660747b31 | ||
|
|
c021f28dc7 | ||
|
|
a7a2eceda6 | ||
|
|
a4cac592e6 | ||
|
|
b29c6e0d9c | ||
|
|
ba9ea5b793 | ||
|
|
47d5ac4108 | ||
|
|
c6df8bbb61 | ||
|
|
97055b7ede | ||
|
|
df64ac268e | ||
|
|
541a3e257b | ||
|
|
1a995dfca8 | ||
|
|
9aef6b02ad | ||
|
|
f914f20283 | ||
|
|
03e558efbb | ||
|
|
925a0551b2 | ||
|
|
1677f9fe89 | ||
|
|
1db4c90479 | ||
|
|
e3beeff5a8 | ||
|
|
52a6d84041 | ||
|
|
84f2183f36 | ||
|
|
492cd7bc9e | ||
|
|
c21a25610c | ||
|
|
4eed3c06a7 | ||
|
|
7f804445c4 | ||
|
|
89eee30488 | ||
|
|
b386a20351 | ||
|
|
ac06bc6103 | ||
|
|
cda8697dd9 | ||
|
|
f7f7400163 | ||
|
|
ea0c4731d8 | ||
|
|
204b923289 | ||
|
|
6f34b6347d | ||
|
|
a935edbb55 | ||
|
|
e58c3b9422 | ||
|
|
a5ddc556f3 | ||
|
|
a541cc3e55 | ||
|
|
e355bdd68a | ||
|
|
d687d91090 | ||
|
|
6b38147c23 | ||
|
|
0241dbcfa1 | ||
|
|
b2e641e269 | ||
|
|
63f32cb79c | ||
|
|
7b05868c5a | ||
|
|
c4fd0d6fd7 | ||
|
|
a4f0eb999c | ||
|
|
85b7231ff6 | ||
|
|
0f3d0008e9 | ||
|
|
74addf45c3 | ||
|
|
f97a8a8475 | ||
|
|
e8cc212968 | ||
|
|
7918924c1c | ||
|
|
28a41860b9 | ||
|
|
20ef700422 | ||
|
|
c017863ef8 | ||
|
|
d28ac2facf | ||
|
|
b391f13b11 | ||
|
|
c2f29a0dc3 | ||
|
|
0c4a148c52 | ||
|
|
5c4aa543fa | ||
|
|
d3a9b50c03 | ||
|
|
46962d95fc | ||
|
|
ea50837750 | ||
|
|
b03fe17e8c | ||
|
|
16cf5ecbb7 | ||
|
|
1b34278ec4 | ||
|
|
045d5a8a5e | ||
|
|
5729c9d5be | ||
|
|
a286584c32 | ||
|
|
a5895aaa7c | ||
|
|
0ab6fef566 | ||
|
|
a973d21bc0 | ||
|
|
e149d3f4c8 | ||
|
|
adfde3e236 | ||
|
|
18cdf705ed | ||
|
|
e5730a5119 | ||
|
|
b295cd7bef | ||
|
|
a168715b3c | ||
|
|
55f8212451 | ||
|
|
c5e7c79c2a | ||
|
|
d4a44b4977 | ||
|
|
f9f0668f6b | ||
|
|
d01beb3ba8 | ||
|
|
3cac052772 | ||
|
|
84bc8bc426 | ||
|
|
98a25908c2 | ||
|
|
4ed3d41eb1 | ||
|
|
672fd7365f | ||
|
|
39a16beb14 | ||
|
|
32dbd1f8c9 | ||
|
|
bcc23ec4fc | ||
|
|
143fc8256b | ||
|
|
70d753d1d3 | ||
|
|
00a67b22df | ||
|
|
12811fe504 | ||
|
|
40ffb99928 | ||
|
|
78fb8fb834 | ||
|
|
614663a49c | ||
|
|
ca9955f4cd | ||
|
|
2f40c5d71b | ||
|
|
1a84ca4ba0 | ||
|
|
5f29986d41 | ||
|
|
bfb63088c1 | ||
|
|
626bd7f4f4 | ||
|
|
bff941264c | ||
|
|
10b472e980 | ||
|
|
7888a8ac31 | ||
|
|
d79adfc0be | ||
|
|
e4baf5fe3e | ||
|
|
3013c30084 | ||
|
|
fc10b550cd | ||
|
|
514787a1c4 | ||
|
|
c32f3c5581 | ||
|
|
94b9b7ade3 | ||
|
|
ee33b4292c | ||
|
|
271e74d6f2 | ||
|
|
2ea41c3dae | ||
|
|
a6d93aba17 | ||
|
|
db1711b76f | ||
|
|
ab5b2e8fdf | ||
|
|
efa4028948 | ||
|
|
65529474cb | ||
|
|
7d756249ae | ||
|
|
3fcfd2ff6d | ||
|
|
98545541e0 | ||
|
|
56820ad626 | ||
|
|
64546b25f2 | ||
|
|
d5496fb465 | ||
|
|
bcd2474d5f | ||
|
|
e331b959c8 | ||
|
|
ce159ac742 | ||
|
|
3e4eaf0397 | ||
|
|
2a2308b4a4 | ||
|
|
47511bac89 | ||
|
|
c6a5e69f91 | ||
|
|
32a9185cc1 | ||
|
|
ec54aeb1fd | ||
|
|
9aab1113fd | ||
|
|
86578cd266 | ||
|
|
566d74a638 | ||
|
|
23988b55a1 | ||
|
|
d5418ed5c7 | ||
|
|
5318b1470d | ||
|
|
0ca91eb071 | ||
|
|
7d31719cd1 | ||
|
|
25ed3d28f4 | ||
|
|
5df64d038b | ||
|
|
ec6d838ea6 | ||
|
|
6bea8aafe6 | ||
|
|
5bbf5d2d85 | ||
|
|
6d09590374 | ||
|
|
0e0ebc0b48 | ||
|
|
0c4a4ddda1 | ||
|
|
1efeddc875 | ||
|
|
82c6e8f42d | ||
|
|
5ff522bb85 | ||
|
|
b2b73575d4 | ||
|
|
d7bbccab08 | ||
|
|
21b8fe8474 | ||
|
|
94c210efd4 | ||
|
|
d3100be95a | ||
|
|
3353c15af6 | ||
|
|
1cced11431 | ||
|
|
646ad5606a | ||
|
|
8214e848f9 | ||
|
|
ece9c63121 | ||
|
|
ce81f07aa2 | ||
|
|
9bcaa6dfad | ||
|
|
295efe8c11 | ||
|
|
5cff811aab | ||
|
|
00e721d714 | ||
|
|
0c602027cb | ||
|
|
7070369cea |
@@ -194,6 +194,8 @@ object Cobblemon {
|
||||
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
|
||||
}
|
||||
|
||||
this.loadConfig()
|
||||
|
||||
implementation.registerPermissionValidator()
|
||||
implementation.registerSoundEvents()
|
||||
implementation.registerDataComponents()
|
||||
@@ -216,7 +218,6 @@ object Cobblemon {
|
||||
ExperienceGroups.registerDefaults()
|
||||
CaptureCalculators.registerDefaults()
|
||||
|
||||
this.loadConfig()
|
||||
// CobblemonBlockPredicates.touch()
|
||||
CobblemonOrePlacedFeatures.register()
|
||||
CobblemonPlacedFeatures.register()
|
||||
|
||||
@@ -18,6 +18,7 @@ object CobblemonActivities {
|
||||
val POKEMON_SLEEPING_ACTIVITY = register(Activity("pokemon_sleeping"))
|
||||
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
|
||||
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
|
||||
val GUARD_NEST = register(Activity("guard_nest"))
|
||||
|
||||
fun register(activity: Activity): Activity {
|
||||
activities.add(activity)
|
||||
|
||||
@@ -49,6 +49,11 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK: BlockEntityType<GrottoBlockEntity> = this.create("grotto_block",
|
||||
BlockEntityType.Builder.of(::GrottoBlockEntity, CobblemonBlocks.GROTTO_BLOCK).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
@@ -82,6 +87,46 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
).build(null)
|
||||
)
|
||||
|
||||
val NEST: BlockEntityType<NestBlockEntity> = this.create("nest",
|
||||
BlockEntityType.Builder.of(
|
||||
::NestBlockEntity,
|
||||
CobblemonBlocks.NEST_STICK,
|
||||
CobblemonBlocks.NEST_LEAF,
|
||||
CobblemonBlocks.NEST_STONE,
|
||||
CobblemonBlocks.NEST_DEEPSLATE,
|
||||
CobblemonBlocks.NEST_BASALT,
|
||||
CobblemonBlocks.NEST_SNOW,
|
||||
CobblemonBlocks.NEST_NETHER,
|
||||
CobblemonBlocks.NEST_SOUL_SAND,
|
||||
CobblemonBlocks.NEST_WARM_OCEAN,
|
||||
CobblemonBlocks.NEST_SEAGRASS,
|
||||
CobblemonBlocks.NEST_LUSH,
|
||||
CobblemonBlocks.NEST_HAY,
|
||||
CobblemonBlocks.NEST_SAND,
|
||||
CobblemonBlocks.NEST_RED_SAND,
|
||||
CobblemonBlocks.NEST_CHORUS,
|
||||
|
||||
CobblemonBlocks.WATER_NEST,
|
||||
CobblemonBlocks.CAVE_NEST,
|
||||
CobblemonBlocks.BASIC_NEST
|
||||
).build(null))
|
||||
|
||||
val EGG: BlockEntityType<EggBlockEntity> = this.create("egg",
|
||||
BlockEntityType.Builder.of(
|
||||
::EggBlockEntity,
|
||||
CobblemonBlocks.EGG
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR: BlockEntityType<IncubatorBlockEntity> = this.create("incubator",
|
||||
BlockEntityType.Builder.of(
|
||||
::IncubatorBlockEntity,
|
||||
CobblemonBlocks.INCUBATOR
|
||||
).build(null)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
|
||||
@@ -478,6 +478,61 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
|
||||
|
||||
val NEST_STICK = nestBlock(NestBlock.NestVariant.STICK)
|
||||
val NEST_LEAF = nestBlock(NestBlock.NestVariant.LEAF)
|
||||
val NEST_STONE = nestBlock(NestBlock.NestVariant.STONE)
|
||||
val NEST_DEEPSLATE = nestBlock(NestBlock.NestVariant.DEEPSLATE)
|
||||
val NEST_BASALT = nestBlock(NestBlock.NestVariant.BASALT)
|
||||
val NEST_SNOW = nestBlock(NestBlock.NestVariant.SNOW)
|
||||
|
||||
val NEST_NETHER = this.create(
|
||||
"nest_nether",
|
||||
NestBlock(NestBlock.NestVariant.NETHER, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 2 })
|
||||
)
|
||||
val NEST_SOUL_SAND = nestBlock(NestBlock.NestVariant.SOUL_SAND)
|
||||
val NEST_WARM_OCEAN = this.create(
|
||||
"nest_warm_ocean",
|
||||
NestBlock(NestBlock.NestVariant.WARM_OCEAN, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 6 })
|
||||
)
|
||||
val NEST_SEAGRASS = nestBlock(NestBlock.NestVariant.SEAGRASS)
|
||||
val NEST_LUSH = nestBlock(NestBlock.NestVariant.LUSH)
|
||||
val NEST_HAY = nestBlock(NestBlock.NestVariant.HAY)
|
||||
val NEST_SAND = nestBlock(NestBlock.NestVariant.SAND)
|
||||
val NEST_RED_SAND = nestBlock(NestBlock.NestVariant.RED_SAND)
|
||||
val NEST_CHORUS = nestBlock(NestBlock.NestVariant.CHORUS)
|
||||
|
||||
|
||||
|
||||
val BASIC_NEST = nestBlock(NestBlock.NestVariant.BASIC)
|
||||
val CAVE_NEST = nestBlock(NestBlock.NestVariant.CAVE)
|
||||
//val NETHER_NEST = nestBlock(NestBlock.NestVariant.NETHER)
|
||||
val WATER_NEST = nestBlock(NestBlock.NestVariant.WATER)
|
||||
|
||||
val EGG = this.create("egg", EggBlock(BlockBehaviour.Properties
|
||||
.ofFullCopy(Blocks.DRAGON_EGG)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.sound(SoundType.BONE_BLOCK)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = this.create("grotto_block", GrottoBlock(BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.STONE)
|
||||
.strength(1.5F)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR = this.create("incubator", IncubatorBlock(BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_LIGHT_GRAY)
|
||||
.sound(SoundType.METAL)
|
||||
.strength(Blocks.COAL_BLOCK.defaultDestroyTime(), Blocks.DIAMOND_ORE.explosionResistance)
|
||||
.noOcclusion()
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.lightLevel { state ->
|
||||
if (state.getValue(IncubatorBlock.HAS_EGG)) 5 else 0 // Emit 5 light when HAS_EGG is true
|
||||
}
|
||||
))
|
||||
|
||||
init {
|
||||
/**
|
||||
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
|
||||
@@ -506,6 +561,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
return block
|
||||
}
|
||||
|
||||
private fun nestBlock(variant: NestBlock.NestVariant): NestBlock {
|
||||
val identifier = cobblemonResource(variant.serializedName)
|
||||
val block = NestBlock(variant, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY))
|
||||
return this.create(identifier.path, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls helper method from Vanilla
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,8 @@ object CobblemonCommands {
|
||||
RenameBoxCommand.register(dispatcher)
|
||||
ChangeBoxWallpaperCommand.register(dispatcher)
|
||||
RunMolangScriptCommand.register(dispatcher)
|
||||
GivePokemonEggCommand.register(dispatcher)
|
||||
GiveAllPokemonEggCommand.register(dispatcher)
|
||||
SpectateBattleCommand.register(dispatcher)
|
||||
CobblemonConfigCommand.register(dispatcher)
|
||||
UnlockPCBoxWallpaperCommand.register(dispatcher)
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.HeldItemCapableComponent
|
||||
import com.cobblemon.mod.common.item.components.PokemonItemComponent
|
||||
import com.cobblemon.mod.common.item.RodBaitComponent
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.component.DataComponentType
|
||||
@@ -36,11 +39,18 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(RodBaitComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val EGG_DATA: DataComponentType<EggComponent> = create("egg", DataComponentType.builder<EggComponent>()
|
||||
.persistent(EggComponent.CODEC) // For saving/loading
|
||||
.networkSynchronized(EggComponent.PACKET_CODEC) // For network synchronization
|
||||
.build())
|
||||
|
||||
|
||||
|
||||
|
||||
fun register() {
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:egg_data"), EGG_DATA)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.NestBlock
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
@@ -41,6 +42,7 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.item.Item.Properties
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.food.FoodProperties
|
||||
@@ -392,6 +394,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
|
||||
|
||||
val NEST_STICK = blockItem(NestBlock.NestVariant.STICK.serializedName, CobblemonBlocks.NEST_STICK)
|
||||
val NEST_LEAF = blockItem(NestBlock.NestVariant.LEAF.serializedName, CobblemonBlocks.NEST_LEAF)
|
||||
val NEST_STONE = blockItem(NestBlock.NestVariant.STONE.serializedName, CobblemonBlocks.NEST_STONE)
|
||||
val NEST_DEEPSLATE = blockItem(NestBlock.NestVariant.DEEPSLATE.serializedName, CobblemonBlocks.NEST_DEEPSLATE)
|
||||
val NEST_BASALT = blockItem(NestBlock.NestVariant.BASALT.serializedName, CobblemonBlocks.NEST_BASALT)
|
||||
val NEST_SNOW = blockItem(NestBlock.NestVariant.SNOW.serializedName, CobblemonBlocks.NEST_SNOW)
|
||||
|
||||
val NEST_NETHER = blockItem(NestBlock.NestVariant.NETHER.serializedName, CobblemonBlocks.NEST_NETHER)
|
||||
val NEST_SOUL_SAND = blockItem(NestBlock.NestVariant.SOUL_SAND.serializedName, CobblemonBlocks.NEST_SOUL_SAND)
|
||||
val NEST_WARM_OCEAN = blockItem(NestBlock.NestVariant.WARM_OCEAN.serializedName, CobblemonBlocks.NEST_WARM_OCEAN)
|
||||
val NEST_SEAGRASS = blockItem(NestBlock.NestVariant.SEAGRASS.serializedName, CobblemonBlocks.NEST_SEAGRASS)
|
||||
val NEST_LUSH = blockItem(NestBlock.NestVariant.LUSH.serializedName, CobblemonBlocks.NEST_LUSH)
|
||||
val NEST_HAY = blockItem(NestBlock.NestVariant.HAY.serializedName, CobblemonBlocks.NEST_HAY)
|
||||
val NEST_SAND = blockItem(NestBlock.NestVariant.SAND.serializedName, CobblemonBlocks.NEST_SAND)
|
||||
val NEST_RED_SAND = blockItem(NestBlock.NestVariant.RED_SAND.serializedName, CobblemonBlocks.NEST_RED_SAND)
|
||||
val NEST_CHORUS = blockItem(NestBlock.NestVariant.CHORUS.serializedName, CobblemonBlocks.NEST_CHORUS)
|
||||
|
||||
val BASIC_NEST = blockItem(NestBlock.NestVariant.BASIC.serializedName, CobblemonBlocks.BASIC_NEST)
|
||||
val CAVE_NEST = blockItem(NestBlock.NestVariant.CAVE.serializedName, CobblemonBlocks.CAVE_NEST)
|
||||
val WATER_NEST = blockItem(NestBlock.NestVariant.WATER.serializedName, CobblemonBlocks.WATER_NEST)
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR = blockItem("incubator", CobblemonBlocks.INCUBATOR)
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = blockItem("grotto_block", CobblemonBlocks.GROTTO_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
|
||||
|
||||
@@ -705,7 +734,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
val DESTINY_KNOT = heldItem("destiny_knot", null, if (Cobblemon.config.canDestinyKnotBreak) Properties().durability(Cobblemon.config.destinyKnotDurability) else Properties())
|
||||
@JvmField
|
||||
val DRAGON_FANG = heldItem("dragon_fang")
|
||||
@JvmField
|
||||
@@ -1225,6 +1254,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
@JvmField
|
||||
val POKEMON_EGG = eggItem("egg", CobblemonBlocks.EGG, Item.Properties().stacksTo(1))
|
||||
|
||||
private fun eggItem(name: String, block: Block, settings: Item.Properties = Item.Properties()): BlockItem = this.create(name, BlockItem(block, settings))
|
||||
|
||||
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
|
||||
|
||||
@@ -1268,9 +1301,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
}
|
||||
)
|
||||
|
||||
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
|
||||
private fun heldItem(name: String, remappedName: String? = null, settings:Item.Properties = Item.Properties()): CobblemonItem = create(
|
||||
name,
|
||||
CobblemonItem(Item.Properties()).also {
|
||||
CobblemonItem(settings).also {
|
||||
if (remappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, remappedName)
|
||||
}
|
||||
|
||||
@@ -10,15 +10,18 @@ package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
object CobblemonMemories {
|
||||
val memories = mutableMapOf<String, MemoryModuleType<*>>()
|
||||
@@ -33,6 +36,11 @@ object CobblemonMemories {
|
||||
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
|
||||
val DIALOGUES = register<List<ActiveDialogue>>("npc_dialogues")
|
||||
val ACTIVE_ACTION_EFFECT = register<ActionEffectContext>("active_action_effect")
|
||||
val POTENTIAL_MATE = register<PokemonEntity>("potential_mate")
|
||||
val MATE_SCAN_COOLDOWN = register("mate_scan_cooldown", PrimitiveCodec.BOOL)
|
||||
val NEST_LOCATION = register<BlockPos>("nest_location")
|
||||
val PATERNAL_NEST_LOCATION = register<BlockPos>("paternal_nest")
|
||||
val IRRITATED_AT = register<UUID>("irritated_at")
|
||||
val POKEMON_FLYING = register("pokemon_flying", PrimitiveCodec.BOOL)
|
||||
val POKEMON_DROWSY = register("pokemon_drowsy", PrimitiveCodec.BOOL)
|
||||
val POKEMON_SLEEPING = register("pokemon_sleeping", PrimitiveCodec.BOOL)
|
||||
@@ -41,6 +49,16 @@ object CobblemonMemories {
|
||||
val TARGETED_BATTLE_POKEMON = register<UUID>("targeted_battle_pokemon")
|
||||
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
|
||||
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
|
||||
val EGG_THIEF = register<Player>("egg_thief") // Stores the player stealing the egg
|
||||
val EGG_LOCATION = register<BlockPos>("egg_location") // Stores the last known location of the egg
|
||||
val EGG_IN_NEST = register("egg_in_nest", PrimitiveCodec.BOOL)
|
||||
val READY_TO_GRAB_EGG = register("ready_to_grab_egg", PrimitiveCodec.BOOL)
|
||||
val HOLDING_EGG = register("holding_egg", PrimitiveCodec.BOOL)
|
||||
val EGG_SEARCH_COUNTDOWN = register ("egg_search_countdown", PrimitiveCodec.BOOL)
|
||||
val NESTING_MATERIAL_LOCATION = register<BlockPos>("nesting_material_location")
|
||||
val SENSED_NEST_MATERIAL = register<ItemEntity>("sensed_nest_material")
|
||||
val IDEAL_NEST_LOCATION = register<BlockPos>("ideal_nest_location")
|
||||
val READY_TO_BUILD_NEST = register("ready_to_build_nest", PrimitiveCodec.BOOL)
|
||||
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
|
||||
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
|
||||
@@ -66,6 +66,7 @@ import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
@@ -105,9 +106,9 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
@@ -139,6 +140,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.breeding.TriggerEggHatchPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -166,6 +168,12 @@ import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPa
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
@@ -183,7 +191,9 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.breeding.TriggerEggHatchHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -215,6 +225,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonToPartyHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonToPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
@@ -222,6 +238,7 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -375,6 +392,8 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BreedingRuleRegistrySyncPacket.ID, BreedingRuleRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(HabitatSyncPacket.ID, HabitatSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
@@ -545,6 +564,9 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
|
||||
// Breeding Packets
|
||||
list.add(PacketRegisterInfo(TriggerEggHatchPacket.ID, TriggerEggHatchPacket::decode, TriggerEggHatchHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
|
||||
|
||||
+6
-1
@@ -19,7 +19,7 @@ import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
object CobblemonVillagerPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Registry<PoiType>>, PoiType>() {
|
||||
object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Registry<PoiType>>, PoiType>() {
|
||||
override val registry: Registry<PoiType> = BuiltInRegistries.POINT_OF_INTEREST_TYPE
|
||||
override val resourceKey: ResourceKey<Registry<PoiType>> = Registries.POINT_OF_INTEREST_TYPE
|
||||
|
||||
@@ -27,6 +27,11 @@ object CobblemonVillagerPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKe
|
||||
val NURSE_KEY: ResourceKey<PoiType> = createKey("nurse")
|
||||
@JvmField
|
||||
val NURSE = create(NURSE_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.HEALING_MACHINE), 1, 1))
|
||||
@JvmField
|
||||
val GROTTO_BLOCK_KEY: ResourceKey<PoiType> = createKey("grotto_block")
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = create(GROTTO_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.GROTTO_BLOCK), 0, 1))
|
||||
|
||||
|
||||
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
|
||||
@@ -25,6 +22,11 @@ object CobblemonSensors {
|
||||
|
||||
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
|
||||
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
|
||||
val MATE_SENSOR = register("mate_sensor", ::MateSensor)
|
||||
val NEST_SENSOR = register("nest_sensor", ::NestSensor)
|
||||
val EGG_SENSOR = register("egg_sensor", :: EggSensor)
|
||||
val NEST_MATERIAL_SENSOR = register("nest_material_sensor", :: NestMaterialSensor)
|
||||
val IDEAL_NEST_LOCATION_SENSOR = register("ideal_nest_location_sensor", ::IdealNestPlacementSensor)
|
||||
// val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
|
||||
val POKEMON_DROWSY = register("pokemon_drowsy", ::DrowsySensor)
|
||||
|
||||
|
||||
@@ -84,6 +84,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val POKEDEX_SCAN_ZOOM_INCREMENT = this.create("item.pokedex.scan_zoom_increment")
|
||||
|
||||
@JvmField
|
||||
val BREEDING_EGG_HATCH = this.create("breeding.egg_hatch")
|
||||
|
||||
@JvmField
|
||||
val ITEM_USE = this.create("item.use")
|
||||
@JvmField
|
||||
|
||||
@@ -24,7 +24,7 @@ object CobblemonVillagerProfessions: PlatformRegistry<Registry<VillagerProfessio
|
||||
override val resourceKey: ResourceKey<Registry<VillagerProfession>> = Registries.VILLAGER_PROFESSION
|
||||
|
||||
@JvmField
|
||||
val NURSE = profession(CobblemonVillagerPoiTypes.NURSE_KEY, CobblemonSounds.VILLAGER_WORK_NURSE)
|
||||
val NURSE = profession(CobblemonPoiTypes.NURSE_KEY, CobblemonSounds.VILLAGER_WORK_NURSE)
|
||||
|
||||
/**
|
||||
* Register a villager profession texture override for Cobblemon villagers of the given [VillagerProfession] for specified names.
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.AttackEggThiefTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class AttackEggThiefTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(AttackEggThiefTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class BreedAtNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(BreedAtNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BuildNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class BuildNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(BuildNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GuardNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class GuardNestTaskConfig : SingleTaskConfig {
|
||||
// todo make new variables
|
||||
companion object {
|
||||
const val BREED = "breed"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(GuardNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.PATERNAL_NEST_LOCATION),
|
||||
it.registered(MemoryModuleType.NEAREST_VISIBLE_PLAYER), // if player is invisible they do not patrol
|
||||
it.registered(MemoryModuleType.ANGRY_AT),
|
||||
it.absent(MemoryModuleType.ATTACK_TARGET),
|
||||
it.registered(CobblemonMemories.IRRITATED_AT)
|
||||
).apply(it) { lookTarget, walkTarget, paternalNest, nearestVisiblePlayer, angryAt, attackTarget, irritatedAt ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (paternalNest.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val nestCenter = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// todo maybe cancel task if player is close to nest and set angry at task to that player
|
||||
val nearestPlayerOptional = entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER)
|
||||
val player = nearestPlayerOptional.orElse(null) as? ServerPlayer
|
||||
val irritatedAtTarget = entity.brain.getMemory(CobblemonMemories.IRRITATED_AT)
|
||||
|
||||
if (player != null) {
|
||||
val playerDistanceSquared = player.position().distanceToSqr(nestCenter)
|
||||
|
||||
if (!irritatedAtTarget.isPresent && playerDistanceSquared <= 15 * 15) {
|
||||
// Player is within 15 blocks, set angry and cry
|
||||
entity.cry()
|
||||
|
||||
// set irritated at target to player near egg
|
||||
irritatedAt.set(player.uuid)
|
||||
|
||||
// Display angry particles above parent
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
}
|
||||
|
||||
if (playerDistanceSquared <= 8 * 8) {
|
||||
// Player is within 8 blocks, attack
|
||||
entity.cry()
|
||||
|
||||
// Display angry particles above parent
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
angryAt.set(player.uuid)
|
||||
attackTarget.set(player)
|
||||
return@Trigger false // Cancel the task
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Scan for a valid walkable position within an 8-block sphere
|
||||
val validPosition = findValidWalkablePosition(world, nestCenter, entity, 8)
|
||||
if (validPosition == null) {
|
||||
return@Trigger false // No valid position found
|
||||
}
|
||||
|
||||
// Set the entity's walk and look targets
|
||||
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findValidWalkablePosition(
|
||||
world: ServerLevel,
|
||||
center: Vec3,
|
||||
entity: PathfinderMob,
|
||||
radius: Int
|
||||
): Vec3? {
|
||||
val random = world.random
|
||||
|
||||
// Try a number of random positions within the radius
|
||||
repeat(20) { // Attempt up to 20 random positions
|
||||
val offsetX = random.nextDouble() * 2 * radius - radius
|
||||
val offsetY = random.nextDouble() * 2 * radius - radius
|
||||
val offsetZ = random.nextDouble() * 2 * radius - radius
|
||||
val candidate = center.add(offsetX, offsetY, offsetZ)
|
||||
|
||||
// Convert to block position
|
||||
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
|
||||
|
||||
// Check if the position is walkable and not inside a solid block
|
||||
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
|
||||
entity.navigation.isStableDestination(blockPos)
|
||||
) {
|
||||
return Vec3.atCenterOf(blockPos) // Return the valid position
|
||||
}
|
||||
}
|
||||
|
||||
return null // No valid position found
|
||||
}*/
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
class HuntPlayerTaskConfig : SingleTaskConfig {
|
||||
// todo make new variables
|
||||
companion object {
|
||||
const val BREED = "breed"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(MemoryModuleType.NEAREST_PLAYERS), // this will be null most of the time
|
||||
it.present(MemoryModuleType.ANGRY_AT),
|
||||
it.registered(MemoryModuleType.ATTACK_TARGET),
|
||||
it.registered(MemoryModuleType.DISTURBANCE_LOCATION),
|
||||
).apply(it) { lookTarget, walkTarget, nearestPlayers, angryAt, attackTarget, disturbanceLocation ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
val angryAtUuid = (angryAt.value() as? IdF<UUID?>)?.value() ?: return@Trigger false
|
||||
val disturbancePos = (disturbanceLocation.value() as? IdF<BlockPos>)?.value()
|
||||
|
||||
val players = (nearestPlayers.value() as? IdF<List<Player?>?>)?.value()
|
||||
val targetPlayer = players?.find { it?.uuid == angryAtUuid }
|
||||
|
||||
if (targetPlayer != null) {
|
||||
// If the angry player is nearby, set them as the attack target
|
||||
entity.cry()
|
||||
attackTarget.set(targetPlayer)
|
||||
return@Trigger true
|
||||
}
|
||||
|
||||
// If the angry player is not nearby, search around the Pokémon
|
||||
val maxTicksToSearch = 200L
|
||||
if (time > maxTicksToSearch) {
|
||||
// Timeout after 200 ticks
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// If no disturbance location or it is caused by a nearby player, skip
|
||||
if (disturbancePos != null) {
|
||||
val disturberCausingDisturbance = players?.any {
|
||||
it?.position()?.distanceToSqr(Vec3.atCenterOf(disturbancePos))!! <= 4.0 * 4.0
|
||||
}
|
||||
disturberCausingDisturbance?.let { it1 ->
|
||||
if (!it1) {
|
||||
// Pathfind toward disturbance location
|
||||
walkTarget.set(WalkTarget(Vec3.atCenterOf(disturbancePos), speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(Vec3.atCenterOf(disturbancePos)))
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no valid disturbance location, pathfind randomly within proximity
|
||||
val validPosition = findValidWalkablePosition(world, entity.position(), entity, 8)
|
||||
if (validPosition == null) {
|
||||
return@Trigger false // No valid position found
|
||||
}
|
||||
|
||||
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findValidWalkablePosition(
|
||||
world: ServerLevel,
|
||||
center: Vec3,
|
||||
entity: PathfinderMob,
|
||||
radius: Int
|
||||
): Vec3? {
|
||||
val random = world.random
|
||||
|
||||
// Try a number of random positions within the radius
|
||||
repeat(20) { // Attempt up to 20 random positions
|
||||
val offsetX = random.nextDouble() * 2 * radius - radius
|
||||
val offsetY = random.nextDouble() * 2 * radius - radius
|
||||
val offsetZ = random.nextDouble() * 2 * radius - radius
|
||||
val candidate = center.add(offsetX, offsetY, offsetZ)
|
||||
|
||||
// Convert to block position
|
||||
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
|
||||
|
||||
// Check if the position is walkable and not inside a solid block
|
||||
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
|
||||
entity.navigation.isStableDestination(blockPos)
|
||||
) {
|
||||
return Vec3.atCenterOf(blockPos) // Return the valid position
|
||||
}
|
||||
}
|
||||
|
||||
return null // No valid position found
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToIdealNestLocationTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToIdealNestLocationTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToIdealNestLocationTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToNestMaterialTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToNestMaterialTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpEggTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PickUpEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PickUpEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpNestMaterialTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PickUpNestMaterialTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PickUpNestMaterialTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PlaceEggInNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PlaceEggInNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.RetrieveEggTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class RetrieveEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(RetrieveEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SearchForEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(SearchForEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -76,6 +76,19 @@ interface TaskConfig {
|
||||
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
|
||||
"run_script" to RunScript::class.java,
|
||||
"look_in_direction" to LookInDirectionTaskConfig::class.java,
|
||||
"path_to_nest" to PathToNestTaskConfig::class.java,
|
||||
"path_to_nest_material" to PathToNestMaterialTaskConfig::class.java,
|
||||
"breed_at_nest" to BreedAtNestTaskConfig::class.java,
|
||||
"guard_nest" to GuardNestTaskConfig::class.java,
|
||||
"search_for_egg" to SearchForEggTaskConfig::class.java,
|
||||
"attack_egg_thief" to AttackEggThiefTaskConfig::class.java,
|
||||
"retrieve_egg" to RetrieveEggTaskConfig::class.java,
|
||||
"pick_up_egg" to PickUpEggTaskConfig::class.java,
|
||||
"pick_up_nest_material" to PickUpNestMaterialTaskConfig::class.java,
|
||||
"path_to_ideal_nest_location" to PathToIdealNestLocationTaskConfig::class.java,
|
||||
"place_egg_in_nest" to PlaceEggInNestTaskConfig::class.java,
|
||||
"build_nest" to BuildNestTaskConfig::class.java,
|
||||
"hunt_player" to HuntPlayerTaskConfig::class.java,
|
||||
"wake_up" to WakeUpTaskConfig::class.java,
|
||||
"go_to_sleep" to GoToSleepTaskConfig::class.java,
|
||||
"find_resting_place" to FindRestingPlaceTaskConfig::class.java,
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.cobblemon.mod.common.api.breeding
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.Optional
|
||||
|
||||
data class BreedingRule(
|
||||
val parent1rules: ParentRules,
|
||||
val parent2rules: ParentRules,
|
||||
|
||||
// The resulting baby data (all strings except hasItem will appear in ParentRules)
|
||||
val species: String?,
|
||||
val form: String?,
|
||||
val nature: String?,
|
||||
val ability: String?,
|
||||
|
||||
val moves: Moves,
|
||||
val ivs: Ivs,
|
||||
val gender: String?,
|
||||
|
||||
// If you want a separate pokeball string field:
|
||||
val pokeball: String?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BreedingRule> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
// Parent rules
|
||||
ParentRules.CODEC.fieldOf("parent1rules")
|
||||
.forGetter<BreedingRule> { it.parent1rules },
|
||||
|
||||
ParentRules.CODEC.fieldOf("parent2rules")
|
||||
.forGetter<BreedingRule> { it.parent2rules },
|
||||
|
||||
// ---- All optional string fields for the baby data ----
|
||||
Codec.STRING.optionalFieldOf("species")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.species },
|
||||
|
||||
Codec.STRING.optionalFieldOf("form")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.form },
|
||||
|
||||
Codec.STRING.optionalFieldOf("nature")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.nature },
|
||||
|
||||
Codec.STRING.optionalFieldOf("ability")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.ability },
|
||||
|
||||
// Moves & IVs can be empty objects but must exist in the JSON
|
||||
Moves.CODEC.fieldOf("moves")
|
||||
.forGetter<BreedingRule> { it.moves },
|
||||
|
||||
Ivs.CODEC.fieldOf("ivs")
|
||||
.forGetter<BreedingRule> { it.ivs },
|
||||
|
||||
Codec.STRING.optionalFieldOf("gender")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.gender },
|
||||
|
||||
// If you want pokeball to be a string instead of ResourceLocation
|
||||
Codec.STRING.optionalFieldOf("pokeball")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.pokeball }
|
||||
|
||||
).apply(builder, ::BreedingRule)
|
||||
}
|
||||
}
|
||||
|
||||
data class ParentRules(
|
||||
val species: String?,
|
||||
val form: String?,
|
||||
val ability: String?,
|
||||
val gender: String?,
|
||||
|
||||
// hasMove is also just a string now
|
||||
val hasMove: String?,
|
||||
|
||||
// hasItem stays as a ResourceLocation?
|
||||
val hasItem: ResourceLocation?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<ParentRules> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.optionalFieldOf("species")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.species },
|
||||
|
||||
Codec.STRING.optionalFieldOf("form")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.form },
|
||||
|
||||
Codec.STRING.optionalFieldOf("ability")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.ability },
|
||||
|
||||
Codec.STRING.optionalFieldOf("gender")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.gender },
|
||||
|
||||
Codec.STRING.optionalFieldOf("hasMove")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.hasMove },
|
||||
|
||||
// Keep hasItem as a ResourceLocation
|
||||
ResourceLocation.CODEC.optionalFieldOf("hasItem")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.hasItem }
|
||||
|
||||
).apply(builder, ::ParentRules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Moves(
|
||||
val move1: String?,
|
||||
val move2: String?,
|
||||
val move3: String?,
|
||||
val move4: String?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Moves> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.optionalFieldOf("move1")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move1 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move2")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move2 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move3")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move3 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move4")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move4 }
|
||||
|
||||
).apply(builder, ::Moves)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Ivs(
|
||||
val hp: Int?,
|
||||
val attack: Int?,
|
||||
val defence: Int?,
|
||||
val special_attack: Int?,
|
||||
val special_defence: Int?,
|
||||
val speed: Int?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Ivs> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.INT.optionalFieldOf("hp")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.hp },
|
||||
|
||||
Codec.INT.optionalFieldOf("attack")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.attack },
|
||||
|
||||
Codec.INT.optionalFieldOf("defence")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.defence },
|
||||
|
||||
Codec.INT.optionalFieldOf("special_attack")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.special_attack },
|
||||
|
||||
Codec.INT.optionalFieldOf("special_defence")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.special_defence },
|
||||
|
||||
Codec.INT.optionalFieldOf("speed")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.speed }
|
||||
).apply(builder, ::Ivs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.cobblemon.mod.common.api.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object BreedingRules : JsonDataRegistry<BreedingRule> {
|
||||
override val id = cobblemonResource("breeding_rules")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<BreedingRules>()
|
||||
override val typeToken: TypeToken<BreedingRule> = TypeToken.get(BreedingRule::class.java)
|
||||
override val resourcePath = "breeding_rules"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val registry = mutableMapOf<ResourceLocation, BreedingRule>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// Send the entire list of BreedingRule entries to the player
|
||||
BreedingRuleRegistrySyncPacket(this.registry.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, BreedingRule>) {
|
||||
// Clear existing data and load new data from JSON
|
||||
registry.clear()
|
||||
data.forEach { (id, breedingRule) ->
|
||||
registry[id] = breedingRule
|
||||
}
|
||||
println("Loaded Breeding Rules: ${registry.size}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a single BreedingRule that matches the given mother/father conditions.
|
||||
* Returns the first matching BreedingRule, or null if no match is found.
|
||||
*/
|
||||
fun getBreedingRule(mother: Pokemon, father: Pokemon): BreedingRule? {
|
||||
// 1. Prepare the actual ParentRules objects for mother/father.
|
||||
val motherRules = createParentRules(mother)
|
||||
val fatherRules = createParentRules(father)
|
||||
|
||||
// 2. Find all matching breeding rules in the registry.
|
||||
// A rule matches if parent1rules & parent2rules match the mother/father conditions.
|
||||
// If multiple rules match, we just return the first (or you could randomize).
|
||||
val matchingRules = registry.values.firstOrNull { rule ->
|
||||
rule.species != null && (
|
||||
(checkParentRules(rule.parent1rules, motherRules) && checkParentRules(rule.parent2rules, fatherRules)) ||
|
||||
(checkParentRules(rule.parent1rules, fatherRules) && checkParentRules(rule.parent2rules, motherRules))
|
||||
)
|
||||
}
|
||||
|
||||
// 3. If none match, return null. Otherwise return the first match.
|
||||
return matchingRules
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a BreedingRule.ParentRules object from an actual Pokémon instance.
|
||||
* Adjust as needed if you'd like more robust checking (e.g. multiple moves, alt forms, etc.).
|
||||
*/
|
||||
private fun createParentRules(parent: Pokemon): BreedingRule.ParentRules {
|
||||
// NOTE: Adjust these calls if your Pokemon class has different ways to get form/ability
|
||||
val speciesId = parent.species.name.lowercase()
|
||||
val formId = parent.form.name.lowercase() // If you store form as a ResourceLocation
|
||||
val abilityId = parent.ability.name.lowercase() // If you store ability as a ResourceLocation
|
||||
|
||||
return BreedingRule.ParentRules(
|
||||
species = speciesId,
|
||||
form = formId,
|
||||
ability = abilityId,
|
||||
gender = parent.gender.name.lowercase(),
|
||||
hasMove = parent.moveSet.getMoves().firstOrNull()?.template?.name,
|
||||
hasItem = parent.heldItemNoCopy().item.let {
|
||||
BuiltInRegistries.ITEM.getKey(it) // If empty, might be minecraft:air
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a parent's actual rules match the expected parent rules from the data.
|
||||
*/
|
||||
private fun checkParentRules(
|
||||
expected: BreedingRule.ParentRules,
|
||||
actual: BreedingRule.ParentRules
|
||||
): Boolean {
|
||||
return (expected.species == null || expected.species == actual.species) &&
|
||||
(expected.form == null || expected.form == actual.form) &&
|
||||
(expected.ability == null || expected.ability == actual.ability) &&
|
||||
(expected.gender == null || expected.gender == actual.gender) &&
|
||||
(expected.hasMove == null || expected.hasMove == actual.hasMove) &&
|
||||
(expected.hasItem == null || expected.hasItem == actual.hasItem)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingEligibilityEvent
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingResultEvent
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
@@ -262,6 +264,12 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
|
||||
|
||||
//Breeding
|
||||
@JvmField
|
||||
val BREEDING_RESULT = EventObservable<BreedingResultEvent>()
|
||||
@JvmField
|
||||
val BREEDING_ELIGIBLE = EventObservable<BreedingEligibilityEvent>()
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
//
|
||||
// Riding
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.breeding
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
data class BreedingEligibilityEvent(
|
||||
val canBreed: Boolean,
|
||||
val mother: Pokemon,
|
||||
val father: Pokemon
|
||||
)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.BreedingResult
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
data class BreedingResultEvent(
|
||||
val breedingResult: BreedingResult,
|
||||
val mother: Pokemon,
|
||||
val father: Pokemon
|
||||
)
|
||||
+1
-1
@@ -23,7 +23,7 @@ import net.minecraft.nbt.CompoundTag
|
||||
* reload.
|
||||
*
|
||||
* @property pokemonEntity The Pokémon entity being saved.
|
||||
* @property nbt The [NbtCompound] that the Pokémon is being saved to.
|
||||
* @property nbt The [CompoundTag] that the Pokémon is being saved to.
|
||||
* You should add or remove from this for whatever you
|
||||
* want to accomplish. The existing contents will be all
|
||||
* of what Cobblemon saves natively.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
data class Habitat(
|
||||
val structure: ResourceLocation,
|
||||
val spawnPool: List<WeightedGroup>
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Habitat> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("structure").forGetter { it.structure },
|
||||
WeightedGroup.CODEC.listOf().fieldOf("spawnPool").forGetter { it.spawnPool }
|
||||
).apply(instance, ::Habitat)
|
||||
}
|
||||
|
||||
val STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC)
|
||||
|
||||
val BLANK_HABITAT = Habitat(
|
||||
structure = cobblemonResource("blank"),
|
||||
spawnPool = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
fun flatten(): List<ResourceLocation> {
|
||||
return spawnPool.flatMap { it.spawns.map { s -> s.species } }
|
||||
}
|
||||
}
|
||||
|
||||
data class WeightedGroup(
|
||||
val weight: Int = 1,
|
||||
val spawns: List<WeightedSpecies>
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<WeightedGroup> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight },
|
||||
WeightedSpecies.CODEC.listOf().fieldOf("spawns").forGetter { it.spawns }
|
||||
).apply(instance, ::WeightedGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WeightedSpecies(
|
||||
val species: ResourceLocation,
|
||||
val weight: Int = 1
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<WeightedSpecies> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("species").forGetter { it.species },
|
||||
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight }
|
||||
).apply(instance, ::WeightedSpecies)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.HabitatSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object Habitats : JsonDataRegistry<Habitat> {
|
||||
override val id = cobblemonResource("habitats")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<Habitats>()
|
||||
override val typeToken: TypeToken<Habitat> = TypeToken.get(Habitat::class.java)
|
||||
override val resourcePath = "habitats"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val table = mutableMapOf<ResourceLocation, Habitat>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
HabitatSyncPacket(table.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Habitat>) {
|
||||
table.clear()
|
||||
table.putAll(data)
|
||||
|
||||
println("[Habitats] Reloaded ${table.size} habitat(s):")
|
||||
for ((structureId, habitat) in table) {
|
||||
println(" - ${structureId}:")
|
||||
if (habitat.spawnPool.isEmpty()) {
|
||||
println(" (No spawn entries)")
|
||||
} else {
|
||||
habitat.spawnPool.forEachIndexed { i, group ->
|
||||
println(" • Group $i (weight: ${group.weight}):")
|
||||
group.spawns.forEach { entry ->
|
||||
println(" - ${entry.species} (weight: ${entry.weight})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getHabitat(structureId: ResourceLocation): Habitat? {
|
||||
return table[structureId]
|
||||
}
|
||||
|
||||
fun getSpawnOptions(structureId: ResourceLocation): List<ResourceLocation> {
|
||||
return table[structureId]?.flatten() ?: emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.cobblemon.mod.common.api.nest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
|
||||
import com.cobblemon.mod.common.block.NestBlock
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.BiomeTags
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
|
||||
/**
|
||||
* Contains the different Nest variants and util methods to get their item, block, biome, and materials from.
|
||||
*
|
||||
* @author Plastered_Crab
|
||||
* @since Feb 7th, 2025
|
||||
*/
|
||||
enum class Nest : StringRepresentable {
|
||||
|
||||
STICK,
|
||||
LEAF,
|
||||
STONE,
|
||||
DEEPSLATE,
|
||||
SNOW,
|
||||
LUSH,
|
||||
SEAGRASS,
|
||||
WARM_OCEAN,
|
||||
SAND,
|
||||
RED_SAND,
|
||||
NETHER,
|
||||
BASALT,
|
||||
SOUL_SAND,
|
||||
CHORUS;
|
||||
//HAY;
|
||||
|
||||
// Crab Note: todo maybe we want these to be a List of BlockItems?
|
||||
fun item(): BlockItem = when(this) {
|
||||
STICK -> CobblemonItems.NEST_STICK
|
||||
LEAF -> CobblemonItems.NEST_LEAF
|
||||
STONE -> CobblemonItems.NEST_STONE
|
||||
DEEPSLATE -> CobblemonItems.NEST_DEEPSLATE
|
||||
SNOW -> CobblemonItems.NEST_SNOW
|
||||
LUSH -> CobblemonItems.NEST_LUSH
|
||||
SEAGRASS -> CobblemonItems.NEST_SEAGRASS
|
||||
WARM_OCEAN -> CobblemonItems.NEST_WARM_OCEAN
|
||||
SAND -> CobblemonItems.NEST_SAND
|
||||
RED_SAND -> CobblemonItems.NEST_RED_SAND
|
||||
NETHER -> CobblemonItems.NEST_NETHER
|
||||
BASALT -> CobblemonItems.NEST_BASALT
|
||||
SOUL_SAND -> CobblemonItems.NEST_SOUL_SAND
|
||||
CHORUS -> CobblemonItems.NEST_CHORUS
|
||||
//HAY -> CobblemonItems.NEST_HAY
|
||||
}
|
||||
|
||||
// todo make a map of ideal placement blocks for each nest
|
||||
fun placementBlocks(): List<Block> = when (this) {
|
||||
STICK -> listOf(Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.PODZOL, Blocks.COARSE_DIRT)
|
||||
LEAF -> listOf(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.BIRCH_LEAVES, Blocks.ACACIA_LEAVES, Blocks.AZALEA_LEAVES, Blocks.MANGROVE_LEAVES, Blocks.CHERRY_LEAVES)
|
||||
STONE -> listOf(Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.GRANITE, Blocks.DRIPSTONE_BLOCK, Blocks.COBBLESTONE, Blocks.ICE, Blocks.PACKED_ICE, Blocks.BLUE_ICE)
|
||||
DEEPSLATE -> listOf(Blocks.DEEPSLATE, Blocks.COBBLED_DEEPSLATE)
|
||||
SNOW -> listOf(Blocks.SNOW_BLOCK, Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.COARSE_DIRT) // todo dirt, grass, and coarse dirt ONLY if replacing the location of snowlayer
|
||||
LUSH -> listOf(Blocks.MOSS_BLOCK, Blocks.CLAY)
|
||||
SEAGRASS -> listOf(Blocks.DIRT, Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.MUD, Blocks.CLAY, Blocks.ANDESITE, Blocks.SAND, Blocks.GRAVEL) // todo (all only if surrounded by water AND not in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
|
||||
WARM_OCEAN -> listOf(Blocks.SAND, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.FIRE_CORAL_BLOCK, Blocks.HORN_CORAL_BLOCK, Blocks.TUBE_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BUBBLE_CORAL_BLOCK) // todo (all only if surrounded by water AND only when in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
|
||||
SAND -> listOf(Blocks.SAND, Blocks.SANDSTONE, Blocks.SMOOTH_SANDSTONE) // todo (only if NOT surrounded by water)
|
||||
RED_SAND -> listOf(Blocks.RED_SAND, Blocks.RED_SANDSTONE, Blocks.SMOOTH_RED_SANDSTONE)
|
||||
NETHER -> listOf(Blocks.NETHERRACK, Blocks.WARPED_NYLIUM, Blocks.CRIMSON_NYLIUM, Blocks.NETHER_WART_BLOCK, Blocks.WARPED_WART_BLOCK, Blocks.MAGMA_BLOCK)
|
||||
BASALT -> listOf(Blocks.BASALT, Blocks.BLACKSTONE, Blocks.SMOOTH_BASALT)
|
||||
SOUL_SAND -> listOf(Blocks.SOUL_SAND, Blocks.SOUL_SOIL)
|
||||
CHORUS -> listOf(Blocks.END_STONE, Blocks.PURPUR_BLOCK, Blocks.END_STONE_BRICKS)
|
||||
//HAY -> listOf(Blocks.HAY_BLOCK, Blocks.GRASS_BLOCK)
|
||||
}
|
||||
|
||||
// todo make a map or something for each nest that can be waterlogged and not
|
||||
/**
|
||||
* Determines if a nest can be waterlogged.
|
||||
* Some nests, like those made of water-related materials, can be placed in water.
|
||||
*/
|
||||
fun isWaterloggable(): Boolean = when (this) {
|
||||
SEAGRASS, WARM_OCEAN -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this nest type can be placed based on **biome tags**.
|
||||
*/
|
||||
fun isValidBiome(world: ServerLevel, pos: BlockPos): Boolean {
|
||||
val biomeHolder: Holder<Biome> = world.getBiome(pos)
|
||||
|
||||
return when (this) {
|
||||
STICK -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STICK_NEST)
|
||||
LEAF -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LEAF_NEST_TREES)
|
||||
STONE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STONE_NEST)
|
||||
DEEPSLATE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_DEEPSLATE_NEST_CAVES)
|
||||
SNOW -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SNOW_NEST)
|
||||
LUSH -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LUSH_NEST_CAVES)
|
||||
SEAGRASS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SEAGRASS_NEST)
|
||||
WARM_OCEAN -> biomeHolder.`is`(CobblemonBiomeTags.HAS_WARM_OCEAN_NEST)
|
||||
SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SAND_NEST)
|
||||
RED_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_RED_SAND_NEST)
|
||||
NETHER -> biomeHolder.`is`(CobblemonBiomeTags.HAS_NETHER_NEST)
|
||||
BASALT -> biomeHolder.`is`(CobblemonBiomeTags.HAS_BASALT_NEST)
|
||||
SOUL_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SOUL_SAND_NEST)
|
||||
CHORUS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_CHORUS_NEST)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Crab Note: todo maybe we want these to be a List of Items?
|
||||
fun materials(): Item = when(this) {
|
||||
STICK -> Items.STICK
|
||||
LEAF -> Items.OAK_SAPLING
|
||||
STONE -> Items.COBBLESTONE
|
||||
DEEPSLATE -> Items.COBBLED_DEEPSLATE
|
||||
SNOW -> Items.SNOWBALL
|
||||
LUSH -> Items.MOSS_BLOCK
|
||||
SEAGRASS -> Items.KELP
|
||||
WARM_OCEAN -> Items.SEA_PICKLE
|
||||
SAND -> Items.SAND
|
||||
RED_SAND -> Items.RED_SAND
|
||||
NETHER -> Items.WARPED_ROOTS
|
||||
BASALT -> Items.BASALT
|
||||
SOUL_SAND -> Items.SOUL_SAND
|
||||
CHORUS -> Items.CHORUS_FRUIT
|
||||
//HAY -> Items.WHEAT
|
||||
}
|
||||
|
||||
// Crab Note: todo maybe we want to redo this if we have lists of items needed for each?
|
||||
/*fun craftedNestResult(itemStack: ItemStack): BlockItem? = when(itemStack) {
|
||||
ItemStack(Items.STICK, 4) -> CobblemonItems.NEST_STICK
|
||||
ItemStack(Items.OAK_SAPLING, 4) -> CobblemonItems.NEST_LEAF
|
||||
ItemStack(Items.COBBLESTONE, 4) -> CobblemonItems.NEST_STONE
|
||||
ItemStack(Items.COBBLED_DEEPSLATE, 4) -> CobblemonItems.NEST_DEEPSLATE
|
||||
ItemStack(Items.SNOWBALL, 4) -> CobblemonItems.NEST_SNOW
|
||||
ItemStack(Items.MOSS_BLOCK, 4) -> CobblemonItems.NEST_LUSH
|
||||
ItemStack(Items.KELP, 4) -> CobblemonItems.NEST_SEAGRASS
|
||||
ItemStack(Items.SEA_PICKLE, 4) -> CobblemonItems.NEST_WARM_OCEAN
|
||||
ItemStack(Items.SAND, 4) -> CobblemonItems.NEST_SAND
|
||||
ItemStack(Items.RED_SAND, 4) -> CobblemonItems.NEST_RED_SAND
|
||||
ItemStack(Items.WARPED_ROOTS, 4) -> CobblemonItems.NEST_NETHER
|
||||
ItemStack(Items.BASALT, 4) -> CobblemonItems.NEST_BASALT
|
||||
ItemStack(Items.SOUL_SAND, 4) -> CobblemonItems.NEST_SOUL_SAND
|
||||
ItemStack(Items.CHORUS_FRUIT, 4) -> CobblemonItems.NEST_CHORUS
|
||||
ItemStack(Items.WHEAT, 4) -> CobblemonItems.NEST_HAY
|
||||
else -> null
|
||||
}*/
|
||||
|
||||
override fun getSerializedName() = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<Nest> = StringRepresentable.fromValues(::values)
|
||||
|
||||
private val materialRequirements: Map<Item, Int> = mapOf(
|
||||
Items.STICK to 2,
|
||||
Items.OAK_SAPLING to 2,
|
||||
Items.COBBLESTONE to 2,
|
||||
Items.COBBLED_DEEPSLATE to 2,
|
||||
Items.SNOWBALL to 2,
|
||||
Items.MOSS_BLOCK to 2,
|
||||
Items.KELP to 2,
|
||||
Items.SEA_PICKLE to 2,
|
||||
Items.SAND to 2,
|
||||
Items.RED_SAND to 2,
|
||||
Items.WARPED_ROOTS to 2,
|
||||
Items.BASALT to 2,
|
||||
Items.SOUL_SAND to 2,
|
||||
Items.CHORUS_FRUIT to 2,
|
||||
//Items.WHEAT to 2
|
||||
)
|
||||
|
||||
/**
|
||||
* Determines if a held stack is sufficient to craft a nest and returns the crafted item and leftover count.
|
||||
* @param heldStack The item stack in the Pokémon's hand.
|
||||
* @return A Pair of (crafted nest block item, remaining materials in hand) or null if crafting is not possible.
|
||||
*/
|
||||
fun craftedNestResult(heldStack: ItemStack): Pair<BlockItem, Int>? {
|
||||
val requiredAmount = materialRequirements[heldStack.item] ?: return null // Check if item is a valid material
|
||||
|
||||
// Ensure Pokémon has enough materials
|
||||
if (heldStack.count >= requiredAmount) {
|
||||
val remaining = heldStack.count - requiredAmount
|
||||
|
||||
// Find the nest type that matches the material
|
||||
val matchingNest = entries.firstOrNull { it.materials() == heldStack.item } ?: return null
|
||||
|
||||
return Pair(matchingNest.item(), remaining)
|
||||
}
|
||||
|
||||
return null // Not enough materials
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
|
||||
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
|
||||
@@ -59,7 +59,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
//.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
|
||||
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
|
||||
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
|
||||
|
||||
@@ -31,6 +31,9 @@ object CobblemonPermissions {
|
||||
val GIVE_POKEMON_SELF = this.create("${GIVE_POKEMON_BASE}.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val GIVE_POKEMON_OTHER = this.create("${GIVE_POKEMON_BASE}.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
val GIVE_POKEMON_EGG = this.create("${COMMAND_PREFIX}givepokemonegg", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
private const val HEAL_POKEMON_BASE = "${COMMAND_PREFIX}healpokemon"
|
||||
@JvmStatic
|
||||
val HEAL_POKEMON_SELF = this.create("$HEAL_POKEMON_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@@ -110,7 +110,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
@@ -186,6 +186,22 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
*/
|
||||
fun getByIdentifier(identifier: ResourceLocation) = this.speciesByIdentifier[identifier]
|
||||
|
||||
/**
|
||||
* Finds the first [Species] in an evolution line
|
||||
*
|
||||
* @param species The [Species] evolution line to use
|
||||
* @return The [Species] if existing.
|
||||
*/
|
||||
fun getFirstEvolution(species: Species): Species {
|
||||
var returnSpecies = species
|
||||
|
||||
while (returnSpecies.preEvolution != null) {
|
||||
returnSpecies = returnSpecies.preEvolution!!.species
|
||||
}
|
||||
|
||||
return returnSpecies
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the currently loaded species.
|
||||
*
|
||||
|
||||
+599
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
|
||||
import com.cobblemon.mod.common.api.breeding.BreedingRule
|
||||
import com.cobblemon.mod.common.api.breeding.BreedingRules
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.*
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.Holder
|
||||
import kotlin.random.Random
|
||||
|
||||
interface BreedingLogic {
|
||||
|
||||
/**
|
||||
* Takes two [Pokemon] and returns a [BreedingResult].
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return The [BreedingResult] of the two [Pokemon]
|
||||
* @author Apion, Nick, whatsy, plastered_crab
|
||||
*/
|
||||
// TODO we may want to call this method "hatch" now since we store parents on the egg itself now
|
||||
fun breed(mother: Pokemon, father: Pokemon): BreedingResult {
|
||||
if (!this.canBreed(mother, father)) {
|
||||
return BreedingResult()
|
||||
}
|
||||
|
||||
// Crab note: TODO Clean this shit up it is ugly af and idk if we even want to do custom breeding rules this way.... HIRO HELP
|
||||
|
||||
// find matching breeding rule
|
||||
val rule = BreedingRules.getBreedingRule(mother, father)
|
||||
|
||||
val fallbackForm: FormData = this.calculateForm(mother, father)
|
||||
|
||||
val finalSpecies: Species = if (rule?.species != null) {
|
||||
PokemonSpecies.getByName(rule.species.lowercase()) ?: fallbackForm.species
|
||||
} else {
|
||||
fallbackForm.species
|
||||
}
|
||||
|
||||
val finalForm: FormData = if (rule != null && rule.form != null) {
|
||||
val speciesEntry = rule.species?.let { PokemonSpecies.getByName(it.lowercase()) } ?: fallbackForm.species
|
||||
speciesEntry.forms.find { formData ->
|
||||
formData.name.lowercase() == rule.form.lowercase()
|
||||
} ?: speciesEntry.standardForm
|
||||
} else {
|
||||
fallbackForm
|
||||
}
|
||||
|
||||
// Calculate fallback IVs, then override stats from the rule if non-null
|
||||
val finalIVs = mergeIvs(rule, mother, father)
|
||||
|
||||
val finalNature: Nature = if (rule?.nature != null) {
|
||||
Natures.getNature(rule.nature.lowercase()) ?: this.calculateNature(mother, father)
|
||||
} else {
|
||||
this.calculateNature(mother, father)
|
||||
}
|
||||
|
||||
// TODO get the ability to actually apply
|
||||
val finalAbility: Ability = if (rule?.ability != null) {
|
||||
Abilities.get(rule.ability.lowercase())?.create(true, Priority.HIGHEST) ?: this.calculateAbility(mother, father, finalForm)
|
||||
} else {
|
||||
this.calculateAbility(mother, father, finalForm)
|
||||
}
|
||||
|
||||
// Handle moves from the rule. We read move1..move4 if present, turn them into MoveTemplates,
|
||||
// then pass them as `eggMoves` to our `calculateMoveset`.
|
||||
val ruleMoveTemplates: List<MoveTemplate> = if (rule != null) {
|
||||
listOfNotNull(
|
||||
rule.moves.move1,
|
||||
rule.moves.move2,
|
||||
rule.moves.move3,
|
||||
rule.moves.move4
|
||||
).mapNotNull { moveString ->
|
||||
Moves.getByName(moveString.lowercase())
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
// Pass these move templates to `calculateMoveset` as the optional egg moves.
|
||||
val finalMoveSet = this.calculateMoveset(
|
||||
mother,
|
||||
father,
|
||||
finalForm ?: fallbackForm,
|
||||
eggMoves = ruleMoveTemplates
|
||||
)
|
||||
|
||||
// Determine shiny based on masuda
|
||||
val masudaActive = masudaActive(mother,father)
|
||||
val isShiny = when {
|
||||
masudaActive -> (Random.nextFloat() < 1f / Cobblemon.config.masudaShinyRate)
|
||||
else -> (Random.nextFloat() < 1f / Cobblemon.config.shinyRate)
|
||||
}
|
||||
|
||||
val finalGender: Gender = if (rule?.gender != null) {
|
||||
|
||||
Gender.entries.find { it.name.equals(rule.gender, ignoreCase = true) }
|
||||
?: this.calculateGender(mother, father, finalForm)
|
||||
} else {
|
||||
this.calculateGender(mother, father, finalForm)
|
||||
}
|
||||
|
||||
val finalPokeball: PokeBall = if (rule?.pokeball != null) {
|
||||
PokeBalls.getPokeBall(cobblemonResource(rule.pokeball.lowercase())) ?: this.calculatePokeball(mother, father)
|
||||
} else {
|
||||
this.calculatePokeball(mother, father)
|
||||
}
|
||||
|
||||
// create final egg from all this awful data
|
||||
val newPoke = EggPokemon(
|
||||
ivs = finalIVs,
|
||||
nature = finalNature,
|
||||
species = getPreEvoSpecies(finalSpecies),
|
||||
formData = finalForm,
|
||||
ability = finalAbility,
|
||||
moveSet = finalMoveSet,
|
||||
isShiny = isShiny,
|
||||
gender = finalGender,
|
||||
pokeball = finalPokeball
|
||||
)
|
||||
|
||||
return BreedingResult(newPoke, calcTimeToHatch(mother, father))
|
||||
}
|
||||
|
||||
|
||||
private fun mergeIvs(
|
||||
rule: BreedingRule?,
|
||||
mother: Pokemon,
|
||||
father: Pokemon
|
||||
): IVs {
|
||||
val fallbackIvs = this.calculateIVs(mother, father)
|
||||
if (rule == null) return fallbackIvs
|
||||
|
||||
// 3. For each stat, if rule.ivs.<stat> is non-null, override fallback
|
||||
rule.ivs.hp?.let { fallbackIvs[Stats.HP] = it }
|
||||
rule.ivs.attack?.let { fallbackIvs[Stats.ATTACK] = it }
|
||||
rule.ivs.defence?.let { fallbackIvs[Stats.DEFENCE] = it }
|
||||
rule.ivs.special_attack?.let { fallbackIvs[Stats.SPECIAL_ATTACK] = it }
|
||||
rule.ivs.special_defence?.let { fallbackIvs[Stats.SPECIAL_DEFENCE] = it }
|
||||
rule.ivs.speed?.let { fallbackIvs[Stats.SPEED] = it }
|
||||
|
||||
return fallbackIvs
|
||||
}
|
||||
|
||||
fun masudaActive(mother: Pokemon, father: Pokemon) : Boolean {
|
||||
val isDifferentOT = (mother.originalTrainer != father.originalTrainer)
|
||||
val bothMaxFriendship = (mother.friendship == 255 && father.friendship == 255)
|
||||
val requireBothOTs = Cobblemon.config.requireOTsForMasuda
|
||||
|
||||
return when {
|
||||
requireBothOTs -> (isDifferentOT && bothMaxFriendship)
|
||||
else -> (isDifferentOT || bothMaxFriendship)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two [Pokemon] can breed.
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return Whether the two [Pokemon] can breed
|
||||
* @author Apion, whatsy, plastered_crab
|
||||
*/
|
||||
fun canBreed(mother: Pokemon, father: Pokemon): Boolean {
|
||||
val canBreedUndiscovered = Cobblemon.config.canBreedUndiscovered
|
||||
val canBreedGenderless = Cobblemon.config.canBreedGenderless
|
||||
val canBreedDitto = Cobblemon.config.canBreedDitto
|
||||
val areSameSpecies = mother.species == father.species
|
||||
val sharedEggGroups = mother.form.eggGroups.intersect(father.form.eggGroups)
|
||||
|
||||
return when {
|
||||
// check custom breeding rules first
|
||||
(BreedingRules.getBreedingRule(mother, father) != null) -> true
|
||||
|
||||
// Check if config allows Undiscovered egg groups to breed
|
||||
(canBreedUndiscovered && areSameSpecies && mother.form.eggGroups.contains(EggGroup.UNDISCOVERED)) -> true
|
||||
|
||||
// Check if either Pokémon is in the No Eggs Discovered egg group
|
||||
(mother.form.eggGroups.contains(EggGroup.UNDISCOVERED) || father.form.eggGroups.contains(EggGroup.UNDISCOVERED)) -> false
|
||||
|
||||
// check if config allows ditto to breed with each other
|
||||
(canBreedDitto && areSameSpecies && mother.form.eggGroups.contains(EggGroup.DITTO)) -> true
|
||||
|
||||
// Check if both Pokémon are Ditto
|
||||
(mother.form.eggGroups.contains(EggGroup.DITTO) && father.form.eggGroups.contains(EggGroup.DITTO)) -> false
|
||||
|
||||
// check it config allows for same species genderless Pokemon can breed
|
||||
(canBreedGenderless && (areSameSpecies && mother.gender == Gender.GENDERLESS)) -> true
|
||||
|
||||
// check for genderless breeding that is not with ditto
|
||||
(mother.gender == Gender.GENDERLESS && !father.form.eggGroups.contains(EggGroup.DITTO)) ||
|
||||
(father.gender == Gender.GENDERLESS && !mother.form.eggGroups.contains(EggGroup.DITTO)) -> false
|
||||
|
||||
// if either parent is ditto
|
||||
(mother.form.eggGroups.contains(EggGroup.DITTO) || father.form.eggGroups.contains(EggGroup.DITTO)) -> true
|
||||
|
||||
// check if egg groups match
|
||||
else -> sharedEggGroups.isNotEmpty() && mother.gender != father.gender
|
||||
}
|
||||
/*
|
||||
// check custom breeding rules first
|
||||
if (BreedingRules.getBreedingRule(mother, father) != null)
|
||||
return true
|
||||
|
||||
// check if different species and Genderless
|
||||
if (!areSameSpecies && (mother.gender == Gender.GENDERLESS))
|
||||
return false
|
||||
|
||||
// Check if either Pokémon is in the No Eggs Discovered egg group
|
||||
if (!canBreedUndiscovered && (mother.form.eggGroups.contains(EggGroup.UNDISCOVERED) || father.form.eggGroups.contains(EggGroup.UNDISCOVERED)))
|
||||
return false
|
||||
|
||||
// Check if both Pokémon are Ditto
|
||||
if (canBreedDitto && (mother.form.eggGroups.contains(EggGroup.DITTO) && father.form.eggGroups.contains(EggGroup.DITTO)))
|
||||
return false
|
||||
|
||||
// check if both parents are Genderless and same species
|
||||
if (canBreedGenderless && (areSameSpecies && mother.gender == Gender.GENDERLESS))
|
||||
return true
|
||||
|
||||
// Check if at least one parent is Ditto
|
||||
val anyDitto = mother.form.eggGroups.contains(EggGroup.DITTO) || father.form.eggGroups.contains(EggGroup.DITTO)
|
||||
if (anyDitto)
|
||||
return true
|
||||
|
||||
|
||||
// Check for shared egg groups and opposite genders
|
||||
val sharedEggGroups = mother.form.eggGroups.intersect(father.form.eggGroups)
|
||||
return sharedEggGroups.isNotEmpty() && mother.gender != father.gender*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the base pre-evolution of a [Pokemon] species.
|
||||
*
|
||||
* @param species The Species of [Pokemon]
|
||||
* @return The base pre-evolution Species of [Pokemon]
|
||||
* @author Plastered_Crab
|
||||
*/
|
||||
private fun getPreEvoSpecies(species: Species): Species {
|
||||
var currentSpecies = species
|
||||
while (currentSpecies.preEvolution != null) {
|
||||
currentSpecies = currentSpecies.preEvolution!!.species
|
||||
}
|
||||
return currentSpecies
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [Gender] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @param form The [FormData] of the child
|
||||
* @return The [Gender] of the child
|
||||
* @author Apion, whatsy
|
||||
*/
|
||||
fun calculateGender(mother: Pokemon, father: Pokemon, form: FormData): Gender {
|
||||
val maleRatio = form.maleRatio
|
||||
if (maleRatio.toDouble() == -1.0) {
|
||||
return Gender.GENDERLESS
|
||||
}
|
||||
val rand = Random.nextDouble(0.0, 1.0)
|
||||
return if (rand < maleRatio) {
|
||||
Gender.MALE
|
||||
} else {
|
||||
Gender.FEMALE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [FormData] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return The [FormData] of the child
|
||||
* @author Nick, Apion, whatsy
|
||||
*/
|
||||
fun calculateForm(mother: Pokemon, father: Pokemon): FormData {
|
||||
/*
|
||||
* In most cases, if a hatched species has multiple forms (not dependent on in-battle conditions), it will
|
||||
* often inherit the form of the mother or non-Ditto parent. Burmy will always hatch with the same cloak as
|
||||
* its mother, and if bred between Mothim and Ditto, it will always hatch with a Plant Cloak. Rockruff with the
|
||||
* Ability Own Tempo, which evolves into Lycanroc's Dusk Form, is treated as a separate form and can pass down
|
||||
* this trait accordingly to its offspring. Oricorio, which changes its form using nectars, can also pass down
|
||||
* its form by breeding.
|
||||
*
|
||||
* There are a few exceptions to this inheritance pattern:
|
||||
*
|
||||
* - Rotom will always hatch into its normal form, but can be changed afterwards.
|
||||
* - Vivillon's pattern depends on the real-world, geographic area of the save file it originated from, regardless of its parent's pattern.
|
||||
* - The form that Scatterbug will evolve into is predetermined when the Egg is first obtained, not when hatched or evolved.
|
||||
* - Scatterbug hatched in Scarlet and Violet will only evolve into Fancy Pattern Vivillon.
|
||||
* - Furfrou, whose trims are temporary, always hatch in its Natural Form.
|
||||
* - Sinistea will always hatch as a Phony Form, even if its parent is an Antique Form.
|
||||
*
|
||||
* In addition, species with regional forms will hatch into whichever form is native to that region. However,
|
||||
* if a parent of a foreign form and the same family is holding an Everstone, the offspring will be of that
|
||||
* parent's form instead.
|
||||
*
|
||||
* The Gigantamax Factor, Alpha, Jumbo and Mini marks cannot be passed down by breeding.
|
||||
*
|
||||
* Pokémon such as Dunsparce, Wurmple, and Tandemaus, where the species and form they will evolve into is
|
||||
* predetermined when they are first generated, do not follow form inheritance rules. The forms of the parents
|
||||
* have no influence on the personality value or encryption constant.
|
||||
*
|
||||
* TODO - Handle this via form data parameters?
|
||||
*/
|
||||
return nonDittoPreferMother(mother, father).form
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [Nature] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return The [Nature] of the child
|
||||
* @author Nick, whatsy
|
||||
*/
|
||||
fun calculateNature(mother: Pokemon, father: Pokemon): Nature {
|
||||
val motherHasEverstone = mother.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }.`is`(CobblemonItemTags.EVERSTONE)
|
||||
val fatherHasEverstone = father.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }.`is`(CobblemonItemTags.EVERSTONE)
|
||||
|
||||
return when {
|
||||
motherHasEverstone && fatherHasEverstone -> if (Random.nextInt(100) < 50) mother.nature else father.nature
|
||||
motherHasEverstone -> mother.nature
|
||||
fatherHasEverstone -> father.nature
|
||||
else -> Natures.getRandomNature()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [Ability] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @param child The [FormData] of the child
|
||||
* @return The [Ability] of the child
|
||||
* @author Nick, whatsy
|
||||
*/
|
||||
fun calculateAbility(mother: Pokemon, father: Pokemon, child: FormData): Ability {
|
||||
val parent = this.nonDittoPreferMother(mother, father)
|
||||
val hidden = parent.form.abilities.filter { it.type == HiddenAbilityType }
|
||||
.map { it.template.name }
|
||||
.map { it == parent.ability.name }
|
||||
.firstOrNull()
|
||||
|
||||
return if(hidden == true) {
|
||||
val rng = Random.nextInt(0, 100) < 60
|
||||
if(rng) {
|
||||
parent.ability
|
||||
} else {
|
||||
child.abilities.select(child.species, child.aspects.toSet()).first
|
||||
}
|
||||
} else {
|
||||
val rng = Random.nextInt(0, 100) < 80
|
||||
if(rng) {
|
||||
parent.ability
|
||||
} else {
|
||||
child.abilities.select(child.species, child.aspects.toSet()).first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [MoveSet] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @form the form of the [Pokemon]
|
||||
* @return The [MoveSet] of the child
|
||||
* @author Nick, Apion, whatsy
|
||||
*/
|
||||
// TODO: This, based off of https://pokemondb.net/mechanics/breeding
|
||||
fun calculateMoveset(mother: Pokemon, father: Pokemon, form: FormData, eggMoves: List<MoveTemplate>? = null): MoveSet {
|
||||
val babyLearnset = form.moves.levelUpMoves[1] ?: emptySet()
|
||||
val inheritedMoves = mutableListOf<MoveTemplate>()
|
||||
|
||||
// Add Egg Moves using the JSON registry based on parent breeding rules
|
||||
//val eggMoves = BreedingRules.getEggMoveList(mother, father, babySpecies.resourceIdentifier)
|
||||
if (eggMoves != null) {
|
||||
inheritedMoves.addAll(eggMoves)
|
||||
}
|
||||
|
||||
// Add TM/HM Moves from both mother and father
|
||||
val tmHmMoves = (mother.moveSet.getMoves() + father.moveSet.getMoves())
|
||||
.map { it.template }
|
||||
.filter { form.species.moves.tmMoves.contains(it) }
|
||||
inheritedMoves.addAll(tmHmMoves)
|
||||
|
||||
// Add Inherited Level-Up Moves
|
||||
inheritedMoves.addAll(mother.moveSet.getMoves()
|
||||
.map { it.template }
|
||||
.filter { it in babyLearnset })
|
||||
inheritedMoves.addAll(father.moveSet.getMoves()
|
||||
.map { it.template }
|
||||
.filter { it in babyLearnset })
|
||||
|
||||
// Add Baby's Default Moves
|
||||
inheritedMoves.addAll(babyLearnset)
|
||||
|
||||
// TODO sort the moves properly
|
||||
// Limit to 4 moves, prioritizing certain moves first based off rules set by the games
|
||||
val finalMoves = inheritedMoves.distinct().sortedWith(
|
||||
compareByDescending<MoveTemplate> { it.name == "volt_tackle" } // Volt Tackle first
|
||||
.thenByDescending { eggMoves?.contains(it) == true } // Egg moves next
|
||||
.thenByDescending { tmHmMoves.contains(it) } // TM/HM moves after
|
||||
.thenByDescending { babyLearnset.contains(it) } // Default moves last
|
||||
).take(4)
|
||||
|
||||
val moveSet = MoveSet()
|
||||
finalMoves.forEach { moveTemplate ->
|
||||
moveSet.add(moveTemplate.create())
|
||||
}
|
||||
|
||||
return moveSet
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines the [IVs] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return The [IVs] of the child
|
||||
* @author Nick, whatsy, Apion
|
||||
*/
|
||||
fun calculateIVs(mother: Pokemon, father: Pokemon): IVs {
|
||||
//FIXME: Use registry holder
|
||||
val motherItem = mother.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
|
||||
val fatherItem = father.heldItemNoCopy().item.let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
|
||||
val isMotherDestinyKnotted = motherItem.`is`(CobblemonItemTags.DESTINY_KNOT)
|
||||
val isFatherDestinyKnotted = fatherItem.`is`(CobblemonItemTags.DESTINY_KNOT)
|
||||
val passedStats = mutableSetOf<Stat>()
|
||||
val childStats = IVs.createRandomIVs()
|
||||
var numPassed = if(isMotherDestinyKnotted || isFatherDestinyKnotted) {
|
||||
5
|
||||
} else {
|
||||
3
|
||||
}
|
||||
|
||||
fun calcForcedStat(holder: Holder<Item>?, mon: Pokemon) {
|
||||
fun passStat(stats: Stat) {
|
||||
if (!passedStats.contains(stats)) {
|
||||
childStats[stats] = mon.ivs[stats]!!
|
||||
passedStats.add(stats)
|
||||
numPassed--
|
||||
}
|
||||
}
|
||||
if(holder == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if(holder.`is`(CobblemonItemTags.POWER_ANKLET)) {
|
||||
passStat(Stats.SPEED)
|
||||
} else if(holder.`is`(CobblemonItemTags.POWER_BAND)) {
|
||||
passStat(Stats.SPECIAL_DEFENCE)
|
||||
} else if(holder.`is`(CobblemonItemTags.POWER_BELT)) {
|
||||
passStat(Stats.DEFENCE)
|
||||
} else if(holder.`is`(CobblemonItemTags.POWER_BRACER)) {
|
||||
passStat(Stats.ATTACK)
|
||||
} else if(holder.`is`(CobblemonItemTags.POWER_LENS)) {
|
||||
passStat(Stats.SPECIAL_ATTACK)
|
||||
} else if(holder.`is`(CobblemonItemTags.POWER_WEIGHT)) {
|
||||
passStat(Stats.HP)
|
||||
}
|
||||
}
|
||||
calcForcedStat(fatherItem, father)
|
||||
calcForcedStat(motherItem, mother)
|
||||
|
||||
val parentIvs = mother.ivs.toMutableList()
|
||||
parentIvs.addAll(father.ivs)
|
||||
while (numPassed > 0 && parentIvs.isNotEmpty()) {
|
||||
val statPicked: Map.Entry<Stat, Int> = parentIvs.random()
|
||||
if (!passedStats.contains(statPicked.key)) {
|
||||
passedStats.add(statPicked.key)
|
||||
childStats[statPicked.key] = statPicked.value
|
||||
parentIvs.removeAll {it.key == statPicked.key}
|
||||
numPassed--
|
||||
}
|
||||
}
|
||||
|
||||
return childStats
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the [PokeBall] of a child
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @return The [PokeBall] of the child
|
||||
* @author Nick, whatsy
|
||||
*/
|
||||
fun calculatePokeball(mother: Pokemon, father: Pokemon): PokeBall {
|
||||
/*
|
||||
* The Master Ball, Cherish Ball, and Strange Ball cannot be inherited via breeding; instead, the game treats
|
||||
* any parent obtained in those balls as if they were in a standard Poké Ball for the purposes of inheritance.
|
||||
* To add Poké Balls to this list, add to the defaulting_poke_balls.json item tag.
|
||||
*/
|
||||
val anyDitto = listOf(mother, father).any { it.form.eggGroups.contains(EggGroup.DITTO) }
|
||||
|
||||
val motherBallItem = mother.caughtBall.item().let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
|
||||
val fatherBallItem = father.caughtBall.item().let { BuiltInRegistries.ITEM.wrapAsHolder(it) }
|
||||
println(motherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS))
|
||||
val motherBall = if (motherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS)) PokeBalls.POKE_BALL else mother.caughtBall
|
||||
val fatherBall = if (fatherBallItem.`is`(CobblemonItemTags.DEFAULTING_POKE_BALLS)) PokeBalls.POKE_BALL else father.caughtBall
|
||||
|
||||
val ball: PokeBall = if(anyDitto) {
|
||||
if (nonDittoPreferMother(mother, father) == mother) motherBall else fatherBall
|
||||
} else if(mother.species.resourceIdentifier == father.species.resourceIdentifier) {
|
||||
if(Random.nextInt(100) < 50) {
|
||||
motherBall
|
||||
} else {
|
||||
fatherBall
|
||||
}
|
||||
} else {
|
||||
motherBall
|
||||
}
|
||||
|
||||
return ball
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mother is a Ditto, returns the father.
|
||||
* Otherwise, returns the mother.
|
||||
*
|
||||
* @param mother The mother [Pokemon]
|
||||
* @param father The father [Pokemon]
|
||||
* @author Nick
|
||||
*/
|
||||
fun nonDittoPreferMother(mother: Pokemon, father: Pokemon): Pokemon {
|
||||
return if(mother.form.eggGroups.contains(EggGroup.DITTO)) father else mother
|
||||
}
|
||||
|
||||
fun resetBreedingCooldown(pokemon: Pokemon): Int {
|
||||
val eggCycle = (pokemon.species.eggCycles).coerceAtLeast(1)
|
||||
val minEggCycle = 5 // this is the lowest eggCycle (like caterpie)
|
||||
val avgEggCycle = 40 // average eggCycle value
|
||||
val minTime = 5 // in minutes
|
||||
val avgTime = 20 // in minutes
|
||||
|
||||
val slope = (avgTime - minTime) / (avgEggCycle - minEggCycle)
|
||||
val intercept = minTime - slope * minEggCycle
|
||||
|
||||
val cooldown = slope * eggCycle + intercept
|
||||
|
||||
return cooldown * 1200 // convert to ticks
|
||||
}
|
||||
|
||||
fun calcTimeToHatch(mother: Pokemon, father: Pokemon): Int {
|
||||
val avgEggCycle = ((mother.species.eggCycles + father.species.eggCycles) / 2).toInt()
|
||||
val cycleToMinute = 2
|
||||
|
||||
return (avgEggCycle * cycleToMinute) * 1200 // convert to ticks
|
||||
}
|
||||
|
||||
fun checkCompatibility(mother: Pokemon, father: Pokemon): Boolean {
|
||||
val sameSpecies = mother.species == father.species
|
||||
val sameTrainer = mother.getOwnerUUID() == father.getOwnerUUID()
|
||||
|
||||
val chance = when {
|
||||
sameSpecies && !sameTrainer -> 70 // Same species, different trainer
|
||||
sameSpecies && sameTrainer -> 50 // Same species, same trainer
|
||||
!sameSpecies && !sameTrainer -> 50 // Different species, different trainer
|
||||
!sameSpecies && sameTrainer -> 20
|
||||
else -> 0 // Different species, same trainer
|
||||
}
|
||||
|
||||
return Random.nextInt(100) < chance
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
data class BreedingResult(
|
||||
val pokemon: EggPokemon? = null,
|
||||
val timeToHatch: Int = 20000,
|
||||
val successful: Boolean = pokemon != null
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
data class Egg(
|
||||
val species: ResourceLocation, // Species of the Pokémon
|
||||
val parentOne: Pokemon?, // First parent's UUID
|
||||
val parentTwo: Pokemon?, // Second parent's UUID
|
||||
val form: String, // Form of the Pokémon
|
||||
var timeToHatch: Float // Time in ticks to hatch
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<Egg?> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.optionalFieldOf("species", cobblemonResource("unknown")).forGetter { it?.species ?: cobblemonResource("unknown") },
|
||||
Pokemon.CODEC.optionalFieldOf("parentOne").forGetter { it?.parentOne?.let { Optional.of(it) } ?: Optional.empty() },
|
||||
Pokemon.CODEC.optionalFieldOf("parentTwo").forGetter { it?.parentTwo?.let { Optional.of(it) } ?: Optional.empty() },
|
||||
Codec.STRING.optionalFieldOf("form", "").forGetter { it?.form ?: "" },
|
||||
Codec.FLOAT.optionalFieldOf("timeToHatch", 0F).forGetter { it?.timeToHatch ?: 0F }
|
||||
).apply(instance) { species, parentOneOpt, parentTwoOpt, form, timeToHatch ->
|
||||
if (species != cobblemonResource("unknown") || timeToHatch != 0F || parentOneOpt.isPresent || parentTwoOpt.isPresent || form.isNotEmpty()) {
|
||||
Egg(
|
||||
species = species,
|
||||
parentOne = parentOneOpt.orElse(null),
|
||||
parentTwo = parentTwoOpt.orElse(null),
|
||||
form = form,
|
||||
timeToHatch = timeToHatch
|
||||
)
|
||||
} else {
|
||||
null // Represents no egg.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createEggItemStack(): ItemStack {
|
||||
// Create a new ItemStack using the Pokémon Egg item
|
||||
val eggItemStack = CobblemonItems.POKEMON_EGG.defaultInstance
|
||||
|
||||
// Add the EggData component to the ItemStack
|
||||
val eggComponent = EggComponent(this)
|
||||
eggItemStack.set(CobblemonItemComponents.EGG_DATA, eggComponent)
|
||||
|
||||
return eggItemStack
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*fun getPattern(): EggPattern? {
|
||||
return EggPatterns.patternMap[patternId]
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromBlockNbt(blockNbt: CompoundTag): Egg {
|
||||
val eggNbt = blockNbt.get(DataKeys.EGG) as CompoundTag
|
||||
return fromNbt(eggNbt)
|
||||
}
|
||||
fun fromNbt(nbt: CompoundTag): Egg {
|
||||
return Egg(
|
||||
EggPokemon.fromNBT(nbt.get(DataKeys.HATCHED_POKEMON) as CompoundTag),
|
||||
ResourceLocation.tryParse(nbt.getString(DataKeys.EGG_PATTERN))!!,
|
||||
nbt.getString(DataKeys.PRIMARY_COLOR),
|
||||
if (nbt.contains(DataKeys.SECONDARY_COLOR)) nbt.getString(DataKeys.SECONDARY_COLOR) else null,
|
||||
nbt.getInt(DataKeys.TIME_TO_HATCH)
|
||||
)
|
||||
}
|
||||
|
||||
//Less expensive than deserializing the whole thing, used for the color provider since it gets
|
||||
//called every frame
|
||||
fun getColorsFromNbt(nbt: CompoundTag): Pair<String, String> {
|
||||
return Pair(
|
||||
nbt.getString(DataKeys.PRIMARY_COLOR),
|
||||
nbt.getString(DataKeys.SECONDARY_COLOR)
|
||||
)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Represents a pattern on an egg
|
||||
*
|
||||
* @author Apion
|
||||
* @since January 2nd, 2024
|
||||
*/
|
||||
data class EggPattern (
|
||||
val model: ResourceLocation,
|
||||
val baseTexturePath: ResourceLocation,
|
||||
val overlayTexturePath: ResourceLocation?,
|
||||
val baseInvSpritePath: ResourceLocation,
|
||||
val overlayInvSpritePath: ResourceLocation?,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object EggPatterns : JsonDataRegistry<EggPattern> {
|
||||
override val id = cobblemonResource("egg_patterns")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<EggPatterns>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val typeToken = TypeToken.get(EggPattern::class.java)
|
||||
override val resourcePath = "egg_patterns"
|
||||
|
||||
val patternMap = hashMapOf<ResourceLocation, EggPattern>()
|
||||
override fun reload(data: Map<ResourceLocation, EggPattern>) {
|
||||
patternMap.putAll(data)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
//TODO: Need to implement
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class EggPokemon(
|
||||
val ivs: IVs,
|
||||
val nature: Nature,
|
||||
val species: Species,
|
||||
val formData: FormData,
|
||||
val ability: Ability,
|
||||
val moveSet: MoveSet,
|
||||
val isShiny: Boolean,
|
||||
val gender: Gender,
|
||||
val pokeball: PokeBall
|
||||
) {
|
||||
fun generatePokemon(): Pokemon {
|
||||
val result = Pokemon()
|
||||
ivs.forEach {
|
||||
result.setIV(it.key, it.value)
|
||||
}
|
||||
result.species = species
|
||||
result.gender = gender
|
||||
result.nature = nature
|
||||
result.moveSet.clear()
|
||||
result.moveSet.copyFrom(moveSet)
|
||||
result.shiny = isShiny
|
||||
result.caughtBall = pokeball
|
||||
result.form = formData
|
||||
result.ability = ability
|
||||
return result
|
||||
|
||||
}
|
||||
fun toNbt(): CompoundTag {
|
||||
val result = CompoundTag()
|
||||
val ivNbt = ivs.saveToNBT(CompoundTag())
|
||||
|
||||
result.put(DataKeys.POKEMON_IVS, ivNbt)
|
||||
result.putString(DataKeys.POKEMON_NATURE, nature.name.toString())
|
||||
result.putString(DataKeys.POKEMON_SPECIES_IDENTIFIER, species.resourceIdentifier.toString())
|
||||
result.putString(DataKeys.POKEMON_FORM_ID, formData.formOnlyShowdownId())
|
||||
val abilityNBT = ability.saveToNBT(CompoundTag())
|
||||
result.put(DataKeys.POKEMON_ABILITY, abilityNBT)
|
||||
result.put(DataKeys.POKEMON_MOVESET, moveSet.getNBT())
|
||||
result.putBoolean(DataKeys.POKEMON_SHINY, isShiny)
|
||||
result.putString(DataKeys.POKEMON_GENDER, gender.name)
|
||||
result.putString(DataKeys.POKEMON_CAUGHT_BALL, pokeball.name.toString())
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
// I heard you like casts so I put some casts in your casts
|
||||
fun fromNBT(nbt: CompoundTag): EggPokemon {
|
||||
val species = PokemonSpecies.getByIdentifier(ResourceLocation.tryParse(nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER))!!)!!
|
||||
val abilityNBT = nbt.getCompound(DataKeys.POKEMON_ABILITY) ?: CompoundTag()
|
||||
val abilityName = abilityNBT.getString(DataKeys.POKEMON_ABILITY_NAME).takeIf { it.isNotEmpty() } ?: "runaway"
|
||||
val moveSet = MoveSet()
|
||||
val pokeball = nbt.getString(DataKeys.POKEMON_CAUGHT_BALL)
|
||||
return EggPokemon(
|
||||
IVs().loadFromNBT(nbt.get(DataKeys.POKEMON_IVS) as CompoundTag) as IVs,
|
||||
Natures.getNature(ResourceLocation.tryParse(nbt.getString(DataKeys.POKEMON_NATURE))!!)!!,
|
||||
species,
|
||||
species.forms.firstOrNull { it.formOnlyShowdownId() == nbt.getString(DataKeys.POKEMON_FORM_ID) } ?: species.standardForm,
|
||||
Abilities.getOrException(abilityName).create(abilityNBT),
|
||||
moveSet.loadFromNBT(nbt),
|
||||
nbt.getBoolean(DataKeys.POKEMON_SHINY),
|
||||
Gender.valueOf(nbt.getString(DataKeys.POKEMON_GENDER)),
|
||||
PokeBalls.getPokeBall(ResourceLocation.tryParse(pokeball)!!) ?: PokeBalls.POKE_BALL
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,37 +18,23 @@ import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
|
||||
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
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.*
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SubmergedSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.*
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.NestSpawner
|
||||
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
|
||||
import com.cobblemon.mod.common.api.spawning.preset.PokemonSpawnDetailPreset
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.AreaSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.*
|
||||
import com.cobblemon.mod.common.entity.pokemon.CobblemonAgingDespawner
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -142,5 +128,12 @@ object BestSpawner {
|
||||
CobblemonSpawnPools.onServerLoad(server)
|
||||
spawnerManagers.forEach(SpawnerManager::onServerStarted)
|
||||
fishingSpawner = FishingSpawner()
|
||||
|
||||
val grottoSpawner = GrottoSpawner(
|
||||
name = "grotto",
|
||||
fallbackSpawns = SpawnPool("grotto_empty"),
|
||||
manager = CobblemonWorldSpawnerManager
|
||||
)
|
||||
CobblemonWorldSpawnerManager.registerSpawner(grottoSpawner)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningInfluenceProspector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningArea
|
||||
@@ -79,6 +81,7 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
val blocks = Array(area.length) { Array(height) { Array(area.width) { defaultBlockData } } }
|
||||
val skyLevel = Array(area.length) { Array(area.width) { world.maxBuildHeight } }
|
||||
val pos = BlockPos.MutableBlockPos()
|
||||
val worldSlicedSpawningInfluences = mutableListOf<WorldSlicedSpawningInfluence>()
|
||||
|
||||
val chunks = mutableMapOf<Pair<Int, Int>, ChunkAccess?>()
|
||||
val yRange = (baseY until baseY + height).reversed()
|
||||
@@ -98,6 +101,7 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
light = world.getMaxLocalRawBrightness(pos),
|
||||
skyLight = skyLight
|
||||
)
|
||||
worldSlicedSpawningInfluences.addAll(SpawningInfluenceProspector.prospectors.mapNotNull { it.prospectBlock(world, pos, state) })
|
||||
if (canSeeSky) {
|
||||
skyLevel[x - area.baseX][z - area.baseZ] = y
|
||||
}
|
||||
@@ -108,6 +112,10 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
}
|
||||
}
|
||||
|
||||
for (prospector in SpawningInfluenceProspector.prospectors) {
|
||||
worldSlicedSpawningInfluences.addAll(prospector.prospect(spawner, area))
|
||||
}
|
||||
|
||||
return WorldSlice(
|
||||
cause = area.cause,
|
||||
world = world,
|
||||
@@ -116,7 +124,8 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
baseZ = area.baseZ,
|
||||
blocks = blocks,
|
||||
skyLevel = skyLevel,
|
||||
nearbyEntityPositions = nearbyEntityPositions
|
||||
nearbyEntityPositions = nearbyEntityPositions,
|
||||
influences = worldSlicedSpawningInfluences
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
class GrottoSpawnCause(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
entity: Entity? = null,
|
||||
val structureCenter: BlockPos,
|
||||
val grottoPos: BlockPos
|
||||
) : SpawnCause(spawner, bucket, entity)
|
||||
@@ -68,7 +68,7 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -35,7 +37,8 @@ class WorldSlice(
|
||||
val baseZ: Int,
|
||||
val blocks: Array<Array<Array<BlockData>>>,
|
||||
val skyLevel: Array<Array<Int>>,
|
||||
var nearbyEntityPositions: List<Vec3>
|
||||
var nearbyEntityPositions: List<Vec3>,
|
||||
val influences: List<WorldSlicedSpawningInfluence>
|
||||
) {
|
||||
class BlockData(
|
||||
val state: BlockState,
|
||||
@@ -129,7 +132,7 @@ class WorldSlice(
|
||||
}
|
||||
|
||||
fun horizontalSpace(position: BlockPos, condition: (BlockState) -> Boolean, maximum: Int)
|
||||
= horizontalSpace(position.x, position.y, position.z, condition, maximum)
|
||||
= horizontalSpace(position.x, position.y, position.z, condition, maximum)
|
||||
fun horizontalSpace(centerX: Int, centerY: Int, centerZ: Int, condition: (BlockState) -> Boolean, maximum: Int): Int {
|
||||
var space = 1
|
||||
var radius = 1
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.NestSpawningContext
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* A spawning condition that applies to [NestSpawningContext]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawningCondition: SpawningCondition<NestSpawningContext>() {
|
||||
override fun contextClass() = NestSpawningContext::class.java
|
||||
|
||||
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(ctx: NestSpawningContext): Boolean {
|
||||
if (!super.fits(ctx)) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
super.copyFrom(other, merger)
|
||||
if (other is NestSpawningCondition) {
|
||||
neededNearbyBlocks = merger.merge(neededNearbyBlocks, other.neededNearbyBlocks)?.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
val containsNullValues = neededNearbyBlocks != null && neededNearbyBlocks!!.any {it == null}
|
||||
return super.isValid() && !containsNullValues
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "nest"
|
||||
}
|
||||
}
|
||||
-1
@@ -11,7 +11,6 @@ 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.util.Merger
|
||||
import com.cobblemon.mod.common.util.math.orMax
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.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.api.spawning.nest.NestSpawnCause
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
/**
|
||||
* A spawning context that was generated for a nest spawn action.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawningContext(
|
||||
cause: NestSpawnCause,
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
influences: MutableList<SpawningInfluence>
|
||||
) : TriggerSpawningContext(
|
||||
cause = cause,
|
||||
world = world,
|
||||
position = pos,
|
||||
light = world.getMaxLocalRawBrightness(pos),
|
||||
skyLight = world.getMaxLocalRawBrightness(pos.above()),
|
||||
canSeeSky = world.canSeeSkyFromBelowWater(pos),
|
||||
influences = influences
|
||||
) {
|
||||
val nearbyBlocks = world.getBlockStates(AABB.ofSize(pos.toVec3d(), 10.0, 10.0, 10.0))
|
||||
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.map { it.block }.distinct().toList() }
|
||||
}
|
||||
-2
@@ -216,6 +216,4 @@ class FishingSpawnCause(
|
||||
}
|
||||
return super.affectWeight(detail, ctx, weight)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.Habitats
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class GrottoInfluence(
|
||||
private val structureId: ResourceLocation,
|
||||
private val allowedSpawns: List<ResourceLocation>? = null // null = allow all from habitat
|
||||
) : SpawningInfluence {
|
||||
|
||||
/*companion object {
|
||||
private val LOGGER = LoggerFactory.getLogger("GrottoInfluence")
|
||||
}
|
||||
|
||||
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
|
||||
if (detail !is PokemonSpawnDetail) return true
|
||||
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return false
|
||||
|
||||
// If strict filtering enabled, reject anything not in allowed list
|
||||
if (allowedSpawns != null && !allowedSpawns.contains(detailId)) {
|
||||
LOGGER.info("[GrottoInfluence] Rejecting ${detailId.path} — not in current moon phase group.")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
|
||||
if (detail !is PokemonSpawnDetail) return weight
|
||||
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return weight
|
||||
|
||||
val boostTargetList = allowedSpawns ?: Habitats.getHabitat(structureId)?.spawnPool?.flatten() ?: emptyList()
|
||||
return if (boostTargetList.contains(detailId)) {
|
||||
LOGGER.info("[GrottoInfluence] Boosting spawn weight for ${detailId.path} (structure: $structureId)")
|
||||
weight * 100f
|
||||
} else {
|
||||
weight
|
||||
}
|
||||
}*/
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
|
||||
/**
|
||||
* A type of [WorldSlicedSpawningInfluence] that is only applied to contexts within a particular radius
|
||||
* of a position.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 9th, 2025
|
||||
*/
|
||||
class WorldSlicedSpatialSpawningInfluence(
|
||||
val pos: BlockPos,
|
||||
val radius: Float,
|
||||
influence: SpawningInfluence
|
||||
) : WorldSlicedSpawningInfluence(influence) {
|
||||
override fun appliesTo(context: SpawningContext): Boolean {
|
||||
return sqrt(context.position.distSqr(pos)) <= radius
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
|
||||
/**
|
||||
* A wrapping around a [SpawningInfluence] that was produced for a [WorldSlice]. This is wrapped
|
||||
* to provide a filtration mechanism on which of these can be applied to a [SpawningContext].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 9th, 2025
|
||||
*/
|
||||
open class WorldSlicedSpawningInfluence(val influence: SpawningInfluence) {
|
||||
/** Whether this influence will apply to */
|
||||
open fun appliesTo(context: SpawningContext): Boolean = true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.nest
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
|
||||
/**
|
||||
* A spawning cause that is embellished with nest information.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawnCause(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
entity: Entity?
|
||||
) : SpawnCause(spawner, bucket, entity) {
|
||||
// Crab Note: todo what do we put here? a reference to the nest entity causing this spawn cause?
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.NestSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
|
||||
/**
|
||||
* A spawner that takes in a [FishingSpawnCause] and spawns things.
|
||||
*
|
||||
* @author Plastered_Crab
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
open class NestSpawner(
|
||||
override val name: String = "nest",
|
||||
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
) : TriggerSpawner<NestSpawnCause>(name, pool) {
|
||||
override fun parseContext(cause: NestSpawnCause, world: ServerLevel, pos: BlockPos): NestSpawningContext? {
|
||||
val ctx = NestSpawningContext(
|
||||
cause = cause,
|
||||
world = world,
|
||||
pos = pos,
|
||||
influences = influences
|
||||
)
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.habitats.Habitats
|
||||
import com.cobblemon.mod.common.api.spawning.influence.GrottoInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpatialSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningArea
|
||||
import com.cobblemon.mod.common.block.GrottoBlock
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object GrottoBlockProspector : SpawningInfluenceProspector {
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
// todo going with this prospect method for now since the GrottoSpawner might be what I want instead of handling it here
|
||||
override fun prospect(spawner: Spawner, area: SpawningArea): MutableList<WorldSlicedSpawningInfluence> {
|
||||
return mutableListOf() // GrottoSpawner handles spawning directly now
|
||||
}
|
||||
|
||||
// todo this was some test code of a prospect function that would try to alter the already filtered spawn lists, but I doubt we want to go this direction
|
||||
/*override fun prospect(spawner: Spawner, area: SpawningArea): MutableList<WorldSlicedSpawningInfluence> {
|
||||
val world = area.world
|
||||
val listOfInfluences = mutableListOf<WorldSlicedSpawningInfluence>()
|
||||
|
||||
val searchRange = RANGE + ceil(sqrt(((area.length pow 2) + (area.width pow 2)).toDouble())).toInt()
|
||||
val grottoBlockPositions = world.poiManager.findAll({ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },{ true },area.getCenter().toBlockPos(),searchRange, PoiManager.Occupancy.ANY).toList()
|
||||
|
||||
for (grottoBlockPos in grottoBlockPositions) {
|
||||
val blockState = world.getBlockState(grottoBlockPos)
|
||||
if (blockState.block !is GrottoBlock) continue
|
||||
|
||||
val blockEntity = world.getBlockEntity(grottoBlockPos) as? GrottoBlockEntity ?: continue
|
||||
val structureId = blockEntity.detectedStructure ?: continue
|
||||
|
||||
if (structureId == null) {
|
||||
println("[GrottoBlockProspector] Grotto at $grottoBlockPos has no detected structure.")
|
||||
continue
|
||||
}
|
||||
|
||||
val habitat = Habitats.getHabitat(structureId)
|
||||
if (habitat == null) {
|
||||
println("[GrottoBlockProspector] Grotto at $grottoBlockPos references unknown habitat: $structureId")
|
||||
continue
|
||||
}
|
||||
|
||||
println("[GrottoBlockProspector] Found Grotto at $grottoBlockPos")
|
||||
println(" ↳ Structure: $structureId")
|
||||
if (habitat.spawnPool.isEmpty()) {
|
||||
println(" ↳ Spawn pool is empty.")
|
||||
} else {
|
||||
println(" ↳ Spawn pool:")
|
||||
habitat.spawnPool.forEach {
|
||||
println(" • $it")
|
||||
}
|
||||
}
|
||||
|
||||
val allowedSpawns = blockEntity.currentSpawnGroup
|
||||
if (allowedSpawns.isNotEmpty()) {
|
||||
listOfInfluences.add(
|
||||
WorldSlicedSpatialSpawningInfluence(
|
||||
grottoBlockPos,
|
||||
RANGE.toFloat(),
|
||||
influence = GrottoInfluence(structureId, allowedSpawns)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return listOfInfluences
|
||||
}*/
|
||||
|
||||
override fun prospectBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState): WorldSlicedSpatialSpawningInfluence? { return null }
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningArea
|
||||
|
||||
/**
|
||||
* Prospects for [WorldSlicedSpawningInfluence] in a world at a given position. Occurs as part of a
|
||||
* [SpawningProspector] scanning a part of the world while formulating a [WorldSlice].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 9th, 2025
|
||||
*/
|
||||
interface SpawningInfluenceProspector {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val prospectors = mutableSetOf<SpawningInfluenceProspector>(
|
||||
// todo commenting out the GrottoBlockProspector since I don't know if we want to use it at all anyways
|
||||
//GrottoBlockProspector
|
||||
)
|
||||
}
|
||||
|
||||
fun prospect(spawner: Spawner, area: SpawningArea) : MutableList<WorldSlicedSpawningInfluence>
|
||||
|
||||
fun prospectBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState): WorldSlicedSpawningInfluence?
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.habitats.Habitats
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnerManager
|
||||
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.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.GrottoSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class GrottoSpawner(
|
||||
name: String,
|
||||
fallbackSpawns: SpawnPool,
|
||||
manager: SpawnerManager
|
||||
) : AreaSpawner(name, fallbackSpawns, manager) {
|
||||
|
||||
companion object {
|
||||
private val cache = ConcurrentHashMap<BlockPos, Pair<SpawnPool, Long>>()
|
||||
private const val CACHE_DURATION_MS = 10_000
|
||||
}
|
||||
|
||||
override var ticksBetweenSpawns: Float = 120F
|
||||
|
||||
// todo use this getArea to try to use a new GrottoSpawnCause and use that to send the structure center so spawns are around the structures
|
||||
override fun getArea(cause: SpawnCause): SpawningArea? {
|
||||
if (cause is GrottoSpawnCause) {
|
||||
val center = cause.structureCenter
|
||||
return SpawningArea(
|
||||
cause = cause,
|
||||
world = server()?.getLevel(cause.entityWorldId) ?: return null,
|
||||
baseX = center.x - 8,
|
||||
baseY = center.y - 4,
|
||||
baseZ = center.z - 8,
|
||||
length = 16,
|
||||
height = 8,
|
||||
width = 16
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
|
||||
//println("[GrottoSpawner] run() starting for cause: $cause")
|
||||
val area = getArea(cause)
|
||||
//println("[GrottoSpawner] getArea() result: $area")
|
||||
val constrainedArea = if (area != null) constrainArea(area) else null
|
||||
//println("[GrottoSpawner] constrainArea() result: $constrainedArea")
|
||||
|
||||
if (constrainedArea != null) {
|
||||
val slice = prospector.prospect(this, constrainedArea)
|
||||
val contexts = resolver.resolve(this, contextCalculators, slice)
|
||||
return getSpawningSelector().select(this, contexts)
|
||||
}
|
||||
|
||||
//println("[GrottoSpawner] Area is null or invalid, skipping spawn")
|
||||
return null
|
||||
}
|
||||
|
||||
// todo test tick
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
ticksUntilNextSpawn -= tickTimerMultiplier
|
||||
if (ticksUntilNextSpawn > 0) return
|
||||
|
||||
val server = server() ?: return
|
||||
val world = server.overworld() ?: return
|
||||
|
||||
for (player in world.players()) {
|
||||
val searchOrigin = player.blockPosition()
|
||||
val poiManager = world.poiManager
|
||||
val grottoPOIs = poiManager.findAll(
|
||||
{ it.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
|
||||
{ true },
|
||||
searchOrigin,
|
||||
128, // Search radius
|
||||
PoiManager.Occupancy.ANY
|
||||
)
|
||||
|
||||
for (grottoPos in grottoPOIs) {
|
||||
val blockEntity = world.getBlockEntity(grottoPos) as? GrottoBlockEntity ?: continue
|
||||
val structureCenter = blockEntity.structureCenter ?: continue
|
||||
|
||||
val cause = GrottoSpawnCause(this, chooseBucket(), player, structureCenter, grottoPos)
|
||||
val result = run(cause)
|
||||
|
||||
//println("[GrottoSpawner] Grotto at $grottoPos spawn result: $result")
|
||||
|
||||
if (result != null) {
|
||||
val (context, detail) = result
|
||||
val action = detail.doSpawn(context)
|
||||
//println("[GrottoSpawner] doSpawn: $detail -> $action")
|
||||
val completion = action.complete()
|
||||
//println("[GrottoSpawner] spawn complete: $completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ticksUntilNextSpawn = ticksBetweenSpawns
|
||||
}
|
||||
|
||||
// todo we want to make it so that it ignores conditions of the area and just use the SpawnPool of the JSON registry that has been cycled during that moon cycle
|
||||
override fun getSpawningSelector(): SpawningSelector {
|
||||
return object : SpawningSelector {
|
||||
override fun select(spawner: Spawner, contexts: List<SpawningContext>): Pair<SpawningContext, SpawnDetail>? {
|
||||
val pool = when {
|
||||
spawner is GrottoSpawner && contexts.firstOrNull()?.cause is GrottoSpawnCause ->
|
||||
spawner.getSpawnPool(contexts.first().cause)
|
||||
else -> spawner.getSpawnPool()
|
||||
}
|
||||
|
||||
for (ctx in contexts) {
|
||||
val details = pool.details.filterIsInstance<PokemonSpawnDetail>()
|
||||
if (details.isEmpty()) continue
|
||||
|
||||
val chosen = weightedRandom(details) { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
|
||||
//println("[GrottoSpawner] Selected ${chosen.pokemon.species} for spawn")
|
||||
return ctx to chosen
|
||||
}
|
||||
|
||||
//println("[GrottoSpawner] No spawnable Pokémon found.")
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getTotalWeights(spawner: Spawner, contexts: List<SpawningContext>): Map<SpawnDetail, Float> {
|
||||
return spawner.getSpawnPool().details.associateWith { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
|
||||
}
|
||||
|
||||
private fun <T> weightedRandom(entries: List<T>, weightFn: (T) -> Float): T {
|
||||
val totalWeight = entries.sumOf { weightFn(it).toDouble() }
|
||||
var roll = Math.random() * totalWeight
|
||||
for (entry in entries) {
|
||||
roll -= weightFn(entry)
|
||||
if (roll <= 0) return entry
|
||||
}
|
||||
return entries.last()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getSpawnPool(): SpawnPool {
|
||||
return spawns
|
||||
}
|
||||
|
||||
// todo new getSpawnPool
|
||||
fun getSpawnPool(cause: SpawnCause): SpawnPool {
|
||||
val server = server() ?: return spawns
|
||||
val world = server.overworld() ?: return spawns
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
if (cause !is GrottoSpawnCause) return spawns
|
||||
|
||||
val center = cause.grottoPos
|
||||
val targetEntity = world.getChunk(center)?.getBlockEntity(center) as? GrottoBlockEntity ?: return spawns
|
||||
|
||||
val structure = targetEntity.detectedStructure ?: return spawns
|
||||
val spawnGroup = targetEntity.currentSpawnGroup
|
||||
if (spawnGroup.isEmpty()) return spawns
|
||||
|
||||
val cached = cache[center]
|
||||
if (cached != null && now - cached.second <= CACHE_DURATION_MS) {
|
||||
println("[GrottoSpawner] Cache hit at $center — using group: $spawnGroup")
|
||||
return cached.first
|
||||
}
|
||||
|
||||
val habitat = Habitats.getHabitat(structure)
|
||||
if (habitat == null) {
|
||||
println("[GrottoSpawner] No habitat found for structure: $structure")
|
||||
return spawns
|
||||
}
|
||||
|
||||
val selectedGroup = habitat.spawnPool.find { group ->
|
||||
group.spawns.toSet() == spawnGroup.toSet()
|
||||
} ?: return spawns.also {
|
||||
/*println("[GrottoSpawner] Could not match spawn group at $center to any group in habitat: $structure")
|
||||
println(" ↳ Current spawnGroup: $spawnGroup")
|
||||
println(" ↳ Habitat groups:")
|
||||
habitat.spawnPool.forEachIndexed { i, g ->
|
||||
println(" [$i]: ${g.spawns}")
|
||||
}*/
|
||||
}
|
||||
|
||||
val poolCacheName = "grotto_pool_${center.x}_${center.y}_${center.z}"
|
||||
val pool = SpawnPool(poolCacheName)
|
||||
for (entry in selectedGroup.spawns) {
|
||||
val player = cause.entity as? ServerPlayer
|
||||
val party = player?.party()?.filterNotNull()
|
||||
val maxLevel = party?.maxOfOrNull { it.level } ?: 20
|
||||
val variation = 5
|
||||
|
||||
val minLevel = max(1, maxLevel - variation)
|
||||
val maxLevelFinal = min(100, maxLevel + variation)
|
||||
val level = (minLevel..maxLevelFinal).random()
|
||||
|
||||
val detail = PokemonSpawnDetail().apply {
|
||||
pokemon.species = entry.species.toString()
|
||||
weight = entry.weight.toFloat()
|
||||
pokemon.level = level
|
||||
}
|
||||
|
||||
pool.details.add(detail)
|
||||
}
|
||||
|
||||
/*println("[GrottoSpawner] Constructing pool for Grotto at ${cause.structureCenter}")
|
||||
println(" ↳ Detected structure: $structure")
|
||||
println(" ↳ Spawn group: $spawnGroup")
|
||||
println(" ↳ Selected group: $selectedGroup")
|
||||
println(" ↳ Pool size: ${pool.details.size}")*/
|
||||
|
||||
cache[center] = pool to now
|
||||
return pool
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -142,7 +142,7 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<CompoundTag> {
|
||||
}
|
||||
|
||||
// TODO - Nicknames and Original Trainer Data
|
||||
// result.nickname = this.find(nbt, "Nickname", NbtCompound::getString)
|
||||
// result.nickname = this.find(nbt, "Nickname", CompoundTag::getString)
|
||||
|
||||
val ball = this.find(nbt, "CaughtBall", CompoundTag::getString)
|
||||
result.caughtBall = if(ball != null) PokeBalls.getPokeBall(ResourceLocation.parse(ball)) ?: PokeBalls.POKE_BALL else PokeBalls.POKE_BALL
|
||||
|
||||
@@ -41,6 +41,40 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val HAS_REVIVAL_HERBS = create("has_feature/revival_herbs")
|
||||
|
||||
// Nests
|
||||
@JvmField
|
||||
val HAS_STICK_NEST = create("has_nest/nest_stick")
|
||||
@JvmField
|
||||
val HAS_STICK_NEST_TREES = create("has_nest/nest_stick_trees")
|
||||
@JvmField
|
||||
val HAS_LEAF_NEST_TREES = create("has_nest/nest_leaf_trees")
|
||||
@JvmField
|
||||
val HAS_SNOW_NEST = create("has_nest/nest_snow")
|
||||
@JvmField
|
||||
val HAS_SAND_NEST = create("has_nest/nest_sand")
|
||||
@JvmField
|
||||
val HAS_RED_SAND_NEST = create("has_nest/nest_red_sand")
|
||||
@JvmField
|
||||
val HAS_SEAGRASS_NEST = create("has_nest/nest_seagrass")
|
||||
@JvmField
|
||||
val HAS_WARM_OCEAN_NEST = create("has_nest/nest_warm_ocean")
|
||||
@JvmField
|
||||
val HAS_STONE_NEST = create("has_nest/nest_stone")
|
||||
@JvmField
|
||||
val HAS_STONE_NEST_CAVES = create("has_nest/nest_stone_caves")
|
||||
@JvmField
|
||||
val HAS_DEEPSLATE_NEST_CAVES = create("has_nest/nest_deepslate_caves")
|
||||
@JvmField
|
||||
val HAS_LUSH_NEST_CAVES = create("has_nest/nest_lush_caves")
|
||||
@JvmField
|
||||
val HAS_BASALT_NEST = create("has_nest/nest_basalt")
|
||||
@JvmField
|
||||
val HAS_NETHER_NEST = create("has_nest/nest_nether")
|
||||
@JvmField
|
||||
val HAS_SOUL_SAND_NEST = create("has_nest/nest_soul_sand")
|
||||
@JvmField
|
||||
val HAS_CHORUS_NEST = create("has_nest/nest_chorus")
|
||||
|
||||
// Has Ore tags
|
||||
@JvmField
|
||||
val HAS_DAWN_STONE_ORE = create("has_ore/ore_dawn_stone_normal")
|
||||
|
||||
@@ -103,6 +103,7 @@ object CobblemonItemTags {
|
||||
@JvmField val ZINC_INGREDIENTS = create("zinc_ingredients")
|
||||
|
||||
// Held Item Tags
|
||||
@JvmField val DEFAULTING_POKE_BALLS = create("defaulting_poke_balls")
|
||||
@JvmField val CONSUMED_IN_NPC_BATTLE = create("held/consumed_in_npc_battle")
|
||||
@JvmField val CONSUMED_IN_PVP_BATTLE = create("held/consumed_in_pvp_battle")
|
||||
@JvmField val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
|
||||
|
||||
@@ -14,9 +14,11 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.battles.actor.MultiPokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -297,6 +299,99 @@ object BattleBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun pveDoubles(
|
||||
player: ServerPlayer,
|
||||
pokemonEntity1: PokemonEntity,
|
||||
pokemonEntity2: PokemonEntity,
|
||||
leadingPokemon: UUID? = null,
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_DOUBLES,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
fleeDistance: Float = Cobblemon.config.defaultFleeDistance,
|
||||
party: PartyStore = player.party()
|
||||
): BattleStartResult {
|
||||
// Prepare the player's team
|
||||
val playerTeam = party.toBattleTeam(
|
||||
clone = cloneParties,
|
||||
healPokemon = healFirst,
|
||||
leadingPokemon = leadingPokemon
|
||||
).sortedBy { it.health <= 0 }
|
||||
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
|
||||
// Combine both wild Pokémon into a single BattleActor
|
||||
val wildTeam = listOf(
|
||||
BattlePokemon(pokemonEntity1.pokemon),
|
||||
BattlePokemon(pokemonEntity2.pokemon)
|
||||
)
|
||||
val wildActor = MultiPokemonBattleActor(pokemonList = wildTeam, fleeDistance = Cobblemon.config.defaultFleeDistance)
|
||||
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
// Validate player's team
|
||||
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
)
|
||||
}
|
||||
|
||||
// Validate opponent Pokémon entities
|
||||
if (pokemonEntity1.battleId != null) {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
if (pokemonEntity2.battleId != null) {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
// Set battle themes
|
||||
playerActor.battleTheme = pokemonEntity1.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
println("Player actor: ${playerActor.pokemonList.size} Pokémon")
|
||||
println("WildActor Pokémon: ${wildActor.pokemonList.map { it.effectedPokemon.species.translatedName }}")
|
||||
|
||||
val side1 = BattleSide(playerActor)
|
||||
val side2 = BattleSide(wildActor)
|
||||
|
||||
println("Side1 actors: ${side1.actors.map { it.uuid }}")
|
||||
println("Side2 actors: ${side2.actors.map { it.uuid }}")
|
||||
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = side1,
|
||||
side2 = side2
|
||||
).ifSuccessful { battle ->
|
||||
println("Battle started with ID: ${battle.battleId}")
|
||||
if (!cloneParties) {
|
||||
pokemonEntity1.battleId = battle.battleId
|
||||
pokemonEntity2.battleId = battle.battleId
|
||||
}
|
||||
}.ifErrored {
|
||||
println("Battle start failed: ${it.errors}")
|
||||
}
|
||||
} else {
|
||||
println("Battle failed due to validation errors: ${errors.errors}")
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to create a PvE battle against the given Pokémon.
|
||||
*
|
||||
|
||||
+38
-4
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.battles.actor
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.ai.RandomBattleAI
|
||||
@@ -17,13 +18,46 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class MultiPokemonBattleActor(
|
||||
pokemonList: List<BattlePokemon>,
|
||||
artificialDecider: BattleAI = RandomBattleAI(),
|
||||
uuid: UUID = UUID.randomUUID()
|
||||
) : AIBattleActor(uuid, pokemonList, artificialDecider) {
|
||||
pokemonList: List<BattlePokemon>,
|
||||
override val fleeDistance: Float,
|
||||
artificialDecider: BattleAI = RandomBattleAI(),
|
||||
uuid: UUID = UUID.randomUUID()
|
||||
) : AIBattleActor(uuid, pokemonList, artificialDecider), FleeableBattleActor {
|
||||
override fun getName(): MutableComponent = "Wild Pokémon".text() // TODO probably remove by making it nullable
|
||||
override fun nameOwned(name: String): MutableComponent = Component.literal(name)
|
||||
override val type = ActorType.WILD
|
||||
override fun getWorldAndPosition(): Pair<ServerLevel, Vec3>? {
|
||||
// Ensure there are Pokémon in the list
|
||||
if (pokemonList.isEmpty()) return null
|
||||
|
||||
// Gather all positions and ensure they share the same world
|
||||
val positionsAndWorlds = pokemonList.mapNotNull { battlePokemon ->
|
||||
val entity = battlePokemon.effectedPokemon.entity
|
||||
val level = entity?.level() as? ServerLevel
|
||||
val position = entity?.position()
|
||||
if (level != null && position != null) {
|
||||
level to position
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (positionsAndWorlds.isEmpty()) return null
|
||||
|
||||
// Ensure all Pokémon are in the same world
|
||||
val world = positionsAndWorlds.first().first
|
||||
if (positionsAndWorlds.any { it.first != world }) return null
|
||||
|
||||
// Compute the average position
|
||||
val averagePosition = positionsAndWorlds
|
||||
.map { it.second }
|
||||
.reduce { acc, pos -> acc.add(pos) }
|
||||
.scale(1.0 / positionsAndWorlds.size)
|
||||
|
||||
return world to averagePosition
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.HealingMachineBlock.Companion
|
||||
import com.cobblemon.mod.common.block.HealingMachineBlock.Companion.CHARGE_LEVEL
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.item.PokemonEggItem
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.mojang.serialization.Dynamic
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class EggBlock(properties: Properties) : BaseEntityBlock(properties) {
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
blockState: BlockState,
|
||||
placer: LivingEntity?,
|
||||
itemStack: ItemStack
|
||||
) {
|
||||
val entity = level.getBlockEntity(blockPos) as? EggBlockEntity
|
||||
|
||||
// Retrieve the EGG_COMPONENT from the item stack and set it in the block entity
|
||||
val eggComponent = itemStack.get(CobblemonItemComponents.EGG_DATA)
|
||||
eggComponent?.let { component ->
|
||||
entity?.setEggData(component.egg) // Pass the Egg data to the block entity
|
||||
println("Set egg in EggBlockEntity: ${component.egg?.species}")
|
||||
}
|
||||
|
||||
super.setPlacedBy(level, blockPos, blockState, placer, itemStack)
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
|
||||
): VoxelShape {
|
||||
return Shapes.or(
|
||||
Shapes.box(5.0 / 16.0, 0.0, 5.0 / 16.0, 11.0 / 16.0, 2.0 / 16.0, 11.0 / 16.0), // Bottom section
|
||||
Shapes.box(4.5 / 16.0, 1.25 / 16.0, 4.5 / 16.0, 11.5 / 16.0, 5.25 / 16.0, 11.5 / 16.0), // Middle section
|
||||
Shapes.box(5.0 / 16.0, 5.25 / 16.0, 5.0 / 16.0, 11.0 / 16.0, 7.25 / 16.0, 11.0 / 16.0), // Upper section
|
||||
Shapes.box(6.0 / 16.0, 7.25 / 16.0, 6.0 / 16.0, 10.0 / 16.0, 8.25 / 16.0, 10.0 / 16.0) // Top small section
|
||||
)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
// Ensure this logic only runs on the server
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS // Client-side only needs to show the hand animation
|
||||
}
|
||||
|
||||
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL // Cast safely to ServerPlayer
|
||||
|
||||
val entity = level.getBlockEntity(blockPos) as? EggBlockEntity ?: return InteractionResult.FAIL
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
val entity = level.getBlockEntity(pos) as? EggBlockEntity
|
||||
|
||||
if (entity != null && !player.isCreative) {
|
||||
entity.dropEgg(state, level, pos)
|
||||
}
|
||||
|
||||
return super.playerWillDestroy(level, pos, state, player)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getRenderShape(blockState: BlockState): RenderShape {
|
||||
return RenderShape.ENTITYBLOCK_ANIMATED
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState) = EggBlockEntity(pos, state)
|
||||
}
|
||||
@@ -32,7 +32,8 @@ import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
|
||||
class FossilAnalyzerBlock(settings: Properties) : MultiblockBlock(settings), WorldlyContainerHolder {
|
||||
class FossilAnalyzerBlock(settings: Properties) : MultiblockBlock(settings),
|
||||
WorldlyContainerHolder {
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH)
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.EntityBlock
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.storage.loot.LootContext
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import java.util.*
|
||||
|
||||
class GrottoBlock(properties: Properties) : Block(properties), EntityBlock {
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return GrottoBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
level: Level,
|
||||
state: BlockState,
|
||||
type: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
return if (!level.isClientSide && type == CobblemonBlockEntities.GROTTO_BLOCK) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
GrottoBlockEntity.TICKER as BlockEntityTicker<T>
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
|
||||
if (!level.isClientSide) {
|
||||
val be = level.getBlockEntity(pos) as? GrottoBlockEntity ?: return InteractionResult.PASS
|
||||
return be.onUse(player, hitResult)
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList<ItemStack> {
|
||||
val blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) as? GrottoBlockEntity
|
||||
val player = params.getOptionalParameter(LootContextParams.THIS_ENTITY) as? Player
|
||||
|
||||
if (player?.isCreative == true) return mutableListOf()
|
||||
|
||||
val mimic = blockEntity?.mimickedState?.block ?: return mutableListOf()
|
||||
return mutableListOf(ItemStack(mimic))
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape {
|
||||
return RenderShape.INVISIBLE
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getCollisionShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getVisualShape(
|
||||
state: BlockState,
|
||||
world: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getOcclusionShape(state: BlockState, level: BlockGetter, pos: BlockPos): VoxelShape? {
|
||||
return Shapes.block()
|
||||
}
|
||||
|
||||
override fun useShapeForLightOcclusion(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.AirItem
|
||||
import net.minecraft.world.item.EggItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.PickaxeItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class IncubatorBlock(settings: Properties) : BaseEntityBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val FACING: DirectionProperty = DirectionProperty.create("facing", { dir -> dir.axis.isHorizontal })
|
||||
val HAS_EGG: BooleanProperty = BooleanProperty.create("has_egg")
|
||||
val CODEC: MapCodec<IncubatorBlock> = simpleCodec(::IncubatorBlock) as MapCodec<IncubatorBlock>
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, net.minecraft.core.Direction.NORTH)
|
||||
.setValue(HAS_EGG, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
|
||||
return Shapes.or(
|
||||
// Base
|
||||
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0),
|
||||
// Top
|
||||
Shapes.box(2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0, 1.0, 14.0 / 16.0),
|
||||
// Unified Glass Section (Covers all four glass sides as one box)
|
||||
Shapes.box(2.5 / 16.0, 3.0 / 16.0, 2.5 / 16.0, 13.5 / 16.0, 14.0 / 16.0, 13.5 / 16.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
if (placer is Player) {
|
||||
blockEntity?.ownerUUID = placer.uuid
|
||||
}
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, HAS_EGG)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): IncubatorBlockEntity {
|
||||
return IncubatorBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
// Ensure this logic only runs on the server
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS // Client-side only needs to show the hand animation
|
||||
}
|
||||
|
||||
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL // Cast safely to ServerPlayer
|
||||
|
||||
val entity = level.getBlockEntity(blockPos) as? IncubatorBlockEntity ?: return InteractionResult.FAIL
|
||||
|
||||
val egg = entity.getItem(0).get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
|
||||
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
|
||||
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
// if the person interacting is not the owner of one of the parents
|
||||
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
return blockEntity?.getComparatorOutput() ?: 0
|
||||
}
|
||||
|
||||
|
||||
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
|
||||
// Retrieve the block entity at the given position
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
|
||||
|
||||
// Check if the player is the owner of the nest
|
||||
val isOwner = blockEntity.ownerUUID == player.uuid
|
||||
|
||||
// Check if there's an egg present in the nest
|
||||
val hasEgg = !blockEntity.getItem(0).isEmpty
|
||||
|
||||
return if (!isOwner && hasEgg) {
|
||||
// Reduce destroy speed by 80%, making it 5 times harder to break
|
||||
super.getDestroyProgress(state, player, level, pos) * 0.2f
|
||||
} else {
|
||||
// Use the default destroy speed
|
||||
super.getDestroyProgress(state, player, level, pos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
val entity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
val itemStack = player.mainHandItem
|
||||
|
||||
entity?.let {
|
||||
if (!player.isCreative) {
|
||||
it.dropItem(level, pos, state)
|
||||
if (itemStack.item is PickaxeItem) {
|
||||
val itemEntity = ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.INCUBATOR.defaultInstance)
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(level, pos, state, player)
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
||||
return createTickerHelper(blockEntityType, CobblemonBlockEntities.INCUBATOR, IncubatorBlockEntity.TICKER)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.PickaxeItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class NestBlock(val variant: NestVariant, properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
|
||||
|
||||
init {
|
||||
registerDefaultState(
|
||||
stateDefinition.any()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
enum class NestVariant(val id: String) : StringRepresentable {
|
||||
STICK("nest_stick"),
|
||||
LEAF("nest_leaf"),
|
||||
STONE("nest_stone"),
|
||||
DEEPSLATE("nest_deepslate"),
|
||||
BASALT("nest_basalt"),
|
||||
SNOW("nest_snow"),
|
||||
NETHER("nest_nether"),
|
||||
SOUL_SAND("nest_soul_sand"),
|
||||
WARM_OCEAN("nest_warm_ocean"),
|
||||
SEAGRASS("nest_seagrass"),
|
||||
LUSH("nest_lush"),
|
||||
HAY("nest_hay"),
|
||||
SAND("nest_sand"),
|
||||
RED_SAND("nest_red_sand"),
|
||||
CHORUS("nest_chorus"),
|
||||
|
||||
CAVE("cave_nest"),
|
||||
//NETHER("nether_nest"),
|
||||
WATER("water_nest"),
|
||||
BASIC("basic_nest");
|
||||
|
||||
override fun getSerializedName() = id
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
|
||||
return when (variant) {
|
||||
NestVariant.STICK -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base
|
||||
Shapes.box(2.8 / 16.0, 0.0 / 16.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
|
||||
Shapes.box(12.0 / 16.0, 3.0 / 16.0, 11.5 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 15.5 / 16.0), // Stick 1
|
||||
Shapes.box(13.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 12.0 / 16.0), // Stick 2
|
||||
Shapes.box(1.5 / 16.0, 2.0 / 16.0, 12.0 / 16.0, 4.5 / 16.0, 6.0 / 16.0, 12.0 / 16.0), // Stick 3
|
||||
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 1.0 / 16.0, 9.0 / 16.0), // Stick 4
|
||||
Shapes.box(4.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 11.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Stick 5
|
||||
Shapes.box(9.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 6.0 / 16.0), // Stick 6
|
||||
Shapes.box(6.0 / 16.0, 3.0 / 16.0, 0.5 / 16.0, 6.0 / 16.0, 6.0 / 16.0, 4.5 / 16.0) // Stick 7
|
||||
)
|
||||
NestVariant.LEAF -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base of the nest
|
||||
Shapes.box(2.8 / 16.0, 0.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
|
||||
Shapes.box(11.975 / 16.0, 6.0 / 16.0, 12.0 / 16.0, 11.975 / 16.0, 10.0 / 16.0, 15.0 / 16.0), // Leaf 1
|
||||
Shapes.box(12.025 / 16.0, 5.525 / 16.0, 6.0 / 16.0, 16.025 / 16.0, 5.525 / 16.0, 9.0 / 16.0), // Leaf 2
|
||||
Shapes.box(12.025 / 16.0, 8.0 / 16.0, 4.0 / 16.0, 12.025 / 16.0, 12.0 / 16.0, 7.0 / 16.0), // Leaf 3
|
||||
Shapes.box(0.0 / 16.0, 5.0 / 16.0, 11.975 / 16.0, 3.0 / 16.0, 9.0 / 16.0, 11.975 / 16.0), // Leaf 4
|
||||
Shapes.box(5.25 / 16.0, 7.75 / 16.0, 12.025 / 16.0, 9.25 / 16.0, 10.75 / 16.0, 12.025 / 16.0), // Leaf 5
|
||||
Shapes.box(6.025 / 16.0, 10.0 / 16.0, 2.25 / 16.0, 6.025 / 16.0, 14.0 / 16.0, 5.25 / 16.0), // Leaf 6
|
||||
Shapes.box(0.525 / 16.0, 8.0 / 16.0, 6.5 / 16.0, 3.525 / 16.0, 12.0 / 16.0, 6.5 / 16.0), // Leaf 7
|
||||
Shapes.box(13.975 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 15.975 / 16.0, 3.0 / 16.0, 5.0 / 16.0) // Leaf 8
|
||||
)
|
||||
NestVariant.STONE -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(10.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, 0.0 / 16.0, 5.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 9.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 5.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, -0.1 / 16.0, 11.0 / 16.0, 8.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0)
|
||||
)
|
||||
NestVariant.DEEPSLATE -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(2.0 / 16.0, 0.0 / 16.0, 2.0 / 16.0, 6.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, -0.05 / 16.0, 5.0 / 16.0, 5.0 / 16.0, 5.95 / 16.0, 9.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 15.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(10.0 / 16.0, -0.1 / 16.0, 14.0 / 16.0, 11.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(8.0 / 16.0, -0.125 / 16.0, 11.0 / 16.0, 11.0 / 16.0, 6.875 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(8.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 5.0 / 16.0)
|
||||
)
|
||||
NestVariant.BASALT -> Shapes.or(
|
||||
Shapes.box(4.0 / 16.0, -0.1 / 16.0, 4.0 / 16.0, 12.0 / 16.0, 1.9 / 16.0, 12.0 / 16.0),
|
||||
Shapes.box(10.55 / 16.0, 0.0 / 16.0, 5.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
|
||||
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 10.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 12.95 / 16.0),
|
||||
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 9.95 / 16.0, 4.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
|
||||
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 9.95 / 16.0, 13.55 / 16.0, 8.8 / 16.0, 13.95 / 16.0),
|
||||
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 0.95 / 16.0, 13.55 / 16.0, 3.8 / 16.0, 4.95 / 16.0),
|
||||
Shapes.box(0.55 / 16.0, -0.2 / 16.0, 5.95 / 16.0, 4.55 / 16.0, 5.8 / 16.0, 9.95 / 16.0),
|
||||
Shapes.box(1.55 / 16.0, -0.2 / 16.0, 2.95 / 16.0, 4.55 / 16.0, 8.8 / 16.0, 5.95 / 16.0),
|
||||
Shapes.box(3.45 / 16.0, -0.2 / 16.0, 11.05 / 16.0, 6.45 / 16.0, 11.8 / 16.0, 14.05 / 16.0),
|
||||
Shapes.box(11.55 / 16.0, -0.1 / 16.0, 4.95 / 16.0, 14.55 / 16.0, 5.9 / 16.0, 7.95 / 16.0)
|
||||
)
|
||||
NestVariant.SNOW -> Shapes.or(
|
||||
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 4.0 / 16.0, 11.0 / 16.0),
|
||||
Shapes.box(3.0 / 16.0, 1.1 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 6.1 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, 1.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 8.0 / 16.0, 5.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, 5.0 / 16.0, 11.0 / 16.0, 7.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(0.25 / 16.0, 7.6 / 16.0, 10.0 / 16.0, 5.25 / 16.0, 14.6 / 16.0, 10.0 / 16.0),
|
||||
Shapes.box(13.5 / 16.0, 4.6 / 16.0, 6.0 / 16.0, 16.5 / 16.0, 8.6 / 16.0, 6.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, 4.6 / 16.0, 12.75 / 16.0, 11.0 / 16.0, 10.6 / 16.0, 16.75 / 16.0),
|
||||
Shapes.box(0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 16.0 / 16.0)
|
||||
)
|
||||
NestVariant.CAVE -> Shapes.or(
|
||||
Shapes.box(0.25, 0.0, 0.25, 0.75, 0.375, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 0.875)
|
||||
)
|
||||
NestVariant.NETHER -> Shapes.or(
|
||||
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(0.0 / 16.0, 0.0, 6.0 / 16.0, 4.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0), // Left extension
|
||||
Shapes.box(7.0 / 16.0, 0.0, 0.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0), // Front extension
|
||||
Shapes.box(11.0 / 16.0, 0.0, 11.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0) // Back right extension
|
||||
)
|
||||
NestVariant.WATER -> Shapes.box(0.2, 0.0, 0.2, 0.8, 0.3, 0.8)
|
||||
NestVariant.BASIC -> Shapes.box(0.1, 0.0, 0.1, 0.9, 0.45, 0.9)
|
||||
NestVariant.SOUL_SAND -> Shapes.or(
|
||||
Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Base layer
|
||||
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Inner rim
|
||||
Shapes.box(12.0 / 16.0, 0.0 / 16.0, 10.0 / 16.0, 16.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0), // Side bump 1
|
||||
Shapes.box(3.0 / 16.0, 0.0 / 16.0, 13.0 / 16.0, 6.0 / 16.0, 7.0 / 16.0, 16.0 / 16.0), // Side bump 2
|
||||
Shapes.box(1.0 / 16.0, 2.0 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 6.0 / 16.0, 6.0 / 16.0) // Side bump 3
|
||||
)
|
||||
NestVariant.WARM_OCEAN -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0)
|
||||
NestVariant.SEAGRASS -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0), // Main base
|
||||
Shapes.box(1.0 / 16.0, 0.0, 7.0 / 16.0, 5.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Side protrusion 1
|
||||
Shapes.box(5.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 9.0 / 16.0), // Side protrusion 2
|
||||
Shapes.box(6.0 / 16.0, 0.0, 13.0 / 16.0, 16.0 / 16.0, 6.0 / 16.0, 14.0 / 16.0) // Backside bump
|
||||
)
|
||||
NestVariant.LUSH -> Shapes.or(
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 12.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Main base
|
||||
Shapes.box(0.0 / 16.0, 0.0, 0.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 12.0 / 16.0), // Side extension 1
|
||||
Shapes.box(0.0 / 16.0, 0.0, 12.0 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 16.0 / 16.0), // Side extension 2
|
||||
Shapes.box(12.0 / 16.0, 0.0, 8.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0), // Side extension 3
|
||||
Shapes.box(12.0 / 16.0, 0.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 8.0 / 16.0) // Side extension 4
|
||||
)
|
||||
NestVariant.HAY -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0)
|
||||
NestVariant.SAND -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
|
||||
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
|
||||
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
|
||||
)
|
||||
NestVariant.RED_SAND -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
|
||||
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
|
||||
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
|
||||
)
|
||||
NestVariant.CHORUS -> Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 4.0 / 16.0, 15.0 / 16.0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, isMoving: Boolean) {
|
||||
super.onPlace(state, level, pos, oldState, isMoving)
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(BlockStateProperties.WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun neighborChanged(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
block: Block,
|
||||
fromPos: BlockPos,
|
||||
isMoving: Boolean
|
||||
) {
|
||||
super.neighborChanged(state, level, pos, block, fromPos, isMoving)
|
||||
|
||||
// Check if the block below is air
|
||||
val blockBelowPos = pos.below()
|
||||
if (fromPos == blockBelowPos && level.getBlockState(blockBelowPos).isAir) {
|
||||
level.destroyBlock(pos, false)
|
||||
}
|
||||
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
}
|
||||
|
||||
private fun checkAndBreakIfSubmerged(state: BlockState, levelAccessor: LevelAccessor, pos: BlockPos) {
|
||||
val fluidState = levelAccessor.getFluidState(pos)
|
||||
|
||||
if ((fluidState.type == Fluids.WATER || fluidState.isSource) && !canBeWaterlogged()) {
|
||||
if (levelAccessor is Level) {
|
||||
levelAccessor.destroyBlock(pos, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun canBeWaterlogged(): Boolean {
|
||||
return variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
// Ensure this logic only runs on the server
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL
|
||||
|
||||
val entity = level.getBlockEntity(blockPos) as? NestBlockEntity ?: return InteractionResult.FAIL
|
||||
|
||||
val egg = entity.getItem().get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
|
||||
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
|
||||
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
// if the person interacting is not the owner of one of the parents
|
||||
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
|
||||
// Retrieve the block entity at the given position
|
||||
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
|
||||
|
||||
// Check if the player is the owner of the nest
|
||||
val isOwner = blockEntity.ownerUUID == player.uuid
|
||||
|
||||
// Check if there's an egg present in the nest
|
||||
val hasEgg = !blockEntity.getItem(0).isEmpty
|
||||
|
||||
return if (!isOwner && hasEgg) {
|
||||
// Reduce destroy speed making it 5 times harder to break
|
||||
super.getDestroyProgress(state, player, level, pos) * 0.2f
|
||||
} else {
|
||||
super.getDestroyProgress(state, player, level, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// todo we want destroying to take longer if it is not their nest
|
||||
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
val entity = level.getBlockEntity(pos) as? NestBlockEntity
|
||||
|
||||
// todo we do silk touch now
|
||||
/*entity?.let {
|
||||
if (!player.isCreative) {
|
||||
val itemEntity = when (this.variant) {
|
||||
NestVariant.BASIC -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.BASE_NEST.defaultInstance)
|
||||
NestVariant.WATER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.WATER_NEST.defaultInstance)
|
||||
NestVariant.CAVE -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.CAVE_NEST.defaultInstance)
|
||||
NestVariant.NETHER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.NETHER_NEST.defaultInstance)
|
||||
}
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
it.dropItem( level, pos, state)
|
||||
}*/
|
||||
|
||||
return super.playerWillDestroy(level, pos, state, player)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState) = NestBlockEntity(pos, state)
|
||||
|
||||
override fun getRenderShape(blockState: BlockState) = RenderShape.MODEL
|
||||
|
||||
override fun canPlaceLiquid(
|
||||
player: Player?,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
fluid: Fluid
|
||||
): Boolean {
|
||||
return (variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN) && super.canPlaceLiquid(player, level, pos, state, fluid)
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||
Fluids.WATER.getSource(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity
|
||||
if (placer is Player) {
|
||||
blockEntity?.ownerUUID = placer.uuid
|
||||
}
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
}
|
||||
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
val fluidState = ctx.level.getFluidState(ctx.clickedPos)
|
||||
|
||||
// If the nest variant can be waterlogged, set it accordingly
|
||||
if (canBeWaterlogged()) {
|
||||
return defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, fluidState.type == Fluids.WATER)
|
||||
}
|
||||
|
||||
// Prevent placement if the block is submerged in water
|
||||
if (!canBeWaterlogged() && fluidState.type == Fluids.WATER) {
|
||||
return null
|
||||
}
|
||||
|
||||
return defaultBlockState()
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
||||
return createTickerHelper(blockEntityType, CobblemonBlockEntities.NEST, NestBlockEntity.TICKER)
|
||||
}
|
||||
|
||||
|
||||
override fun codec(): MapCodec<NestBlock> {
|
||||
return RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.fieldOf("variant").forGetter { block -> block.variant.id }, // Serializes the variant
|
||||
Codec.BOOL.optionalFieldOf("waterlogged", false).forGetter { block ->
|
||||
block.defaultBlockState().getValue(BlockStateProperties.WATERLOGGED)
|
||||
}
|
||||
).apply(instance) { variant, waterlogged ->
|
||||
val block = NestBlock(NestVariant.valueOf(variant.uppercase()), Properties.of().noOcclusion())
|
||||
block.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, waterlogged)
|
||||
block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import net.minecraft.world.level.gameevent.GameEvent
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state) {
|
||||
lateinit var berryIdentifier: ResourceLocation
|
||||
private val ticksPerMinute = 1200
|
||||
var renderState: RenderState? = null
|
||||
var renderState: BlockEntityRenderState? = null
|
||||
//The time left for the tree until its either age 3 or age 5 (check block state, if we are at age 0<x<3 unti 3 otherwise until 5)
|
||||
var growthTimer: Int = 0
|
||||
set(value) {
|
||||
@@ -346,11 +346,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
return
|
||||
}
|
||||
|
||||
//Using a similar approach to ComputerCraft, implement this client side
|
||||
interface RenderState : AutoCloseable {
|
||||
var needsRebuild: Boolean
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal val TICKER = BlockEntityTicker<BerryBlockEntity> { world, pos, state, blockEntity ->
|
||||
if (world.isClientSide) return@BlockEntityTicker
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
//Using a similar approach to ComputerCraft, implement this client side
|
||||
interface BlockEntityRenderState : AutoCloseable {
|
||||
var needsRebuild: Boolean
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.level.block.Block
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import java.util.*
|
||||
|
||||
class EggBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.EGG, pos, state) {
|
||||
private var eggComponent: EggComponent? = null
|
||||
var renderState: BlockEntityRenderState? = null
|
||||
|
||||
// Component data codec
|
||||
companion object {
|
||||
val CODEC: Codec<EggBlockEntity> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.optionalFieldOf("species", cobblemonResource("unknown")).forGetter { it.getEgg()?.species ?: cobblemonResource("unknown") },
|
||||
Pokemon.CODEC.optionalFieldOf("parentOne").forGetter { Optional.ofNullable(it.getEgg()?.parentOne) },
|
||||
Pokemon.CODEC.optionalFieldOf("parentTwo").forGetter { Optional.ofNullable(it.getEgg()?.parentTwo) },
|
||||
Codec.STRING.optionalFieldOf("form", "").forGetter { it.getEgg()?.form ?: "" },
|
||||
Codec.FLOAT.optionalFieldOf("timeToHatch", 0F).forGetter { it.getEgg()?.timeToHatch ?: 0F }
|
||||
).apply(instance) { species, parentOneOpt, parentTwoOpt, form, timeToHatch ->
|
||||
EggBlockEntity(BlockPos(0, 0, 0), Blocks.AIR.defaultBlockState()).apply {
|
||||
setEgg(
|
||||
Egg(
|
||||
species = species,
|
||||
parentOne = parentOneOpt.orElse(null),
|
||||
parentTwo = parentTwoOpt.orElse(null),
|
||||
form = form,
|
||||
timeToHatch = timeToHatch
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the egg data and notifies the client.
|
||||
*/
|
||||
fun setEggData(egg: Egg?) {
|
||||
this.eggComponent = EggComponent(egg)
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Egg data from the EggComponent.
|
||||
*/
|
||||
fun getEgg(): Egg? {
|
||||
return eggComponent?.egg
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the EggComponent and synchronizes with the client.
|
||||
*/
|
||||
fun setEgg(egg: Egg?) {
|
||||
this.eggComponent = EggComponent(egg)
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the EggComponent data manually to NBT.
|
||||
*/
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
eggComponent?.egg?.let { egg ->
|
||||
Egg.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, egg)
|
||||
.result()
|
||||
?.ifPresent { encoded -> tag.put("EggComponent", encoded) }
|
||||
}
|
||||
}
|
||||
|
||||
fun dropEgg(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos
|
||||
) {
|
||||
val currentEgg = getEgg() ?: return
|
||||
|
||||
// Log before clearing
|
||||
println("Dropping egg: ${currentEgg.species}, Form: ${currentEgg.form} at $pos")
|
||||
|
||||
// Create an ItemStack with the EggComponent
|
||||
val eggItemStack = CobblemonItems.POKEMON_EGG.defaultInstance.apply {
|
||||
set(CobblemonItemComponents.EGG_DATA, EggComponent(currentEgg))
|
||||
}
|
||||
|
||||
// Debug log to verify component data
|
||||
val component = eggItemStack.get(CobblemonItemComponents.EGG_DATA)
|
||||
println("EggComponent on dropped ItemStack: $component")
|
||||
|
||||
// Spawn the egg as an entity
|
||||
val itemEntity = ItemEntity(world, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, eggItemStack)
|
||||
world.addFreshEntity(itemEntity)
|
||||
|
||||
// Remove egg block
|
||||
world.removeBlockEntity(pos)
|
||||
world.removeBlock(pos, false)
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
fun onUse(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: ServerPlayer,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
val currentEgg = getEgg()
|
||||
|
||||
if (currentEgg != null) {
|
||||
dropEgg(state, world, pos)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the EggComponent data manually from NBT.
|
||||
*/
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
if (tag.contains("EggComponent")) {
|
||||
val egg = Egg.CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, tag.get("EggComponent"))
|
||||
.result()
|
||||
?.orElse(null)
|
||||
eggComponent = if (egg != null) EggComponent(egg) else null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
val tag = saveWithoutMetadata(registryLookup)
|
||||
return tag
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.habitats.Habitats
|
||||
import com.cobblemon.mod.common.api.habitats.WeightedSpecies
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
||||
class GrottoBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
BlockEntity(CobblemonBlockEntities.GROTTO_BLOCK, pos, state) {
|
||||
|
||||
var detectedStructure: ResourceLocation? = null
|
||||
var structureCenter: BlockPos? = null
|
||||
var hasCheckedStructure: Boolean = false
|
||||
var lastMoonPhaseChecked: Int = -1
|
||||
var currentSpawnGroup: List<WeightedSpecies> = emptyList()
|
||||
|
||||
var mimicId: ResourceLocation = BuiltInRegistries.BLOCK.getKey(Blocks.SPONGE)
|
||||
val mimickedState: BlockState
|
||||
get() = BuiltInRegistries.BLOCK.get(mimicId).defaultBlockState() ?: Blocks.SPONGE.defaultBlockState()
|
||||
|
||||
fun setMimic(id: ResourceLocation) {
|
||||
mimicId = id
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(worldPosition, blockState, blockState, Block.UPDATE_ALL)
|
||||
}
|
||||
|
||||
// todo to test with checking how it looks as other blocks
|
||||
val mimicCycle: List<ResourceLocation> = listOf(
|
||||
ResourceLocation.parse("minecraft:stone"),
|
||||
ResourceLocation.parse("minecraft:cobblestone"),
|
||||
ResourceLocation.parse("minecraft:granite"),
|
||||
ResourceLocation.parse("minecraft:diorite"),
|
||||
ResourceLocation.parse("minecraft:andesite"),
|
||||
ResourceLocation.parse("minecraft:deepslate"),
|
||||
ResourceLocation.parse("minecraft:polished_deepslate"),
|
||||
|
||||
ResourceLocation.parse("minecraft:oak_planks"),
|
||||
ResourceLocation.parse("minecraft:spruce_planks"),
|
||||
ResourceLocation.parse("minecraft:birch_planks"),
|
||||
ResourceLocation.parse("minecraft:jungle_planks"),
|
||||
ResourceLocation.parse("minecraft:acacia_planks"),
|
||||
ResourceLocation.parse("minecraft:dark_oak_planks"),
|
||||
ResourceLocation.parse("minecraft:mangrove_planks"),
|
||||
ResourceLocation.parse("minecraft:cherry_planks"),
|
||||
ResourceLocation.parse("minecraft:bamboo_planks"),
|
||||
|
||||
ResourceLocation.parse("minecraft:bricks"),
|
||||
ResourceLocation.parse("minecraft:mossy_cobblestone"),
|
||||
ResourceLocation.parse("minecraft:moss_block"),
|
||||
ResourceLocation.parse("minecraft:bookshelf"),
|
||||
ResourceLocation.parse("minecraft:loom"),
|
||||
ResourceLocation.parse("minecraft:note_block"),
|
||||
|
||||
ResourceLocation.parse("minecraft:coal_block"),
|
||||
ResourceLocation.parse("minecraft:iron_block"),
|
||||
ResourceLocation.parse("minecraft:gold_block"),
|
||||
ResourceLocation.parse("minecraft:diamond_block"),
|
||||
ResourceLocation.parse("minecraft:emerald_block"),
|
||||
ResourceLocation.parse("minecraft:lapis_block"),
|
||||
ResourceLocation.parse("minecraft:redstone_block"),
|
||||
ResourceLocation.parse("minecraft:netherite_block"),
|
||||
|
||||
ResourceLocation.parse("minecraft:obsidian"),
|
||||
ResourceLocation.parse("minecraft:crying_obsidian"),
|
||||
ResourceLocation.parse("minecraft:glowstone"),
|
||||
ResourceLocation.parse("minecraft:blue_ice"),
|
||||
ResourceLocation.parse("minecraft:packed_ice"),
|
||||
ResourceLocation.parse("minecraft:ice"),
|
||||
|
||||
ResourceLocation.parse("minecraft:sandstone"),
|
||||
ResourceLocation.parse("minecraft:red_sandstone"),
|
||||
ResourceLocation.parse("minecraft:cut_sandstone"),
|
||||
ResourceLocation.parse("minecraft:smooth_sandstone"),
|
||||
|
||||
ResourceLocation.parse("minecraft:black_concrete"),
|
||||
ResourceLocation.parse("minecraft:white_concrete"),
|
||||
ResourceLocation.parse("minecraft:lime_concrete"),
|
||||
ResourceLocation.parse("minecraft:cyan_concrete"),
|
||||
ResourceLocation.parse("minecraft:purple_concrete"),
|
||||
ResourceLocation.parse("minecraft:light_blue_concrete"),
|
||||
ResourceLocation.parse("minecraft:yellow_concrete"),
|
||||
ResourceLocation.parse("minecraft:red_concrete")
|
||||
)
|
||||
|
||||
var mimicIndex: Int = 0
|
||||
|
||||
fun cycleMimic() {
|
||||
mimicIndex = (mimicIndex + 1) % mimicCycle.size
|
||||
mimicId = mimicCycle[mimicIndex]
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(worldPosition, blockState, blockState, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
fun onUse(player: Player, hit: BlockHitResult): InteractionResult {
|
||||
cycleMimic()
|
||||
player.displayClientMessage(Component.literal("Mimicking: $mimicId"), true)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registryLookup)
|
||||
tag.putString("MimicId", mimicId.toString())
|
||||
detectedStructure?.let { tag.putString("DetectedStructure", it.toString()) }
|
||||
tag.putBoolean("HasCheckedStructure", hasCheckedStructure)
|
||||
|
||||
val list = ListTag()
|
||||
currentSpawnGroup.forEach {
|
||||
val entry = CompoundTag()
|
||||
entry.putString("species", it.species.toString())
|
||||
entry.putInt("weight", it.weight)
|
||||
list.add(entry)
|
||||
}
|
||||
tag.put("CurrentSpawnGroup", list)
|
||||
|
||||
structureCenter?.let {
|
||||
val centerTag = CompoundTag()
|
||||
centerTag.putInt("X", it.x)
|
||||
centerTag.putInt("Y", it.y)
|
||||
centerTag.putInt("Z", it.z)
|
||||
tag.put("StructureCenter", centerTag)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registryLookup)
|
||||
tag.getString("MimicId").let {
|
||||
mimicId = ResourceLocation.tryParse(it) ?: BuiltInRegistries.BLOCK.getKey(Blocks.SPONGE)
|
||||
}
|
||||
hasCheckedStructure = tag.getBoolean("HasCheckedStructure")
|
||||
detectedStructure = tag.getString("DetectedStructure").takeIf { it.isNotEmpty() }?.let(ResourceLocation::tryParse)
|
||||
|
||||
val list = tag.getList("CurrentSpawnGroup", Tag.TAG_COMPOUND.toInt())
|
||||
currentSpawnGroup = list.mapNotNull {
|
||||
if (it !is CompoundTag) return@mapNotNull null
|
||||
val species = ResourceLocation.tryParse(it.getString("species")) ?: return@mapNotNull null
|
||||
val weight = it.getInt("weight")
|
||||
WeightedSpecies(species, weight)
|
||||
}
|
||||
|
||||
if (tag.contains("StructureCenter", Tag.TAG_COMPOUND.toInt())) {
|
||||
val centerTag = tag.getCompound("StructureCenter")
|
||||
val x = centerTag.getInt("X")
|
||||
val y = centerTag.getInt("Y")
|
||||
val z = centerTag.getInt("Z")
|
||||
structureCenter = BlockPos(x, y, z)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener> {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
return saveWithoutMetadata(registryLookup)
|
||||
}
|
||||
|
||||
fun findNearbyStructure(level: ServerLevel, pos: BlockPos, radius: Int): ResourceLocation? {
|
||||
val structureManager = level.structureManager()
|
||||
val registry = level.registryAccess().registryOrThrow(Registries.STRUCTURE)
|
||||
|
||||
for (entry in registry.entrySet()) {
|
||||
val structure = entry.value
|
||||
val structureStart = structureManager.getStructureWithPieceAt(pos, structure)
|
||||
if (structureStart != StructureStart.INVALID_START && structureStart.boundingBox.isInside(pos)) {
|
||||
structureCenter = structureStart.boundingBox.center
|
||||
return entry.key.location()
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TICKER = BlockEntityTicker<GrottoBlockEntity> { world, pos, state, blockEntity ->
|
||||
if (world is ServerLevel) {
|
||||
if (!blockEntity.hasCheckedStructure) {
|
||||
blockEntity.hasCheckedStructure = true
|
||||
|
||||
val structure = blockEntity.findNearbyStructure(world, pos, radius = 5)
|
||||
if (structure != null) {
|
||||
blockEntity.detectedStructure = structure
|
||||
blockEntity.setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
|
||||
println("[GrottoBlockEntity] Found structure at $pos: $structure")
|
||||
}
|
||||
}
|
||||
|
||||
val currentMoonPhase = world.moonPhase
|
||||
if (currentMoonPhase != blockEntity.lastMoonPhaseChecked) {
|
||||
blockEntity.lastMoonPhaseChecked = currentMoonPhase
|
||||
|
||||
if (currentMoonPhase == 7 || blockEntity.currentSpawnGroup.isEmpty()) {
|
||||
val structure = blockEntity.detectedStructure
|
||||
if (structure != null) {
|
||||
blockEntity.detectedStructure = structure
|
||||
blockEntity.setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
|
||||
println("[GrottoBlockEntity] Detected structure at $pos: $structure")
|
||||
|
||||
val habitat = Habitats.getHabitat(structure)
|
||||
if (habitat != null && habitat.spawnPool.isNotEmpty()) {
|
||||
val selectedGroup = weightedGroupRoll(habitat.spawnPool)
|
||||
blockEntity.currentSpawnGroup = selectedGroup.spawns
|
||||
blockEntity.lastMoonPhaseChecked = world.moonPhase
|
||||
blockEntity.setChanged()
|
||||
|
||||
println("[GrottoBlockEntity] Selected spawn group for $structure: ${blockEntity.currentSpawnGroup.map { it.species }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun weightedGroupRoll(groups: List<com.cobblemon.mod.common.api.habitats.WeightedGroup>): com.cobblemon.mod.common.api.habitats.WeightedGroup {
|
||||
val totalWeight = groups.sumOf { it.weight.toDouble() }
|
||||
var roll = Math.random() * totalWeight
|
||||
for (group in groups) {
|
||||
roll -= group.weight
|
||||
if (roll <= 0) return group
|
||||
}
|
||||
return groups.last() // fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.block.IncubatorBlock
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.*
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.BaseEntityBlock.createTickerHelper
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import java.util.*
|
||||
|
||||
class IncubatorBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
BlockEntity(CobblemonBlockEntities.INCUBATOR, pos, state), WorldlyContainer {
|
||||
val eggState = EggState()
|
||||
|
||||
companion object {
|
||||
val TICKER = BlockEntityTicker<IncubatorBlockEntity> { world, pos, state, blockEntity ->
|
||||
// Retrieve the Egg from the EggComponent
|
||||
val egg = blockEntity.getItem(0)?.get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
if (egg != null) {
|
||||
if (egg.timeToHatch <= 0) {
|
||||
// todo egg is ready to hatch
|
||||
|
||||
blockEntity.setChanged()
|
||||
return@BlockEntityTicker
|
||||
}
|
||||
|
||||
// Decrease hatch time if the egg still has time left (faster if in nether)
|
||||
val isNether = if (world != null && world.dimension() == Level.NETHER) true else false
|
||||
|
||||
if (egg.timeToHatch > 0F) {
|
||||
egg.timeToHatch = when {
|
||||
isNether -> egg.timeToHatch - Cobblemon.config.netherIncubatorHatchRate
|
||||
else -> egg.timeToHatch - Cobblemon.config.incubatorHatchRate
|
||||
}
|
||||
}
|
||||
|
||||
// todo is this needed?
|
||||
// Sync to client every 10 seconds (200 ticks)
|
||||
/*if (egg.timeToHatch % 200F == 0F) {
|
||||
blockEntity.setEgg(egg) // Update component and synchronize
|
||||
blockEntity.setChanged()
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var eggItem: ItemStack = ItemStack.EMPTY
|
||||
var ownerUUID: UUID? = null
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registryLookup)
|
||||
tag.put("EggItem", eggItem.saveOptional(registryLookup))
|
||||
tag.putBoolean("IsHatching", eggState.isHatching)
|
||||
tag.putBoolean("HatchingTriggered", eggState.hatchingTriggered)
|
||||
eggState.renderingBlockPos?.let {
|
||||
tag.putIntArray("RenderingBlockPos", listOf(it.x, it.y, it.z))
|
||||
}
|
||||
ownerUUID?.let { tag.putUUID("OwnerUUID", it) }
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registryLookup)
|
||||
eggItem = ItemStack.parseOptional(registryLookup, tag.getCompound("EggItem"))
|
||||
|
||||
eggState.isHatching = tag.getBoolean("IsHatching")
|
||||
eggState.hatchingTriggered = tag.getBoolean("HatchingTriggered")
|
||||
if (tag.contains("OwnerUUID")) {
|
||||
ownerUUID = tag.getUUID("OwnerUUID")
|
||||
}
|
||||
if (tag.contains("RenderingBlockPos")) {
|
||||
val posArray = tag.getIntArray("RenderingBlockPos")
|
||||
if (posArray.size == 3) {
|
||||
eggState.renderingBlockPos = BlockPos(posArray[0], posArray[1], posArray[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getComparatorOutput(): Int {
|
||||
val eggComponent = eggItem.get(CobblemonItemComponents.EGG_DATA)
|
||||
val egg = eggComponent?.egg ?: return 0
|
||||
|
||||
/*val maxHatchTime = 100F // todo replace this with the egg's species expected time to hatch (will need a new algorithm method. Maybe based off of egg cycle + other stuff)
|
||||
val currentHatchTime = egg.timeToHatch.coerceAtLeast(0F).coerceAtMost(maxHatchTime)
|
||||
|
||||
// Calculate progress: 0 (ready) to 15
|
||||
val progress = ((maxHatchTime - currentHatchTime) / maxHatchTime * 15).toInt()
|
||||
|
||||
return progress*/
|
||||
if (egg.timeToHatch <= 0) return 15 else return 0 // send signal based on whether it is ready to hatch or now for now (we can make it more advanced later)
|
||||
}
|
||||
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
val tag = saveWithoutMetadata(registryLookup)
|
||||
return tag
|
||||
}
|
||||
|
||||
|
||||
fun dropItem(world: Level, pos: BlockPos, state: BlockState) {
|
||||
if (!eggItem.isEmpty) {
|
||||
val itemEntity = ItemEntity(world, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, eggItem.copy())
|
||||
world.addFreshEntity(itemEntity)
|
||||
eggItem = ItemStack.EMPTY
|
||||
setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
}
|
||||
|
||||
fun onUse(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: ServerPlayer,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
val heldItem = player.getItemInHand(hand)
|
||||
|
||||
val eggComponent = eggItem.get(CobblemonItemComponents.EGG_DATA)
|
||||
if (!eggItem.isEmpty && eggComponent != null) {
|
||||
// Hatch the Pokémon if the time to hatch is 0 or below
|
||||
if ((eggComponent.egg?.timeToHatch ?: 0F) <= 0F) {
|
||||
eggState.isHatching = true // Trigger hatch animation
|
||||
eggState.renderingBlockPos = this.blockPos // Pass block pos to renderer for packet
|
||||
setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
// Otherwise, drop the egg
|
||||
dropItem(world, pos, state)
|
||||
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, net.minecraft.sounds.SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
// Place a new egg if the player is holding one
|
||||
if (!heldItem.isEmpty && heldItem.get(CobblemonItemComponents.EGG_DATA) != null) {
|
||||
if (!player.isCreative) {
|
||||
setItem(0, heldItem.split(1))
|
||||
} else {
|
||||
setItem(0, heldItem.copy())
|
||||
}
|
||||
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, net.minecraft.sounds.SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
|
||||
fun hatchPokemon(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: ServerPlayer
|
||||
) {
|
||||
val eggItem = getItem(0) // Retrieve the egg item from the container
|
||||
val egg = eggItem.get(CobblemonItemComponents.EGG_DATA)?.egg ?: return
|
||||
if (world !is ServerLevel) return
|
||||
|
||||
val parentOne = egg.parentOne
|
||||
val parentTwo = egg.parentTwo
|
||||
|
||||
// Validate parent Pokémon
|
||||
if (parentOne == null || parentTwo == null) {
|
||||
player.displayClientMessage("Hatching failed: Parent Pokémon not found.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
// Perform breeding to generate the Pokémon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val hatchedPokemon = breedingResult.pokemon?.generatePokemon()
|
||||
|
||||
if (hatchedPokemon == null) {
|
||||
player.displayClientMessage("Hatching failed: Unable to generate Pokémon.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
val playerParty = Cobblemon.storage.getParty(player)
|
||||
val playerPC = Cobblemon.storage.getPC(player)
|
||||
|
||||
// Ensure PC has space for the Pokémon
|
||||
if (playerPC.getFirstAvailablePosition() == null) {
|
||||
player.displayClientMessage("Hatching failed: Your PC is full.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
// Add Pokémon to the party or PC
|
||||
if (playerParty.getFirstAvailablePosition() != null) {
|
||||
playerParty.add(hatchedPokemon)
|
||||
player.displayClientMessage("The Pokémon was added to your party!".green(), true)
|
||||
} else if (playerPC.add(hatchedPokemon)) {
|
||||
player.displayClientMessage("The Pokémon was added to your PC storage.".green(), true)
|
||||
} else {
|
||||
player.displayClientMessage("Hatching failed: Unable to store Pokémon.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
// Clear the egg item and synchronize
|
||||
setItem(0, ItemStack.EMPTY)
|
||||
setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
override fun getContainerSize(): Int = 1
|
||||
|
||||
override fun isEmpty(): Boolean = eggItem.isEmpty
|
||||
|
||||
override fun getItem(slot: Int): ItemStack = if (slot == 0) eggItem else ItemStack.EMPTY
|
||||
|
||||
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||
if (slot == 0 && !eggItem.isEmpty) {
|
||||
val removed = eggItem.split(amount)
|
||||
setChanged()
|
||||
return removed
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||
if (slot == 0) {
|
||||
val removed = eggItem
|
||||
eggItem = ItemStack.EMPTY
|
||||
return removed
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) {
|
||||
if (slot == 0) {
|
||||
eggItem = stack
|
||||
setChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
val hasEgg = !eggItem.isEmpty
|
||||
|
||||
level?.let {
|
||||
if (blockState.getValue(IncubatorBlock.HAS_EGG) != hasEgg) {
|
||||
// Update the block state with the new `HAS_EGG` value
|
||||
val newState = blockState.setValue(IncubatorBlock.HAS_EGG, hasEgg)
|
||||
level?.setBlock(worldPosition, newState, 3)
|
||||
}
|
||||
|
||||
it.blockEntityChanged(worldPosition)
|
||||
it.sendBlockUpdated(worldPosition, blockState, blockState, Block.UPDATE_ALL)
|
||||
it.updateNeighborsAt(worldPosition, blockState.block)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean = true
|
||||
|
||||
override fun getSlotsForFace(direction: Direction): IntArray = intArrayOf(0)
|
||||
|
||||
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, direction: Direction?): Boolean {
|
||||
return slot == 0 && stack.get(CobblemonItemComponents.EGG_DATA) != null
|
||||
}
|
||||
|
||||
override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean {
|
||||
return slot == 0 && !eggItem.isEmpty
|
||||
}
|
||||
|
||||
override fun clearContent() {
|
||||
eggItem = ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.*
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.NestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.component.DataComponentType
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.WorldlyContainer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.ticks.TickPriority
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
class NestBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.NEST, pos, state), WorldlyContainer {
|
||||
var renderState: BlockEntityRenderState? = null
|
||||
var parents: Pair<UUID?, UUID?> = Pair(null, null) // todo make sure this is synced
|
||||
private var eggItem: ItemStack = ItemStack.EMPTY
|
||||
var ownerUUID: UUID? = null // player UUID that placed this nest
|
||||
val eggState = EggState()
|
||||
var nestClaimedBy: Pair<UUID?, UUID?> = Pair(null, null)
|
||||
|
||||
/**
|
||||
* Sets the ItemStack and synchronizes it.
|
||||
*/
|
||||
fun setItem(itemStack: ItemStack) {
|
||||
this.eggItem = itemStack
|
||||
setChanged() // Mark the block entity as changed.
|
||||
level?.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS) // Notify the client.
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ItemStack.
|
||||
*/
|
||||
fun getItem(): ItemStack = eggItem
|
||||
|
||||
/**
|
||||
* Drops the ItemStack in the world.
|
||||
*/
|
||||
fun dropItem(world: Level, pos: BlockPos, state: BlockState) {
|
||||
if (!eggItem.isEmpty) {
|
||||
val itemEntity = ItemEntity(world, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, eggItem.copy())
|
||||
world.addFreshEntity(itemEntity)
|
||||
eggItem = ItemStack.EMPTY
|
||||
setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interaction with the block.
|
||||
*/
|
||||
fun onUse(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: ServerPlayer,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
val heldItem = player.getItemInHand(hand)
|
||||
|
||||
val eggComponent = eggItem.get(CobblemonItemComponents.EGG_DATA)
|
||||
if (!eggItem.isEmpty && eggComponent != null) {
|
||||
// Hatch the Pokémon if the time to hatch is 0 or below
|
||||
if ((eggComponent.egg?.timeToHatch ?: 0F) <= 0F) {
|
||||
eggState.isHatching = true // Trigger hatch animation on the renderer
|
||||
//playSound(CobblemonSounds.BREEDING_EGG_HATCH)
|
||||
eggState.renderingBlockPos = this.blockPos // Pass block pos to renderer for packet
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
|
||||
// Otherwise, drop the egg
|
||||
dropItem(world, pos, state)
|
||||
eggState.resetHatchingState()
|
||||
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, net.minecraft.sounds.SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
// Place a new egg if the player is holding one
|
||||
if (!heldItem.isEmpty && heldItem.get(CobblemonItemComponents.EGG_DATA) != null) {
|
||||
if (!player.isCreative) {
|
||||
setItem(heldItem.split(1))
|
||||
} else {
|
||||
setItem(heldItem.copy())
|
||||
}
|
||||
eggState.resetHatchingState()
|
||||
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, net.minecraft.sounds.SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registryLookup)
|
||||
//if (!eggItem.isEmpty) {
|
||||
tag.put("EggItem", eggItem.saveOptional(registryLookup))
|
||||
//}
|
||||
ownerUUID?.let { tag.putUUID("OwnerUUID", it) }
|
||||
tag.putBoolean("IsHatching", eggState.isHatching)
|
||||
tag.putBoolean("HatchingTriggered", eggState.hatchingTriggered)
|
||||
eggState.renderingBlockPos?.let {
|
||||
tag.putIntArray("RenderingBlockPos", listOf(it.x, it.y, it.z))
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registryLookup)
|
||||
//if (tag.contains("EggItem")) {
|
||||
eggItem = ItemStack.parseOptional(registryLookup, tag.getCompound("EggItem"))
|
||||
//}
|
||||
if (tag.contains("OwnerUUID")) {
|
||||
ownerUUID = tag.getUUID("OwnerUUID")
|
||||
}
|
||||
|
||||
eggState.isHatching = tag.getBoolean("IsHatching")
|
||||
eggState.hatchingTriggered = tag.getBoolean("HatchingTriggered")
|
||||
if (tag.contains("RenderingBlockPos")) {
|
||||
val posArray = tag.getIntArray("RenderingBlockPos")
|
||||
if (posArray.size == 3) {
|
||||
eggState.renderingBlockPos = BlockPos(posArray[0], posArray[1], posArray[2])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
|
||||
//println("getUpdatePacket called for NestBlockEntity at $blockPos with egg: $eggComponent")
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
val tag = saveWithoutMetadata(registryLookup)
|
||||
//println("getUpdateTag called for NestBlockEntity at $blockPos with tag: $tag")
|
||||
return tag
|
||||
}
|
||||
|
||||
/*fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}*/
|
||||
|
||||
fun hatchPokemon(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: ServerPlayer
|
||||
) {
|
||||
val egg = getItem().get(CobblemonItemComponents.EGG_DATA)?.egg ?: return
|
||||
if (world !is ServerLevel) return
|
||||
|
||||
val parentOne = egg.parentOne
|
||||
val parentTwo = egg.parentTwo
|
||||
|
||||
if (parentOne == null || parentTwo == null) {
|
||||
player.displayClientMessage("Hatching failed: Parent Pokémon not found.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val hatchedPokemon = breedingResult.pokemon?.generatePokemon()
|
||||
|
||||
if (hatchedPokemon == null) {
|
||||
player.displayClientMessage("Hatching failed: Unable to generate Pokémon.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
val playerParty = Cobblemon.storage.getParty(player)
|
||||
val playerPC = Cobblemon.storage.getPC(player)
|
||||
|
||||
// If PC is full, prevent hatching
|
||||
// todo find maybe a better way to check if PC is full
|
||||
if (playerPC.getFirstAvailablePosition() == null) {
|
||||
player.displayClientMessage("Hatching failed: Your PC is full.".red(), true)
|
||||
return
|
||||
}
|
||||
|
||||
// Spawn the Pokémon entity at the egg's position
|
||||
val spawnedEntity = PokemonEntity(world, hatchedPokemon).apply {
|
||||
setPos(pos.x + 0.5, pos.y.toDouble(), pos.z + 0.5)
|
||||
isNewlyHatched = true
|
||||
world.addFreshEntity(this)
|
||||
}
|
||||
|
||||
// Trigger cry animation
|
||||
afterOnServer(seconds = 1.0f) {
|
||||
spawnedEntity.cry()
|
||||
}
|
||||
|
||||
// If party has space, add Pokémon to the party
|
||||
if (playerParty.getFirstAvailablePosition() != null) {
|
||||
playerParty.add(hatchedPokemon)
|
||||
player.displayClientMessage("The Pokémon was added to your party!".green(), true)
|
||||
}
|
||||
|
||||
// If party is full but PC has space, add to PC and recall Pokémon
|
||||
else if (playerPC.add(hatchedPokemon)) {
|
||||
player.displayClientMessage("The Pokémon was added to your PC storage.".green(), true)
|
||||
|
||||
// Schedule recall animation
|
||||
spawnedEntity.after(seconds = 3.0f) {
|
||||
spawnedEntity.recallWithAnimation()
|
||||
}
|
||||
|
||||
/*// Remove the Pokémon entity after recall
|
||||
spawnedEntity.after(seconds = 4.0f) {
|
||||
spawnedEntity.remove(Entity.RemovalReason.DISCARDED)
|
||||
}*/
|
||||
}
|
||||
|
||||
// Clear the egg from the nest and update the block entity
|
||||
setItem(Items.AIR.defaultInstance)
|
||||
eggState.resetHatchingState()
|
||||
setChanged()
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Spawns a Pokémon of the given species around the specified position.
|
||||
* @param world The world in which to spawn the Pokémon.
|
||||
* @param pos The position around which to spawn the Pokémon.
|
||||
* @param species The species of the Pokémon to spawn.
|
||||
* @param isMale Whether the Pokémon should be male or female.
|
||||
* @return The spawned Pokémon entity, or null if spawning failed.
|
||||
*//*
|
||||
fun spawnPokemon(
|
||||
world: Level,
|
||||
pos: BlockPos//,
|
||||
//species: PokemonSpecies
|
||||
): Pair<PokemonEntity?,PokemonEntity?> {
|
||||
if (world !is ServerLevel) return null to null
|
||||
|
||||
// Generate a random offset within the radius
|
||||
val randomOffset = {
|
||||
world.random.nextDouble() * SPAWN_RADIUS * 2 - SPAWN_RADIUS
|
||||
}
|
||||
val spawnPos = pos.offset(randomOffset().toInt(), 0, randomOffset().toInt())
|
||||
|
||||
// todo Use Spawn files
|
||||
// todo CHANGE THIS! We probably want a brand new spawner similar to the fishing spawner
|
||||
val spawner = BestSpawner.nestSpawner
|
||||
|
||||
val buckets = Cobblemon.bestSpawner.config.buckets
|
||||
|
||||
val chosenBucket = chooseSpawnBucket(buckets)
|
||||
|
||||
val spawnCause = NestSpawnCause(
|
||||
spawner = spawner,
|
||||
bucket = chosenBucket,
|
||||
entity = null
|
||||
)
|
||||
|
||||
//
|
||||
val result = spawner.run(spawnCause, level as ServerLevel, spawnPos)
|
||||
|
||||
var spawnedPokemon: PokemonEntity? = null
|
||||
var partnerPokemon: PokemonEntity? = null
|
||||
val resultingSpawn = result?.get()
|
||||
|
||||
if (resultingSpawn is EntitySpawnResult) {
|
||||
for (entity in resultingSpawn.entities) {
|
||||
// we can query the spawned pokemon here
|
||||
spawnedPokemon = (entity as PokemonEntity)
|
||||
|
||||
// spawn a partner pokemon also in a random position
|
||||
partnerPokemon = this.spawnPartnerPokemon(world, pos, spawnedPokemon)
|
||||
|
||||
// CRAB IDEA: todo maybe some grass particles? as if they appeared from the grass or forest?
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return spawnedPokemon to partnerPokemon
|
||||
}
|
||||
|
||||
fun spawnPartnerPokemon(
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
originalPokemon: PokemonEntity
|
||||
): PokemonEntity? {
|
||||
println("Attempting to spawn partner Pokémon...")
|
||||
println("Original Pokémon being cloned: ${originalPokemon}")
|
||||
|
||||
val pokemon = originalPokemon.pokemon
|
||||
println("Pokemon details before cloning: UUID=${pokemon.uuid}, Ability=${pokemon.ability}, Species=${pokemon.species}, Form=${pokemon.form}")
|
||||
|
||||
// Validate ability before cloning
|
||||
if (pokemon.ability.template == Abilities.DUMMY) {
|
||||
println("Original Pokémon has DUMMY ability. Rolling a new one before cloning...")
|
||||
pokemon.rollAbility()
|
||||
println("New ability assigned: ${pokemon.ability}")
|
||||
}
|
||||
|
||||
// Perform the clone
|
||||
val clonedPokemon = try {
|
||||
pokemon.clone()
|
||||
} catch (e: IllegalStateException) {
|
||||
println("Error cloning Pokémon: ${e.message}")
|
||||
return null // Skip spawning if cloning fails
|
||||
}
|
||||
|
||||
// Apply further customization to the cloned Pokémon
|
||||
clonedPokemon.gender = if (pokemon.gender == Gender.MALE) Gender.FEMALE else Gender.MALE
|
||||
println("Cloned Pokémon details: ${clonedPokemon}")
|
||||
|
||||
// Generate a random offset for the partner Pokémon
|
||||
val randomOffset = {
|
||||
world.random.nextDouble() * SPAWN_RADIUS * 2 - SPAWN_RADIUS
|
||||
}
|
||||
val spawnPos = pos.offset(randomOffset().toInt(), 0, randomOffset().toInt())
|
||||
|
||||
// Create the partner PokemonEntity
|
||||
val partnerPokemonEntity = PokemonEntity(world, clonedPokemon).apply {
|
||||
setPos(spawnPos.x + 0.5, spawnPos.y.toDouble(), spawnPos.z + 0.5)
|
||||
}
|
||||
|
||||
// Add the partner to the world
|
||||
return if (world.addFreshEntity(partnerPokemonEntity)) partnerPokemonEntity else null
|
||||
}
|
||||
|
||||
fun chooseSpawnBucket(buckets: List<SpawnBucket>): SpawnBucket {
|
||||
val baseIncreases = listOf(2.5F, 1.0F, 0.6F) // 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
|
||||
}
|
||||
}.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
|
||||
}*/
|
||||
|
||||
// Hopper interaction methods
|
||||
override fun getContainerSize(): Int = 1
|
||||
|
||||
override fun isEmpty(): Boolean = eggItem.isEmpty
|
||||
|
||||
override fun getItem(slot: Int): ItemStack = if (slot == 0) eggItem else ItemStack.EMPTY
|
||||
|
||||
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||
if (slot == 0 && !eggItem.isEmpty) {
|
||||
val removed = eggItem.split(amount)
|
||||
setChanged()
|
||||
return removed
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||
if (slot == 0) {
|
||||
val removed = eggItem
|
||||
eggItem = ItemStack.EMPTY
|
||||
return removed
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) {
|
||||
if (slot == 0) {
|
||||
eggItem = stack
|
||||
setChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
// Marks the block entity as modified
|
||||
level?.blockEntityChanged(worldPosition)
|
||||
|
||||
// Schedules a block update to synchronize with the client
|
||||
level?.sendBlockUpdated(worldPosition, blockState, blockState, Block.UPDATE_ALL)
|
||||
}
|
||||
|
||||
override fun getSlotsForFace(direction: Direction): IntArray {
|
||||
return intArrayOf(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows hoppers to insert items into the container.
|
||||
*/
|
||||
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, direction: Direction?): Boolean {
|
||||
// Allow only items with EggComponent to be inserted
|
||||
return slot == 0 && stack.get(CobblemonItemComponents.EGG_DATA) != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows hoppers to pull items out of the container.
|
||||
*/
|
||||
override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean {
|
||||
// Allow items to be pulled out only if the slot contains an egg item
|
||||
return slot == 0 && !eggItem.isEmpty
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean = true
|
||||
|
||||
override fun clearContent() {
|
||||
eggItem = ItemStack.EMPTY
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val RANDOM_SPAWN_CHANCE = 10000 // 1 in 100 chance per random tick // todo remove this
|
||||
private const val SPAWN_RADIUS = 5.0 // Radius around the nest to spawn Pokémon
|
||||
|
||||
val TICKER = BlockEntityTicker<NestBlockEntity> { world, pos, state, blockEntity ->
|
||||
// Retrieve the Egg from the EggComponent
|
||||
val egg = blockEntity.getItem().get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
if (egg != null) {
|
||||
/*if (egg.timeToHatch <= 0) {
|
||||
// Try to find a nearby ServerPlayer to pass to hatchPokemon
|
||||
val nearestPlayer = world.getNearestPlayer(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, 10.0, false)
|
||||
|
||||
if (nearestPlayer is ServerPlayer) {
|
||||
// todo Crab Note: I removed this as it was auto-hatching eggs as soon as you got near them
|
||||
//blockEntity.hatchPokemon(state, world, pos, nearestPlayer)
|
||||
}
|
||||
|
||||
blockEntity.setChanged()
|
||||
return@BlockEntityTicker
|
||||
}*/
|
||||
|
||||
// Decrease hatch time if the egg still has time left (faster if in nether)
|
||||
val isNether = if (world != null && world.dimension() == Level.NETHER) true else false
|
||||
|
||||
if (egg.timeToHatch > 0F) {
|
||||
egg.timeToHatch = when {
|
||||
isNether -> egg.timeToHatch - Cobblemon.config.netherNestHatchRate
|
||||
else -> egg.timeToHatch - Cobblemon.config.nestHatchRate
|
||||
}
|
||||
}
|
||||
|
||||
// Sync to client every 10 seconds (200 ticks)
|
||||
if (egg.timeToHatch % 200F == 0F) {
|
||||
//blockEntity.setItem(egg) // Update component and synchronize
|
||||
//blockEntity.setChanged()
|
||||
val test = 1
|
||||
}
|
||||
} else {
|
||||
// Attempt to spawn a random egg in the nest with a small chance
|
||||
if (world.random.nextInt(RANDOM_SPAWN_CHANCE) == 0 && false) { // TODO remove the false part and remove this entirely later too
|
||||
|
||||
/*val (parent1, parent2) = blockEntity.spawnPokemon(world, pos)
|
||||
val test = 1
|
||||
|
||||
// todo remove this as we are not having nests spawn pokemon anymore
|
||||
/*val (parent1, parent2) = blockEntity.spawnPokemon(world, pos)
|
||||
|
||||
if (parent1 != null && parent2 != null) {
|
||||
val childResult = BreedingLogicManager.breed(parent1.pokemon,parent2.pokemon)
|
||||
|
||||
// Create an egg with the parents
|
||||
val egg = childResult.pokemon?.species?.let {
|
||||
Egg(
|
||||
species = it.resourceIdentifier,
|
||||
parentOne = parent1.pokemon, // Parent one UUID
|
||||
parentTwo = parent2.pokemon, // Parent two UUID
|
||||
form = childResult.pokemon.formData.name.lowercase(),
|
||||
timeToHatch = 200F // 10 seconds hatch time
|
||||
)
|
||||
}*/ // TODO KILL ALL OF THIS CODE ONCE WE HAVE A HABITAT SPAWNER
|
||||
if (egg != null) {
|
||||
blockEntity.setItem(egg.createEggItemStack())
|
||||
}
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+219
@@ -11,21 +11,29 @@ package com.cobblemon.mod.common.block.entity
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.api.pasture.PastureLinkManager
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.EggPatterns
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.block.NestBlock
|
||||
import com.cobblemon.mod.common.block.PastureBlock
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.breeding.SimpleBreedingLogic
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsList
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
@@ -35,6 +43,8 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.core.particles.ParticleOptions
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.NbtUtils
|
||||
@@ -46,12 +56,17 @@ import net.minecraft.world.entity.Pose
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.entity.EntityTypeTest
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
import kotlin.random.Random
|
||||
|
||||
class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
BlockEntity(CobblemonBlockEntities.PASTURE, pos, state) {
|
||||
@@ -95,9 +110,11 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
blockEntity.ticksUntilCheck--
|
||||
if (blockEntity.ticksUntilCheck <= 0) {
|
||||
blockEntity.checkPokemon()
|
||||
//blockEntity.tryBreed()
|
||||
}
|
||||
blockEntity.togglePastureOn(blockEntity.getInRangeViewerCount(world, blockEntity.blockPos) > 0)
|
||||
}
|
||||
val DITTO_DEX_NUM = 132
|
||||
}
|
||||
|
||||
var ticksUntilCheck = Cobblemon.config.pastureBlockUpdateTicks
|
||||
@@ -106,6 +123,11 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
var maxRoamPos: BlockPos
|
||||
var ownerId: UUID? = null
|
||||
var ownerName: String = ""
|
||||
//Maps a male pokemon to all of the female pokemon it can breed with
|
||||
//val breedingSets: MutableMap<Tethering, MutableSet<Tethering>> = mutableMapOf()
|
||||
//Basically here to decide whether to calc all breeding sets when updating the block entity
|
||||
//We add breeding sets when pokemon are tethered but we need to calc them on world load too,
|
||||
var initializedBreedingSets = false
|
||||
|
||||
init {
|
||||
val radius = Cobblemon.config.pastureMaxWanderDistance
|
||||
@@ -174,6 +196,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
pcId = pc.uuid,
|
||||
entityId = entity.id
|
||||
)
|
||||
//calcBreedingSets(tethering)
|
||||
pokemon.tetheringId = tethering.tetheringId
|
||||
tetheredPokemon.add(tethering)
|
||||
entity.tethering = tethering
|
||||
@@ -283,6 +306,188 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
setChanged()
|
||||
}
|
||||
|
||||
// todo the pasture block no longer tracks nests and is in control of breeding. it is all brains now
|
||||
/*//Dear god this method needs to be cleaned up
|
||||
fun tryBreed() {
|
||||
val bredPokemon = mutableSetOf<Pokemon>()
|
||||
//We should probably find a way to minimize how often this gets called
|
||||
//Maybe cache the result and update it infrequently?
|
||||
val nests = findUnusedNests()
|
||||
if (nests.isNotEmpty()) {
|
||||
breedingSets.forEach { father, mothers ->
|
||||
if (father.getPokemon()?.breedingCooldown == 0) {
|
||||
mothers.forEach { mother ->
|
||||
if (mother.getPokemon()?.breedingCooldown == 0) {
|
||||
val motherPoke = mother.getPokemon()!!
|
||||
val fatherPoke = father.getPokemon()!!
|
||||
if (BreedingLogicManager.canBreed(motherPoke, fatherPoke) && nests.isNotEmpty()) {
|
||||
val breedResult = BreedingLogicManager.breed(motherPoke, fatherPoke)
|
||||
|
||||
if (breedResult.successful) {
|
||||
// add cooldown to the parents
|
||||
motherPoke.breedingCooldown = 240
|
||||
|
||||
// Add heart particles <3 <3 <3
|
||||
val fatherPokemon = father.getPokemon()?.entity
|
||||
val motherPokemon = mother.getPokemon()?.entity
|
||||
|
||||
if (fatherPokemon != null && motherPokemon != null) {
|
||||
// Spawn heart particles for father
|
||||
(level as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
fatherPokemon.getRandomX(1.0), // Particle X position with slight randomness
|
||||
fatherPokemon.getRandomY() + 0.5, // Slightly raised Y position
|
||||
fatherPokemon.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * fatherPokemon.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * fatherPokemon.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * fatherPokemon.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Spawn heart particles for mother
|
||||
(level as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
motherPokemon.getRandomX(1.0), // Particle X position with slight randomness
|
||||
motherPokemon.getRandomY() + 0.5, // Slightly raised Y position
|
||||
motherPokemon.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * motherPokemon.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * motherPokemon.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * motherPokemon.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
println(breedResult.pokemon)
|
||||
val nestTaken = nests.random()
|
||||
val nestStal = level?.getBlockState(nestTaken)
|
||||
val blockEntity = level?.getBlockEntity(nestTaken) as? NestBlockEntity
|
||||
val avgEggCycle = (motherPoke.species.eggCycles + fatherPoke.species.eggCycles) / 2
|
||||
if (blockEntity != null && breedResult.pokemon != null) {
|
||||
val egg = Egg(
|
||||
species = breedResult.pokemon!!.species.resourceIdentifier, // Species
|
||||
parentOne = motherPoke, // Mother UUID
|
||||
parentTwo = fatherPoke, // Father UUID
|
||||
form = breedResult.pokemon.formData.name.lowercase(),
|
||||
timeToHatch = 100F //breedResult.timeToHatch.toFloat() // 10 seconds hatch time
|
||||
)
|
||||
|
||||
//Most params here need to be gotten from the form when implemented properly
|
||||
blockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
val isClientSide = this.level?.isClientSide ?: false
|
||||
if (isClientSide) {
|
||||
//println("PokemonPastureBlockEntity: Running on the CLIENT side.")
|
||||
} else {
|
||||
//println("PokemonPastureBlockEntity: Running on the SERVER side.")
|
||||
}
|
||||
|
||||
//println("Egg set in NestBlockEntity at ${nestTaken}: ${blockEntity.getEgg()!!.species}")
|
||||
|
||||
level?.getBlockState(nestTaken)?.let { level?.sendBlockUpdated(nestTaken, it, level?.getBlockState(nestTaken)!!, Block.UPDATE_CLIENTS) }
|
||||
|
||||
// remove the nest used from the nest consideration pool
|
||||
nests.remove(nestTaken)
|
||||
|
||||
bredPokemon.add(fatherPoke)
|
||||
bredPokemon.add(motherPoke)
|
||||
|
||||
if (Cobblemon.config.canDestinyKnotBreak) {
|
||||
if (fatherPokemon?.pokemon?.heldItem?.item == CobblemonItems.DESTINY_KNOT.asItem())
|
||||
fatherPokemon.pokemon.heldItemNoCopy().damageValue += 1
|
||||
|
||||
if (motherPokemon?.pokemon?.heldItem?.item == CobblemonItems.DESTINY_KNOT.asItem())
|
||||
motherPokemon.pokemon.heldItemNoCopy().damageValue += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bredPokemon.forEach {
|
||||
it.breedingCooldown = 20 // todo remove this when pasture doesn't do this anymore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//If new mon is passed, only calc/add breeding sets with that parent
|
||||
//Otherwise, find all combos in the block (like on world load)
|
||||
fun calcBreedingSets(newMon: Tethering? = null) {
|
||||
initializedBreedingSets = true
|
||||
if (newMon != null) {
|
||||
for (t in tetheredPokemon) {
|
||||
addIfBreedingPair(newMon, t)
|
||||
}
|
||||
}
|
||||
else {
|
||||
tetheredPokemon.forEachIndexed { i, mon ->
|
||||
val listLen = tetheredPokemon.size
|
||||
if (i != listLen - 1) {
|
||||
tetheredPokemon.subList(i + 1, listLen).forEach {
|
||||
addIfBreedingPair(mon, it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun addIfBreedingPair(monOne: Tethering, monTwo: Tethering) {
|
||||
val pokemonOne = monOne.getPokemon()
|
||||
val pokemonTwo = monTwo.getPokemon()
|
||||
|
||||
if (pokemonOne == null || pokemonTwo == null) {
|
||||
return // Skip if either Pokémon is null
|
||||
}
|
||||
|
||||
// Ensure distinct Pokémon
|
||||
if (pokemonOne.uuid == pokemonTwo.uuid) {
|
||||
return // Skip if the Pokémon have the same UUID
|
||||
}
|
||||
|
||||
// Determine mother and father based on gender or if Ditto is involved
|
||||
val mother = if (pokemonOne.gender == Gender.FEMALE || pokemonOne.species.nationalPokedexNumber == DITTO_DEX_NUM) monOne else monTwo
|
||||
val father = if (mother == monOne) monTwo else monOne
|
||||
|
||||
// Ensure they can breed
|
||||
if (SimpleBreedingLogic.canBreed(mother.getPokemon()!!, father.getPokemon()!!)) {
|
||||
val fatherSet: MutableSet<Tethering> = breedingSets.getOrDefault(father, mutableSetOf())
|
||||
fatherSet.add(mother)
|
||||
breedingSets[father] = fatherSet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun findUnusedNests(): MutableSet<BlockPos> {
|
||||
val res = mutableSetOf<BlockPos>()
|
||||
val cube = Shapes.box(
|
||||
(this.blockPos.x - 2).toDouble(), (this.blockPos.y - 1).toDouble(), (this.blockPos.z - 2).toDouble(),
|
||||
(this.blockPos.x + 3).toDouble(), (this.blockPos.y + 3).toDouble(), (this.blockPos.z + 3).toDouble()
|
||||
)
|
||||
cube.blockPositionsAsList().forEach {
|
||||
val state = level?.getBlockState(it)
|
||||
if (state?.block is NestBlock) {
|
||||
val entity = level?.getBlockEntity(it) as NestBlockEntity
|
||||
//Cobblemon.LOGGER.warn("Nest at ${it.toString()}")
|
||||
if (entity.getItem().item != CobblemonItems.POKEMON_EGG.asItem()) {
|
||||
res.add(it)
|
||||
}
|
||||
else
|
||||
{
|
||||
//Cobblemon.LOGGER.warn("Nest with egg at ${it.toString()}")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return res
|
||||
}*/
|
||||
|
||||
fun onBroken() {
|
||||
if (level is ServerLevel) {
|
||||
tetheredPokemon.toList().forEach { releasePokemon(it.pokemonId) }
|
||||
@@ -294,6 +499,11 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
val tethering = tetheredPokemon.find { it.pokemonId == pokemonId } ?: return
|
||||
tethering.getPokemon()?.tetheringId = null
|
||||
tetheredPokemon.remove(tethering)
|
||||
|
||||
// Recalculate breeding sets after removal
|
||||
//breedingSets.clear()
|
||||
//tetheredPokemon.forEach { calcBreedingSets(it) }
|
||||
|
||||
setChanged()
|
||||
}
|
||||
|
||||
@@ -304,10 +514,16 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
tetheredPokemon.remove(it)
|
||||
unpastured.add(it.pokemonId)
|
||||
}
|
||||
|
||||
// Recalculate breeding sets after all removals
|
||||
//breedingSets.clear()
|
||||
//tetheredPokemon.forEach { calcBreedingSets(it) }
|
||||
|
||||
setChanged()
|
||||
return unpastured
|
||||
}
|
||||
|
||||
|
||||
private fun getInRangeViewerCount(world: Level, pos: BlockPos, range: Double = 5.0): Int {
|
||||
val box = AABB(
|
||||
pos.x.toDouble() - range,
|
||||
@@ -355,6 +571,9 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
this.minRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MIN_ROAM_POS).get()
|
||||
this.maxRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MAX_ROAM_POS).get()
|
||||
/*if (!initializedBreedingSets) {
|
||||
calcBreedingSets()
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun saveAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.breeding
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingEligibilityEvent
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingResultEvent
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.BreedingLogic
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.BreedingResult
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Contains all breeding logics to be run through when doing the breeding logic
|
||||
* Basically if a sidemod wants to override some aspect of breeding logic, they can implement the [BreedingLogic] interface
|
||||
* then add it here
|
||||
*/
|
||||
object BreedingLogicManager : BreedingLogic {
|
||||
override fun breed(mother: Pokemon, father: Pokemon): BreedingResult {
|
||||
// todo this was not working
|
||||
return super.breed(mother, father)
|
||||
/*val baseBreedingResult = super.breed(mother, father)
|
||||
var finalBreedingResult = baseBreedingResult
|
||||
CobblemonEvents.BREEDING_RESULT.post(BreedingResultEvent(baseBreedingResult, mother, father)) {
|
||||
finalBreedingResult = it.breedingResult
|
||||
}
|
||||
return finalBreedingResult*/
|
||||
}
|
||||
|
||||
// todo this canBreed method is NOT working lmao
|
||||
override fun canBreed(mother: Pokemon, father: Pokemon): Boolean {
|
||||
// todo this was not working
|
||||
return super.canBreed(mother, father)
|
||||
/*val initBreedBool = super.canBreed(mother, father)
|
||||
var finalBreedingResult = initBreedBool
|
||||
CobblemonEvents.BREEDING_ELIGIBLE.post(BreedingEligibilityEvent(initBreedBool, mother, father) ) {
|
||||
finalBreedingResult = it.canBreed
|
||||
}
|
||||
return super.canBreed(mother, father)*/
|
||||
}
|
||||
|
||||
override fun resetBreedingCooldown(pokemon: Pokemon): Int {
|
||||
return super.resetBreedingCooldown(pokemon)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.BreedingLogic
|
||||
|
||||
object SimpleBreedingLogic : BreedingLogic {
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.client.render.block.GrottoBlockRenderer
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
@@ -15,7 +16,6 @@ import com.cobblemon.mod.common.CobblemonClientImplementation
|
||||
import com.cobblemon.mod.common.CobblemonEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
@@ -26,13 +26,7 @@ import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
||||
import com.cobblemon.mod.common.client.render.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
|
||||
@@ -57,6 +51,7 @@ import com.cobblemon.mod.common.client.tooltips.TooltipManager
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.PokemonEggItem
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
@@ -73,14 +68,10 @@ import net.minecraft.client.renderer.blockentity.SignRenderer
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.resources.PlayerSkin
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.AABB
|
||||
import java.util.UUID
|
||||
import net.minecraft.client.player.AbstractClientPlayer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object CobblemonClient {
|
||||
|
||||
@@ -94,6 +85,16 @@ object CobblemonClient {
|
||||
var checkedStarterScreen = false
|
||||
var requests = ClientPlayerActionRequests()
|
||||
var teamData = ClientPlayerTeamData()
|
||||
/*var posableModelRepositories = listOf(
|
||||
PokemonModelRepository,
|
||||
PokeBallModelRepository,
|
||||
NPCModelRepository,
|
||||
FossilModelRepository,
|
||||
BlockEntityModelRepository,
|
||||
GenericBedrockEntityModelRepository,
|
||||
EggModelRepo
|
||||
)*/
|
||||
|
||||
val overlay: PartyOverlay by lazy { PartyOverlay() }
|
||||
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
|
||||
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
|
||||
@@ -127,7 +128,8 @@ object CobblemonClient {
|
||||
|
||||
this.registerBlockEntityRenderers()
|
||||
registerBlockRenderTypes()
|
||||
//registerColors()
|
||||
registerBlockColors()
|
||||
registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
Berries.observable.subscribe {
|
||||
@@ -136,7 +138,8 @@ object CobblemonClient {
|
||||
this.registerTooltipManagers()
|
||||
|
||||
LOGGER.info("Registering custom BuiltinItemRenderers")
|
||||
CobblemonBuiltinItemRendererRegistry.register(CobblemonItems.POKEMON_MODEL, PokemonItemRenderer())
|
||||
registerBuiltinItemRenderers()
|
||||
|
||||
|
||||
PlatformEvents.CLIENT_ITEM_TOOLTIP.subscribe { event ->
|
||||
val stack = event.stack
|
||||
@@ -187,16 +190,9 @@ object CobblemonClient {
|
||||
// .apply()
|
||||
}
|
||||
|
||||
/*
|
||||
fun registerColors() {
|
||||
this.implementation.registerBlockColors(BlockColorProvider { _, _, _, _ ->
|
||||
return@BlockColorProvider 0xE0A33A
|
||||
}, CobblemonBlocks.APRICORN_LEAVES)
|
||||
this.implementation.registerItemColors(ItemColorProvider { _, _ ->
|
||||
return@ItemColorProvider 0xE0A33A
|
||||
}, CobblemonItems.APRICORN_LEAVES)
|
||||
this.implementation.registerItemColors(PokemonEggItem::getColor, CobblemonItems.POKEMON_EGG)
|
||||
}
|
||||
*/
|
||||
|
||||
private fun registerBlockRenderTypes() {
|
||||
|
||||
@@ -243,6 +239,24 @@ object CobblemonClient {
|
||||
CobblemonBlocks.PEP_UP_FLOWER,
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
CobblemonBlocks.NEST_STICK,
|
||||
CobblemonBlocks.NEST_LEAF,
|
||||
CobblemonBlocks.NEST_STONE,
|
||||
CobblemonBlocks.NEST_DEEPSLATE,
|
||||
CobblemonBlocks.NEST_BASALT,
|
||||
CobblemonBlocks.NEST_SNOW,
|
||||
CobblemonBlocks.NEST_NETHER,
|
||||
CobblemonBlocks.NEST_SOUL_SAND,
|
||||
CobblemonBlocks.NEST_WARM_OCEAN,
|
||||
CobblemonBlocks.NEST_SEAGRASS,
|
||||
CobblemonBlocks.NEST_LUSH,
|
||||
CobblemonBlocks.NEST_HAY,
|
||||
CobblemonBlocks.NEST_SAND,
|
||||
CobblemonBlocks.NEST_RED_SAND,
|
||||
CobblemonBlocks.NEST_CHORUS,
|
||||
CobblemonBlocks.BASIC_NEST,
|
||||
CobblemonBlocks.CAVE_NEST,
|
||||
CobblemonBlocks.WATER_NEST,
|
||||
*CobblemonBlocks.berries().values.toTypedArray(),
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.RESTORATION_TANK,
|
||||
@@ -260,12 +274,23 @@ object CobblemonClient {
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE,
|
||||
CobblemonBlocks.INCUBATOR,
|
||||
CobblemonBlocks.LECTERN
|
||||
)
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderType.translucent(),
|
||||
CobblemonBlocks.TUMBLESTONE_BLOCK
|
||||
)
|
||||
|
||||
|
||||
this.createBoatModelLayers()
|
||||
}
|
||||
|
||||
fun registerBlockColors() {
|
||||
this.implementation.registerBlockColors(NestLeafBlockColorProvider, CobblemonBlocks.NEST_LEAF)
|
||||
}
|
||||
|
||||
fun beforeChatRender(context: GuiGraphics, partialDeltaTicks: Float) {
|
||||
val partialDeltaTicks = Minecraft.getInstance().timer // Checking that this even works
|
||||
// ClientTaskTracker.update(partialDeltaTicks / 20f)
|
||||
@@ -291,6 +316,10 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.NEST, ::NestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GROTTO_BLOCK, ::GrottoBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.EGG, ::EggBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.INCUBATOR, ::IncubatorRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
||||
@@ -315,6 +344,10 @@ object CobblemonClient {
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.NPC, ::NPCRenderer)
|
||||
}
|
||||
|
||||
private fun registerBuiltinItemRenderers() {
|
||||
CobblemonBuiltinItemRendererRegistry.register(CobblemonItems.POKEMON_MODEL, PokemonItemRenderer())
|
||||
}
|
||||
|
||||
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
||||
LOGGER.info("Loading assets...")
|
||||
// Particles come first because animations need them.
|
||||
|
||||
+15
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.client.ClientMoLangFunctions
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.JsonModelAdapter.Companion.model
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
|
||||
@@ -385,6 +386,10 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
incrementAge(entity)
|
||||
playWildShinySounds()
|
||||
sendRidingChanges(entity)
|
||||
if (entity.isNewlyHatched) {
|
||||
hatch()
|
||||
entity.isNewlyHatched = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendRidingChanges(entity: PokemonEntity) {
|
||||
@@ -398,6 +403,16 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fun hatch() {
|
||||
val animation = model?.getAnimation(this, "q.bedrock_stateful('hatch', 'hatch', 'endures_primary_animations');", runtime) ?: return
|
||||
if (animation is PrimaryAnimation) {
|
||||
addPrimaryAnimation(animation)
|
||||
}
|
||||
else {
|
||||
addActiveAnimation(animation)
|
||||
}
|
||||
}
|
||||
|
||||
fun playWildShinySounds() {
|
||||
val player = Minecraft.getInstance().player ?: return
|
||||
val isWithinRange = player.position().distanceTo(currentEntity.position()) <= Cobblemon.config.shinyNoticeParticlesDistance
|
||||
|
||||
@@ -257,6 +257,7 @@ class PCGUI(
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
|
||||
// Render Portrait Background
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = portraitBackgroundResource,
|
||||
@@ -272,6 +273,7 @@ class PCGUI(
|
||||
}
|
||||
|
||||
// Render Base Resource
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = baseResource,
|
||||
|
||||
+5
@@ -16,6 +16,11 @@ object CobblemonAtlases {
|
||||
val atlases = mutableSetOf<TextureAtlasHolder>()
|
||||
|
||||
val BERRY_SPRITE_ATLAS = register("textures/atlas/berries.png", "berries")
|
||||
val EGG_PATTERN_ATLAS = register("textures/atlas/egg_patterns.png", "egg_patterns")
|
||||
//val EGG_PATTERN_SPRITE_ATLAS = register("textures/atlas/egg_pattern_sprites.png", "egg_pattern_sprites")
|
||||
//val EGG_SPRITE_ATLAS = register("textures/atlas/eggs.png", "eggs")
|
||||
val EGG_PATTERN_SPRITE_ATLAS = register("textures/atlas/egg_pattern_sprites.png", "egg_pattern_sprites")
|
||||
|
||||
fun register(atlasId: String, sourcePath: String): TextureAtlasHolder {
|
||||
val atlas = CobblemonAtlas(
|
||||
Minecraft.getInstance().textureManager,
|
||||
|
||||
+3
-2
@@ -8,10 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.BlockEntityRenderState
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer
|
||||
import net.minecraft.client.renderer.GameRenderer
|
||||
|
||||
class BerryBlockEntityRenderState : BerryBlockEntity.RenderState {
|
||||
class BasicBlockEntityRenderState : BlockEntityRenderState {
|
||||
val lastRenderFrame = -1
|
||||
override var needsRebuild = true
|
||||
val vbo: VertexBuffer = VertexBuffer(VertexBuffer.Usage.STATIC)
|
||||
+3
-3
@@ -52,10 +52,10 @@ class BerryBlockRenderer(private val context: BlockEntityRendererProvider.Contex
|
||||
if (!shouldRender(entity, entity.blockPos.toVec3d())) return
|
||||
val blockState = entity.blockState
|
||||
if (entity.renderState == null) {
|
||||
entity.renderState = BerryBlockEntityRenderState()
|
||||
entity.renderState = BasicBlockEntityRenderState()
|
||||
}
|
||||
val bf = vertexConsumers.getBuffer(CobblemonRenderLayers.BERRY_LAYER)
|
||||
val renderState = entity.renderState as BerryBlockEntityRenderState
|
||||
val renderState = entity.renderState as BasicBlockEntityRenderState
|
||||
// if (renderState.needsRebuild || renderState.vboLightLevel != light) {
|
||||
renderToBuffer(entity, matrices, light, overlay, renderState, bf)
|
||||
// renderState.vboLightLevel = light
|
||||
@@ -98,7 +98,7 @@ class BerryBlockRenderer(private val context: BlockEntityRendererProvider.Contex
|
||||
}
|
||||
|
||||
|
||||
fun renderToBuffer(entity: BerryBlockEntity, matrices: PoseStack, light: Int, overlay: Int, renderState: BerryBlockEntityRenderState, buffer: VertexConsumer) {
|
||||
fun renderToBuffer(entity: BerryBlockEntity, matrices: PoseStack, light: Int, overlay: Int, renderState: BasicBlockEntityRenderState, buffer: VertexConsumer) {
|
||||
if (entity.blockState.getValue(BerryBlock.AGE) == 0) {
|
||||
renderBabyToBuffer(entity, matrices, light, overlay, buffer)
|
||||
renderState.drawVbo = true
|
||||
|
||||
+50
-17
@@ -9,11 +9,18 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.DisplayCaseBlock
|
||||
import com.cobblemon.mod.common.block.EggBlock
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.helper.EggRenderHelper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.PokedexItem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
@@ -22,6 +29,7 @@ import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Position
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.world.item.*
|
||||
import net.minecraft.world.item.component.CustomModelData
|
||||
@@ -69,27 +77,50 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
|
||||
*/
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.5f, 0.4f, 0.5f)
|
||||
|
||||
matrices.scale(posType.scaleX, posType.scaleY, posType.scaleZ)
|
||||
matrices.translate(posType.transX, posType.transY, posType.transZ)
|
||||
try {
|
||||
if (stack.`is`(CobblemonItems.POKEMON_EGG)) {
|
||||
val eggComponent = stack.get(CobblemonItemComponents.EGG_DATA)
|
||||
eggComponent?.egg?.let { egg ->
|
||||
// Render the egg using the EggRenderHelper
|
||||
EggRenderHelper.renderEgg(
|
||||
egg = egg,
|
||||
state = EggState(),
|
||||
matrices = matrices,
|
||||
vertexConsumers = vertexConsumers,
|
||||
light = light,
|
||||
overlay = overlay,
|
||||
yRot = 0f,
|
||||
scale = 1f,
|
||||
translation = Triple(0.5f, 0.6f, 0.5f), // Adjust translation
|
||||
rotation = Triple(180f, 0f, 0f), // Adjust rotation
|
||||
tickDelta = 0F
|
||||
)
|
||||
}
|
||||
return // Exit early after rendering the egg
|
||||
}
|
||||
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(-yRot))
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(posType.rotY))
|
||||
matrices.translate(0.5f, 0.4f, 0.5f)
|
||||
|
||||
Minecraft.getInstance().itemRenderer.renderStatic(
|
||||
stack,
|
||||
ItemDisplayContext.GROUND,
|
||||
light,
|
||||
overlay,
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
entity.level,
|
||||
0
|
||||
)
|
||||
matrices.scale(posType.scaleX, posType.scaleY, posType.scaleZ)
|
||||
matrices.translate(posType.transX, posType.transY, posType.transZ)
|
||||
|
||||
matrices.popPose()
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(-yRot))
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(posType.rotY))
|
||||
|
||||
Minecraft.getInstance().itemRenderer.renderStatic(
|
||||
stack,
|
||||
ItemDisplayContext.GROUND,
|
||||
light,
|
||||
overlay,
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
entity.level,
|
||||
0
|
||||
)
|
||||
} finally {
|
||||
matrices.popPose() // Ensure pose stack is balanced
|
||||
}
|
||||
}
|
||||
/*
|
||||
private fun renderPokemon(
|
||||
@@ -160,6 +191,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
|
||||
stack.item is BedItem -> PositioningType.BED
|
||||
stack.item is BannerItem -> PositioningType.BANNER
|
||||
stack.`is`(CobblemonItemTags.POKE_BALLS) -> PositioningType.POKE_BALL
|
||||
stack.`is`(CobblemonItems.POKEMON_EGG) -> PositioningType.EGG
|
||||
stack.item is PokedexItem -> PositioningType.POKEDEX
|
||||
stack.item == CobblemonItems.RELIC_COIN_POUCH -> PositioningType.COIN_POUCH
|
||||
stack.item == CobblemonItems.PASTURE -> PositioningType.PASTURE
|
||||
@@ -185,6 +217,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
|
||||
MOB_HEAD(1f, 1f, 1f, 0f, -0.025f, 0f, 180f),
|
||||
SHIELD(1f, 1f, 1f, 0f, -0.045f, 0f, 180f),
|
||||
PASTURE(1f, 1f, 1f, 0f, 0.0375f, 0f),
|
||||
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f)
|
||||
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f),
|
||||
EGG(1f, 1f, 1f, 0f, 1f, 0f)
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.helper.EggRenderHelper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.EggModelRepo
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class EggBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EggBlockEntity> {
|
||||
override fun render(
|
||||
entity: EggBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: PoseStack,
|
||||
vertexConsumers: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
// Ensure the renderer runs only on the client
|
||||
val isClientSide = entity.level?.isClientSide ?: false
|
||||
if (!isClientSide) return
|
||||
|
||||
entity.getEgg()?.let { egg ->
|
||||
matrices.pushPose() // Start transformation stack
|
||||
try {
|
||||
// Render the egg model with the dynamically determined texture
|
||||
EggRenderHelper.renderEgg(
|
||||
egg = egg,
|
||||
state = EggState(),
|
||||
matrices = matrices,
|
||||
vertexConsumers = vertexConsumers,
|
||||
light = light,
|
||||
overlay = overlay,
|
||||
yRot = 0f,
|
||||
scale = 1f,
|
||||
translation = Triple(0.5f, 0.285f, 0.5f), // Adjust translation
|
||||
rotation = Triple(180f, 0f, 0f), // Adjust rotation
|
||||
tickDelta = 0F
|
||||
)
|
||||
} finally {
|
||||
matrices.popPose() // Ensure this is always called
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.render.block
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
||||
import com.mojang.authlib.minecraft.client.MinecraftClient
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -24,7 +25,6 @@ import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class FossilAnalyzerRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<FossilAnalyzerBlockEntity> {
|
||||
|
||||
override fun render(
|
||||
entity: FossilAnalyzerBlockEntity,
|
||||
tickDelta: Float,
|
||||
@@ -45,7 +45,7 @@ class FossilAnalyzerRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEn
|
||||
|
||||
struct.fossilInventory.forEachIndexed { index, fossilStack ->
|
||||
matrices.pushPose()
|
||||
|
||||
|
||||
val dirOffset = when (direction) {
|
||||
Direction.NORTH -> Vec3(0.0, 0.0, 0.05)
|
||||
Direction.SOUTH -> Vec3(0.0, 0.0, -0.05)
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.LevelRenderer
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class GrottoBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<GrottoBlockEntity> {
|
||||
|
||||
override fun render(
|
||||
entity: GrottoBlockEntity,
|
||||
partialTicks: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
val stateToRender: BlockState = entity.mimickedState
|
||||
val level = entity.level ?: return
|
||||
|
||||
poseStack.pushPose()
|
||||
|
||||
Minecraft.getInstance().blockRenderer.renderBatched(
|
||||
stateToRender,
|
||||
entity.blockPos,
|
||||
level,
|
||||
poseStack,
|
||||
bufferSource.getBuffer(RenderType.solid()),
|
||||
true,
|
||||
level.random
|
||||
)
|
||||
|
||||
val overlayTexture = cobblemonResource("textures/block/grotto_overlay.png")
|
||||
val overlayRenderType = RenderType.entityTranslucentCull(overlayTexture)
|
||||
val overlayBuffer = bufferSource.getBuffer(overlayRenderType)
|
||||
|
||||
renderOverlayCube(
|
||||
poseStack,
|
||||
overlayBuffer,
|
||||
packedLight,
|
||||
OverlayTexture.NO_OVERLAY
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
fun renderOverlayCube(
|
||||
poseStack: PoseStack,
|
||||
buffer: VertexConsumer,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val matrix = poseStack.last().pose()
|
||||
val min = -0.001f
|
||||
val max = 1.001f
|
||||
|
||||
// Front (Z+)
|
||||
buffer.addVertex(matrix, min, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(min, min, max)
|
||||
buffer.addVertex(matrix, max, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(min, min, max)
|
||||
buffer.addVertex(matrix, max, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(min, min, max)
|
||||
buffer.addVertex(matrix, min, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(min, min, max)
|
||||
|
||||
// Back (Z-)
|
||||
buffer.addVertex(matrix, max, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(min, min, -max)
|
||||
buffer.addVertex(matrix, min, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(min, min, -max)
|
||||
buffer.addVertex(matrix, min, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(min, min, -max)
|
||||
buffer.addVertex(matrix, max, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(min, min, -max)
|
||||
|
||||
// Left (X-)
|
||||
buffer.addVertex(matrix, min, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(-max, min, min)
|
||||
buffer.addVertex(matrix, min, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(-max, min, min)
|
||||
buffer.addVertex(matrix, min, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(-max, min, min)
|
||||
buffer.addVertex(matrix, min, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(-max, min, min)
|
||||
|
||||
// Right (X+)
|
||||
buffer.addVertex(matrix, max, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(max, min, min)
|
||||
buffer.addVertex(matrix, max, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(max, min, min)
|
||||
buffer.addVertex(matrix, max, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(max, min, min)
|
||||
buffer.addVertex(matrix, max, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(max, min, min)
|
||||
|
||||
// Top (Y+)
|
||||
buffer.addVertex(matrix, min, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(min, max, min)
|
||||
buffer.addVertex(matrix, max, max, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(min, max, min)
|
||||
buffer.addVertex(matrix, max, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(min, max, min)
|
||||
buffer.addVertex(matrix, min, max, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(min, max, min)
|
||||
|
||||
// Bottom (Y-)
|
||||
buffer.addVertex(matrix, min, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(min, max).setOverlay(overlay).setLight(light).setNormal(min, -max, min)
|
||||
buffer.addVertex(matrix, max, min, min).setColor(255, 255, 255, 128)
|
||||
.setUv(max, max).setOverlay(overlay).setLight(light).setNormal(min, -max, min)
|
||||
buffer.addVertex(matrix, max, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(max, min).setOverlay(overlay).setLight(light).setNormal(min, -max, min)
|
||||
buffer.addVertex(matrix, min, min, max).setColor(255, 255, 255, 128)
|
||||
.setUv(min, min).setOverlay(overlay).setLight(light).setNormal(min, -max, min)
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.helper.EggRenderHelper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.EggModelRepo
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class IncubatorRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<IncubatorBlockEntity> {
|
||||
|
||||
override fun render(
|
||||
entity: IncubatorBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: PoseStack,
|
||||
vertexConsumers: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
// Ensure the renderer runs only on the client
|
||||
val isClientSide = entity.level?.isClientSide ?: false
|
||||
if (!isClientSide) return
|
||||
|
||||
// Retrieve the egg data from the entity
|
||||
val egg = entity.getItem(0).get(CobblemonItemComponents.EGG_DATA)?.egg ?: return
|
||||
matrices.pushPose()
|
||||
// Render the egg model with the dynamically determined texture
|
||||
EggRenderHelper.renderEgg(
|
||||
egg = egg,
|
||||
state = entity.eggState,
|
||||
matrices = matrices,
|
||||
vertexConsumers = vertexConsumers,
|
||||
light = light,
|
||||
overlay = overlay,
|
||||
yRot = 0f,
|
||||
scale = 1f,
|
||||
translation = Triple(0.5f, 0.54f, 0.5f), // Adjust translation
|
||||
rotation = Triple(180f, 0f, 0f), // Adjust rotation
|
||||
tickDelta = tickDelta
|
||||
)
|
||||
|
||||
matrices.popPose() // Restore the matrix stack
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.helper.EggRenderHelper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.EggModelRepo
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class NestBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<NestBlockEntity> {
|
||||
|
||||
override fun render(
|
||||
entity: NestBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: PoseStack,
|
||||
vertexConsumers: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val isClientSide = entity.level?.isClientSide ?: false
|
||||
if (!isClientSide) return
|
||||
|
||||
val egg = entity.getItem().get(CobblemonItemComponents.EGG_DATA)?.egg ?: return
|
||||
|
||||
EggRenderHelper.renderEgg(
|
||||
egg = egg,
|
||||
state = entity.eggState,
|
||||
matrices = matrices,
|
||||
vertexConsumers = vertexConsumers,
|
||||
light = light,
|
||||
overlay = overlay,
|
||||
yRot = 0f,
|
||||
scale = 1f,
|
||||
translation = Triple(0.5f, 0.42f, 0.5f),
|
||||
rotation = Triple(180f, 0f, 0f),
|
||||
tickDelta = tickDelta
|
||||
)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import net.minecraft.client.color.block.BlockColor
|
||||
import net.minecraft.client.renderer.BiomeColors
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.BlockAndTintGetter
|
||||
import net.minecraft.world.level.GrassColor
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
object NestLeafBlockColorProvider : BlockColor {
|
||||
override fun getColor(
|
||||
blockState: BlockState,
|
||||
blockAndTintGetter: BlockAndTintGetter?,
|
||||
blockPos: BlockPos?,
|
||||
i: Int
|
||||
): Int {
|
||||
if (blockAndTintGetter == null || blockPos == null) {
|
||||
return GrassColor.get(0.5, 1.0)
|
||||
}
|
||||
|
||||
return BiomeColors.getAverageFoliageColor(blockAndTintGetter, blockPos)
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package com.cobblemon.mod.common.client.render.helper
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.egg.EggState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.EggModelRepo
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.breeding.TriggerEggHatchPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
|
||||
object EggRenderHelper {
|
||||
val context = RenderContext().also {
|
||||
it.put(RenderContext.DO_QUIRKS, true)
|
||||
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.RESURRECTION_MACHINE) // todo make new one that is false
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given egg with animations based on its time to hatch.
|
||||
*
|
||||
* @param egg The egg to render.
|
||||
* @param matrices The pose stack for transformations.
|
||||
* @param vertexConsumers The buffer for rendering.
|
||||
* @param light The light level.
|
||||
* @param overlay The overlay level.
|
||||
* @param yRot The rotation angle in degrees around the Y-axis.
|
||||
* @param scale The scale of the egg model.
|
||||
* @param translation Adjustments for translation in the form [x, y, z].
|
||||
* @param rotation Adjustments for rotation in the form [x, y, z].
|
||||
* @param animTime Current animation time in seconds.
|
||||
*/
|
||||
fun renderEgg(
|
||||
egg: Egg,
|
||||
state: EggState,
|
||||
matrices: PoseStack,
|
||||
vertexConsumers: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int,
|
||||
yRot: Float,
|
||||
scale: Float = 1.0f,
|
||||
translation: Triple<Float, Float, Float> = Triple(0.5f, 0.5f, 0.5f),
|
||||
rotation: Triple<Float, Float, Float> = Triple(0f, 0f, 0f),
|
||||
tickDelta: Float
|
||||
) {
|
||||
// Determine the texture
|
||||
val form = if (egg.form.lowercase() == "normal") "" else "_${egg.form.lowercase()}"
|
||||
val texture = cobblemonResource("textures/egg/${egg.species.path}${form}_egg.png")
|
||||
|
||||
val eggState = state
|
||||
eggState.updatePartialTicks(tickDelta)
|
||||
|
||||
val eggModel = VaryingModelRepository.getPoser(cobblemonResource("egg"), eggState) as EggModel
|
||||
eggModel.context = context
|
||||
|
||||
val vertexConsumer = vertexConsumers.getBuffer(RenderType.entityCutout(texture))
|
||||
|
||||
eggState.currentModel = eggModel
|
||||
val poseName = when { // todo polish these values
|
||||
egg.timeToHatch > 9600 -> "stage0" // Above 8 minutes left has No animation
|
||||
egg.timeToHatch > 2400 -> "stage1" // 8 minutes left
|
||||
egg.timeToHatch > 1200 -> "stage2" // 2 minutes left
|
||||
egg.timeToHatch > 20 -> "stage3" // 1 minute left
|
||||
(egg.timeToHatch == 0F && eggState.isHatching) -> "hatching" // currently trying to hatch....
|
||||
else -> "stage4" // Ready to hatch!
|
||||
}
|
||||
eggState.setPose(poseName)
|
||||
|
||||
if (poseName == "hatching" && eggState.primaryAnimation == null) {
|
||||
val animation = eggModel.getAnimation(eggState, "hatch", eggModel.runtime)
|
||||
if (animation is com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation) {
|
||||
eggState.addPrimaryAnimation(animation)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
eggState.primaryAnimation?.animation?.applyEffects(
|
||||
entity = null,
|
||||
state = eggState,
|
||||
previousSeconds = eggState.previousAnimationSeconds,
|
||||
newSeconds = eggState.animationSeconds
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Failed to apply animation effects", e)
|
||||
}
|
||||
|
||||
// Trigger hatch packet after animation finishes
|
||||
if (state.isHatching && !state.hatchingTriggered && state.isHatchAnimationFinished()) {
|
||||
println("Sending hatch packet for block at ${state.renderingBlockPos}")
|
||||
state.hatchingTriggered = true
|
||||
state.renderingBlockPos?.let {
|
||||
CobblemonNetwork.sendToServer(TriggerEggHatchPacket(it))
|
||||
}
|
||||
eggState.resetHatchingState()
|
||||
}
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(translation.first, translation.second, translation.third)
|
||||
matrices.mulPose(com.mojang.math.Axis.XP.rotationDegrees(rotation.first))
|
||||
matrices.mulPose(com.mojang.math.Axis.YP.rotationDegrees(-yRot + rotation.second))
|
||||
matrices.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(rotation.third))
|
||||
matrices.scale(scale, scale, scale)
|
||||
|
||||
context.put(RenderContext.TEXTURE, texture)
|
||||
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.RESURRECTION_MACHINE)
|
||||
context.put(RenderContext.POSABLE_STATE, eggState)
|
||||
|
||||
matrices.pushPose()
|
||||
eggModel.applyAnimations(
|
||||
entity = null,
|
||||
state = eggState,
|
||||
headYaw = 0F,
|
||||
headPitch = 0F,
|
||||
limbSwing = 0F,
|
||||
limbSwingAmount = 0F,
|
||||
ageInTicks = eggState.animationSeconds * 20
|
||||
)
|
||||
|
||||
eggModel.render(context, matrices, vertexConsumer, light, overlay, -0x1)
|
||||
eggModel.withLayerContext(vertexConsumers, eggState, VaryingModelRepository.getLayers(cobblemonResource("egg"), eggState)) {
|
||||
eggModel.render(context, matrices, vertexConsumer, light, OverlayTexture.NO_OVERLAY, -0x1)
|
||||
}
|
||||
|
||||
eggModel.setDefault()
|
||||
matrices.popPose()
|
||||
matrices.popPose()
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.render.item
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
import net.minecraft.client.renderer.item.ItemProperties
|
||||
@@ -20,6 +21,7 @@ object CobblemonModelPredicateRegistry {
|
||||
fun registerPredicates() {
|
||||
|
||||
val rods = listOf(
|
||||
|
||||
CobblemonItems.AZURE_ROD,
|
||||
CobblemonItems.BEAST_ROD,
|
||||
CobblemonItems.CHERISH_ROD,
|
||||
@@ -90,7 +92,13 @@ object CobblemonModelPredicateRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ItemProperties.register(CobblemonItems.POKEMON_EGG, ResourceLocation.parse("species")) { stack, world, entity, seed ->
|
||||
val eggComponent = stack.get(CobblemonItemComponents.EGG_DATA) ?: return@register 0.0f
|
||||
val egg = eggComponent.egg ?: return@register 0.0f
|
||||
val form = if (egg.form.lowercase() == "normal") "" else "_${egg.form.lowercase()}"
|
||||
val modelId = PokemonEggItemModelRegistry.getModelId(egg.species.path + form)
|
||||
//println("Evaluating species predicate for ${egg.species.path} -> $modelId")
|
||||
return@register modelId
|
||||
}
|
||||
}
|
||||
}
|
||||
+613
@@ -0,0 +1,613 @@
|
||||
package com.cobblemon.mod.common.client.render.item
|
||||
|
||||
object PokemonEggItemModelRegistry {
|
||||
private val speciesModelMap = mapOf(
|
||||
// gen 1
|
||||
"bulbasaur" to 0.0001f,
|
||||
"charmander" to 0.0002f,
|
||||
"squirtle" to 0.0003f,
|
||||
"caterpie" to 0.0004f,
|
||||
"weedle" to 0.0005f,
|
||||
"pidgey" to 0.0006f,
|
||||
"rattata" to 0.0007f,
|
||||
"rattata_alola" to 0.0008f,
|
||||
"spearow" to 0.0009f,
|
||||
"ekans" to 0.0010f,
|
||||
"sandshrew" to 0.0011f,
|
||||
"sandshrew_alola" to 0.0012f,
|
||||
"nidoranm" to 0.0013f,
|
||||
"nidoranf" to 0.0014f,
|
||||
"vulpix" to 0.0015f,
|
||||
"vulpix_alola" to 0.0016f,
|
||||
"zubat" to 0.0017f,
|
||||
"oddish" to 0.0018f,
|
||||
"paras" to 0.0019f,
|
||||
"venonat" to 0.0020f,
|
||||
"diglett" to 0.0021f,
|
||||
"diglett_alola" to 0.0022f,
|
||||
"meowth" to 0.0023f,
|
||||
"meowth_alola" to 0.0024f,
|
||||
"meowth_galar" to 0.0025f,
|
||||
"psyduck" to 0.0026f,
|
||||
"mankey" to 0.0027f,
|
||||
"growlithe" to 0.0028f,
|
||||
"growlithe_hisui" to 0.0029f,
|
||||
"poliwag" to 0.0030f,
|
||||
"abra" to 0.0031f,
|
||||
"machop" to 0.0032f,
|
||||
"bellsprout" to 0.0033f,
|
||||
"tentacool" to 0.0034f,
|
||||
"geodude" to 0.0035f,
|
||||
"geodude_alola" to 0.0036f,
|
||||
"ponyta" to 0.0037f,
|
||||
"ponyta_galar" to 0.0038f,
|
||||
"slowpoke" to 0.0039f,
|
||||
"slowpoke_galar" to 0.0040f,
|
||||
"magnemite" to 0.0041f,
|
||||
"farfetchd" to 0.0042f,
|
||||
"farfetchd_galar" to 0.0043f,
|
||||
"doduo" to 0.0044f,
|
||||
"seel" to 0.0045f,
|
||||
"grimer" to 0.0046f,
|
||||
"grimer_alola" to 0.0047f,
|
||||
"shellder" to 0.0048f,
|
||||
"gastly" to 0.0049f,
|
||||
"onix" to 0.0050f,
|
||||
"drowzee" to 0.0051f,
|
||||
"krabby" to 0.0052f,
|
||||
"voltorb" to 0.0053f,
|
||||
"voltorb_hisui" to 0.0054f,
|
||||
"exeggcute" to 0.0055f,
|
||||
"cubone" to 0.0056f,
|
||||
"lickitung" to 0.0057f,
|
||||
"koffing" to 0.0058f,
|
||||
"rhyhorn" to 0.0059f,
|
||||
"tangela" to 0.0060f,
|
||||
"kangaskhan" to 0.0061f,
|
||||
"horsea" to 0.0062f,
|
||||
"goldeen" to 0.0063f,
|
||||
"staryu" to 0.0064f,
|
||||
"scyther" to 0.0065f,
|
||||
"pinsir" to 0.0066f,
|
||||
"tauros" to 0.0067f,
|
||||
"tauros_paldea" to 0.0068f,
|
||||
"magikarp" to 0.0069f,
|
||||
"lapras" to 0.0070f,
|
||||
"ditto" to 0.0071f,
|
||||
"eevee" to 0.0072f,
|
||||
"porygon" to 0.0073f,
|
||||
"omanyte" to 0.0074f,
|
||||
"kabuto" to 0.0075f,
|
||||
"aerodactyl" to 0.0076f,
|
||||
"articuno" to 0.0077f,
|
||||
"articuno_galar" to 0.0078f,
|
||||
"zapdos" to 0.0079f,
|
||||
"zapdos_galar" to 0.0080f,
|
||||
"moltres" to 0.0081f,
|
||||
"moltres_galar" to 0.0082f,
|
||||
"dratini" to 0.0083f,
|
||||
"mewtwo" to 0.0084f,
|
||||
"mew" to 0.0085f,
|
||||
|
||||
// gen 2
|
||||
"chikorita" to 0.0086f,
|
||||
"cyndaquil" to 0.0087f,
|
||||
"totodile" to 0.0088f,
|
||||
"sentret" to 0.0089f,
|
||||
"hoothoot" to 0.0090f,
|
||||
"ledyba" to 0.0091f,
|
||||
"spinarak" to 0.0092f,
|
||||
"chinchou" to 0.0093f,
|
||||
"pichu" to 0.0094f,
|
||||
"cleffa" to 0.0095f,
|
||||
"igglybuff" to 0.0096f,
|
||||
"togepi" to 0.0097f,
|
||||
"natu" to 0.0098f,
|
||||
"mareep" to 0.0099f,
|
||||
"hoppip" to 0.0100f,
|
||||
"aipom" to 0.0101f,
|
||||
"sunkern" to 0.0102f,
|
||||
"yanma" to 0.0103f,
|
||||
"wooper" to 0.0104f,
|
||||
"wooper_paldea" to 0.0105f,
|
||||
"murkrow" to 0.0106f,
|
||||
"misdreavus" to 0.0107f,
|
||||
"unown" to 0.0108f,
|
||||
"girafarig" to 0.0109f,
|
||||
"pineco" to 0.0110f,
|
||||
"dunsparce" to 0.0111f,
|
||||
"gligar" to 0.0112f,
|
||||
"snubbull" to 0.0113f,
|
||||
"qwilfish" to 0.0114f,
|
||||
"qwilfish_hisui" to 0.0115f,
|
||||
"shuckle" to 0.0116f,
|
||||
"heracross" to 0.0117f,
|
||||
"sneasel" to 0.0118f,
|
||||
"sneasel_hisui" to 0.0119f,
|
||||
"teddiursa" to 0.0120f,
|
||||
"slugma" to 0.0121f,
|
||||
"swinub" to 0.0122f,
|
||||
"corsola" to 0.0123f,
|
||||
"corsola_galar" to 0.0124f,
|
||||
"remoraid" to 0.0125f,
|
||||
"delibird" to 0.0126f,
|
||||
"skarmory" to 0.0127f,
|
||||
"houndour" to 0.0128f,
|
||||
"phanpy" to 0.0129f,
|
||||
"stantler" to 0.0130f,
|
||||
"smeargle" to 0.0131f,
|
||||
"tyrogue" to 0.0132f,
|
||||
"smoochum" to 0.0133f,
|
||||
"elekid" to 0.0134f,
|
||||
"magby" to 0.0135f,
|
||||
"miltank" to 0.0136f,
|
||||
"raikou" to 0.0137f,
|
||||
"entei" to 0.0138f,
|
||||
"suicune" to 0.0139f,
|
||||
"larvitar" to 0.0140f,
|
||||
"lugia" to 0.0141f,
|
||||
"hooh" to 0.0142f,
|
||||
"celebi" to 0.0143f,
|
||||
|
||||
// gen 3
|
||||
"treecko" to 0.0144f,
|
||||
"torchic" to 0.0145f,
|
||||
"mudkip" to 0.0146f,
|
||||
"poochyena" to 0.0147f,
|
||||
"zigzagoon" to 0.0148f,
|
||||
"zigzagoon_galar" to 0.0149f,
|
||||
"wurmple" to 0.0150f,
|
||||
"lotad" to 0.0151f,
|
||||
"seedot" to 0.0152f,
|
||||
"taillow" to 0.0153f,
|
||||
"wingull" to 0.0154f,
|
||||
"ralts" to 0.0155f,
|
||||
"surskit" to 0.0156f,
|
||||
"shroomish" to 0.0157f,
|
||||
"slakoth" to 0.0158f,
|
||||
"nincada" to 0.0159f,
|
||||
"whismur" to 0.0160f,
|
||||
"makuhita" to 0.0161f,
|
||||
"azurill" to 0.0162f,
|
||||
"nosepass" to 0.0163f,
|
||||
"skitty" to 0.0164f,
|
||||
"sableye" to 0.0165f,
|
||||
"mawile" to 0.0166f,
|
||||
"aron" to 0.0167f,
|
||||
"meditite" to 0.0168f,
|
||||
"electrike" to 0.0169f,
|
||||
"plusle" to 0.0170f,
|
||||
"minun" to 0.0171f,
|
||||
"volbeat" to 0.0172f,
|
||||
"illumise" to 0.0173f,
|
||||
"roselia" to 0.0174f,
|
||||
"gulpin" to 0.0175f,
|
||||
"carvanha" to 0.0176f,
|
||||
"wailmer" to 0.0177f,
|
||||
"numel" to 0.0178f,
|
||||
"torkoal" to 0.0179f,
|
||||
"spoink" to 0.0180f,
|
||||
"spinda" to 0.0181f,
|
||||
"trapinch" to 0.0182f,
|
||||
"cacnea" to 0.0183f,
|
||||
"swablu" to 0.0184f,
|
||||
"zangoose" to 0.0185f,
|
||||
"seviper" to 0.0186f,
|
||||
"lunatone" to 0.0187f,
|
||||
"solrock" to 0.0188f,
|
||||
"barboach" to 0.0189f,
|
||||
"corphish" to 0.0190f,
|
||||
"baltoy" to 0.0191f,
|
||||
"lileep" to 0.0192f,
|
||||
"anorith" to 0.0193f,
|
||||
"feebas" to 0.0194f,
|
||||
"castform" to 0.0195f,
|
||||
"kecleon" to 0.0196f,
|
||||
"shuppet" to 0.0197f,
|
||||
"duskull" to 0.0198f,
|
||||
"tropius" to 0.0199f,
|
||||
"chimecho" to 0.0200f,
|
||||
"absol" to 0.0201f,
|
||||
"wynaut" to 0.0202f,
|
||||
"snorunt" to 0.0203f,
|
||||
"spheal" to 0.0204f,
|
||||
"clamperl" to 0.0205f,
|
||||
"relicanth" to 0.0206f,
|
||||
"luvdisc" to 0.0207f,
|
||||
"bagon" to 0.0208f,
|
||||
"beldum" to 0.0209f,
|
||||
"regirock" to 0.0210f,
|
||||
"regice" to 0.0211f,
|
||||
"registeel" to 0.0212f,
|
||||
"latias" to 0.0213f,
|
||||
"latios" to 0.0214f,
|
||||
"kyogre" to 0.0215f,
|
||||
"groudon" to 0.0216f,
|
||||
"rayquaza" to 0.0217f,
|
||||
"jirachi" to 0.0218f,
|
||||
"deoxys" to 0.0219f,
|
||||
|
||||
// gen 4
|
||||
"turtwig" to 0.0220f,
|
||||
"chimchar" to 0.0221f,
|
||||
"piplup" to 0.0222f,
|
||||
"starly" to 0.0223f,
|
||||
"bidoof" to 0.0224f,
|
||||
"kricketot" to 0.0225f,
|
||||
"shinx" to 0.0226f,
|
||||
"budew" to 0.0227f,
|
||||
"cranidos" to 0.0228f,
|
||||
"shieldon" to 0.0229f,
|
||||
"burmy" to 0.0230f,
|
||||
"combee" to 0.0231f,
|
||||
"pachirisu" to 0.0232f,
|
||||
"buizel" to 0.0233f,
|
||||
"cherubi" to 0.0234f,
|
||||
"shellos" to 0.0235f,
|
||||
"shellos_east" to 0.0236f,
|
||||
"drifloon" to 0.0237f,
|
||||
"buneary" to 0.0238f,
|
||||
"glameow" to 0.0239f,
|
||||
"chingling" to 0.0240f,
|
||||
"stunky" to 0.0241f,
|
||||
"bronzor" to 0.0242f,
|
||||
"bonsly" to 0.0243f,
|
||||
"mimejr" to 0.0244f,
|
||||
"happiny" to 0.0245f,
|
||||
"chatot" to 0.0246f,
|
||||
"spiritomb" to 0.0247f,
|
||||
"gible" to 0.0248f,
|
||||
"munchlax" to 0.0249f,
|
||||
"riolu" to 0.0250f,
|
||||
"hippopotas" to 0.0251f,
|
||||
"skorupi" to 0.0252f,
|
||||
"croagunk" to 0.0253f,
|
||||
"carnivine" to 0.0254f,
|
||||
"finneon" to 0.0255f,
|
||||
"mantyke" to 0.0256f,
|
||||
"snover" to 0.0257f,
|
||||
"rotom" to 0.0258f,
|
||||
"uxie" to 0.0259f,
|
||||
"mesprit" to 0.0260f,
|
||||
"azelf" to 0.0261f,
|
||||
"dialga" to 0.0262f,
|
||||
"palkia" to 0.0263f,
|
||||
"heatran" to 0.0264f,
|
||||
"regigigas" to 0.0265f,
|
||||
"giratina" to 0.0266f,
|
||||
"cresselia" to 0.0267f,
|
||||
"phione" to 0.0268f,
|
||||
"manaphy" to 0.0269f,
|
||||
"darkrai" to 0.0270f,
|
||||
"shaymin" to 0.0271f,
|
||||
"arceus" to 0.0272f,
|
||||
"victini" to 0.0273f,
|
||||
|
||||
// gen 5
|
||||
"snivy" to 0.0274f,
|
||||
"tepig" to 0.0275f,
|
||||
"oshawott" to 0.0276f,
|
||||
"patrat" to 0.0277f,
|
||||
"lillipup" to 0.0278f,
|
||||
"purrloin" to 0.0279f,
|
||||
"pansage" to 0.0280f,
|
||||
"pansear" to 0.0281f,
|
||||
"panpour" to 0.0282f,
|
||||
"munna" to 0.0283f,
|
||||
"pidove" to 0.0284f,
|
||||
"blitzle" to 0.0285f,
|
||||
"roggenrola" to 0.0286f,
|
||||
"woobat" to 0.0287f,
|
||||
"drilbur" to 0.0288f,
|
||||
"audino" to 0.0289f,
|
||||
"timburr" to 0.0290f,
|
||||
"tympole" to 0.0291f,
|
||||
"throh" to 0.0292f,
|
||||
"sawk" to 0.0293f,
|
||||
"sewaddle" to 0.0294f,
|
||||
"venipede" to 0.0295f,
|
||||
"cottonee" to 0.0296f,
|
||||
"petilil" to 0.0297f,
|
||||
"basculin" to 0.0298f,
|
||||
"sandile" to 0.0299f,
|
||||
"darumaka" to 0.0300f,
|
||||
"darumaka_galar" to 0.0301f,
|
||||
"maractus" to 0.0302f,
|
||||
"dwebble" to 0.0303f,
|
||||
"scraggy" to 0.0304f,
|
||||
"sigilyph" to 0.0305f,
|
||||
"yamask" to 0.0306f,
|
||||
"yamask_galar" to 0.0307f,
|
||||
"tirtouga" to 0.0308f,
|
||||
"archen" to 0.0309f,
|
||||
"trubbish" to 0.0310f,
|
||||
"zorua" to 0.0311f,
|
||||
"zorua_hisui" to 0.0312f,
|
||||
"minccino" to 0.0313f,
|
||||
"gothita" to 0.0314f,
|
||||
"solosis" to 0.0315f,
|
||||
"ducklett" to 0.0316f,
|
||||
"vanillite" to 0.0317f,
|
||||
"deerling" to 0.0318f,
|
||||
"emolga" to 0.0319f,
|
||||
"karrablast" to 0.0320f,
|
||||
"foongus" to 0.0321f,
|
||||
"frillish" to 0.0322f,
|
||||
"alomomola" to 0.0323f,
|
||||
"joltik" to 0.0324f,
|
||||
"ferroseed" to 0.0325f,
|
||||
"klink" to 0.0326f,
|
||||
"tynamo" to 0.0327f,
|
||||
"elgyem" to 0.0328f,
|
||||
"litwick" to 0.0329f,
|
||||
"axew" to 0.0330f,
|
||||
"cubchoo" to 0.0331f,
|
||||
"cryogonal" to 0.0332f,
|
||||
"shelmet" to 0.0333f,
|
||||
"stunfisk" to 0.0334f,
|
||||
"stunfisk_galar" to 0.0335f,
|
||||
"mienfoo" to 0.0336f,
|
||||
"druddigon" to 0.0337f,
|
||||
"golett" to 0.0338f,
|
||||
"pawniard" to 0.0339f,
|
||||
"bouffalant" to 0.0340f,
|
||||
"rufflet" to 0.0341f,
|
||||
"vullaby" to 0.0342f,
|
||||
"heatmor" to 0.0343f,
|
||||
"durant" to 0.0344f,
|
||||
"deino" to 0.0345f,
|
||||
"larvesta" to 0.0346f,
|
||||
"cobalion" to 0.0347f,
|
||||
"terrakion" to 0.0348f,
|
||||
"virizion" to 0.0349f,
|
||||
"tornadus" to 0.0350f,
|
||||
"thundurus" to 0.0351f,
|
||||
"reshiram" to 0.0352f,
|
||||
"zekrom" to 0.0353f,
|
||||
"landorus" to 0.0354f,
|
||||
"kyurem" to 0.0355f,
|
||||
"keldeo" to 0.0356f,
|
||||
"meloetta" to 0.0357f,
|
||||
"genesect" to 0.0358f,
|
||||
|
||||
// gen 6
|
||||
"chespin" to 0.0359f,
|
||||
"fennekin" to 0.0360f,
|
||||
"froakie" to 0.0361f,
|
||||
"bunnelby" to 0.0362f,
|
||||
"fletchling" to 0.0363f,
|
||||
"scatterbug" to 0.0364f,
|
||||
"litleo" to 0.0365f,
|
||||
"flabebe" to 0.0366f,
|
||||
"skiddo" to 0.0367f,
|
||||
"pancham" to 0.0368f,
|
||||
"furfrou" to 0.0369f,
|
||||
"espurr" to 0.0370f,
|
||||
"honedge" to 0.0371f,
|
||||
"spritzee" to 0.0372f,
|
||||
"swirlix" to 0.0373f,
|
||||
"inkay" to 0.0374f,
|
||||
"binacle" to 0.0375f,
|
||||
"skrelp" to 0.0376f,
|
||||
"clauncher" to 0.0377f,
|
||||
"helioptile" to 0.0378f,
|
||||
"tyrunt" to 0.0379f,
|
||||
"amaura" to 0.0380f,
|
||||
"hawlucha" to 0.0381f,
|
||||
"dedenne" to 0.0382f,
|
||||
"carbink" to 0.0383f,
|
||||
"goomy" to 0.0384f,
|
||||
"klefki" to 0.0385f,
|
||||
"phantump" to 0.0386f,
|
||||
"pumpkaboo" to 0.0387f,
|
||||
"bergmite" to 0.0388f,
|
||||
"noibat" to 0.0389f,
|
||||
"xerneas" to 0.0390f,
|
||||
"yveltal" to 0.0391f,
|
||||
"zygarde" to 0.0392f,
|
||||
"diancie" to 0.0393f,
|
||||
"hoopa" to 0.0394f,
|
||||
"volcanion" to 0.0395f,
|
||||
|
||||
// gen 7
|
||||
"rowlet" to 0.0396f,
|
||||
"litten" to 0.0397f,
|
||||
"popplio" to 0.0398f,
|
||||
"pikipek" to 0.0399f,
|
||||
"yungoos" to 0.0400f,
|
||||
"grubbin" to 0.0401f,
|
||||
"crabrawler" to 0.0402f,
|
||||
"oricorio" to 0.0403f,
|
||||
"oricorio_pompom" to 0.0404f,
|
||||
"oricorio_pau" to 0.0405f,
|
||||
"oricorio_sensu" to 0.0406f,
|
||||
"cutiefly" to 0.0407f,
|
||||
"rockruff" to 0.0408f,
|
||||
"wishiwashi" to 0.0409f,
|
||||
"mareanie" to 0.0410f,
|
||||
"mudbray" to 0.0411f,
|
||||
"dewpider" to 0.0412f,
|
||||
"fomantis" to 0.0413f,
|
||||
"morelull" to 0.0414f,
|
||||
"salandit" to 0.0415f,
|
||||
"stufful" to 0.0416f,
|
||||
"bounsweet" to 0.0417f,
|
||||
"comfey" to 0.0418f,
|
||||
"oranguru" to 0.0419f,
|
||||
"passimian" to 0.0420f,
|
||||
"wimpod" to 0.0421f,
|
||||
"sandygast" to 0.0422f,
|
||||
"pyukumuku" to 0.0423f,
|
||||
"typenull" to 0.0424f,
|
||||
"minior" to 0.0425f,
|
||||
"komala" to 0.0426f,
|
||||
"turtonator" to 0.0427f,
|
||||
"togedemaru" to 0.0428f,
|
||||
"mimikyu" to 0.0429f,
|
||||
"bruxish" to 0.0430f,
|
||||
"drampa" to 0.0431f,
|
||||
"dhelmise" to 0.0432f,
|
||||
"jangmoo" to 0.0433f,
|
||||
"tapukoko" to 0.0434f,
|
||||
"tapulele" to 0.0435f,
|
||||
"tapubulu" to 0.0436f,
|
||||
"tapufini" to 0.0437f,
|
||||
"cosmog" to 0.0438f,
|
||||
"nihilego" to 0.0439f,
|
||||
"buzzwole" to 0.0440f,
|
||||
"pheromosa" to 0.0441f,
|
||||
"xurkitree" to 0.0442f,
|
||||
"celesteela" to 0.0443f,
|
||||
"kartana" to 0.0444f,
|
||||
"guzzlord" to 0.0445f,
|
||||
"necrozma" to 0.0446f,
|
||||
"magearna" to 0.0447f,
|
||||
"marshadow" to 0.0448f,
|
||||
"poipole" to 0.0449f,
|
||||
"stakataka" to 0.0450f,
|
||||
"blacephalon" to 0.0451f,
|
||||
"zeraora" to 0.0452f,
|
||||
"meltan" to 0.0453f,
|
||||
|
||||
// gen 8
|
||||
"grookey" to 0.0454f,
|
||||
"scorbunny" to 0.0455f,
|
||||
"sobble" to 0.0456f,
|
||||
"skwovet" to 0.0457f,
|
||||
"rookidee" to 0.0458f,
|
||||
"blipbug" to 0.0459f,
|
||||
"nickit" to 0.0460f,
|
||||
"gossifleur" to 0.0461f,
|
||||
"wooloo" to 0.0462f,
|
||||
"chewtle" to 0.0463f,
|
||||
"yamper" to 0.0464f,
|
||||
"rolycoly" to 0.0465f,
|
||||
"applin" to 0.0466f,
|
||||
"silicobra" to 0.0467f,
|
||||
"cramorant" to 0.0468f,
|
||||
"arrokuda" to 0.0469f,
|
||||
"toxel" to 0.0470f,
|
||||
"sizzlipede" to 0.0471f,
|
||||
"clobbopus" to 0.0472f,
|
||||
"sinistea" to 0.0473f,
|
||||
"hatenna" to 0.0474f,
|
||||
"impidimp" to 0.0475f,
|
||||
"milcery" to 0.0476f,
|
||||
"falinks" to 0.0477f,
|
||||
"pincurchin" to 0.0478f,
|
||||
"snom" to 0.0479f,
|
||||
"stonjourner" to 0.0480f,
|
||||
"eiscue" to 0.0481f,
|
||||
"indeedee" to 0.0482f,
|
||||
"morpeko" to 0.0483f,
|
||||
"cufant" to 0.0484f,
|
||||
"dracozolt" to 0.0485f,
|
||||
"arctozolt" to 0.0486f,
|
||||
"dracovish" to 0.0487f,
|
||||
"arctovish" to 0.0488f,
|
||||
"duraludon" to 0.0489f,
|
||||
"dreepy" to 0.0490f,
|
||||
"zacian" to 0.0491f,
|
||||
"zamazenta" to 0.0492f,
|
||||
"eternatus" to 0.0493f,
|
||||
"kubfu" to 0.0494f,
|
||||
"zarude" to 0.0495f,
|
||||
"regieleki" to 0.0496f,
|
||||
"regidrago" to 0.0497f,
|
||||
"glastrier" to 0.0498f,
|
||||
"spectrier" to 0.0499f,
|
||||
"calyrex" to 0.0500f,
|
||||
"enamorus" to 0.0501f,
|
||||
|
||||
// gen 9
|
||||
"sprigatito" to 0.0502f,
|
||||
"fuecoco" to 0.0503f,
|
||||
"quaxly" to 0.0504f,
|
||||
"lechonk" to 0.0505f,
|
||||
"tarountula" to 0.0506f,
|
||||
"nymble" to 0.0507f,
|
||||
"pawmi" to 0.0508f,
|
||||
"tandemaus" to 0.0509f,
|
||||
"fidough" to 0.0510f,
|
||||
"smoliv" to 0.0511f,
|
||||
"squawkabilly" to 0.0512f,
|
||||
"squawkabilly_blue" to 0.0513f,
|
||||
"squawkabilly_yellow" to 0.0514f,
|
||||
"squawkabilly_white" to 0.0515f,
|
||||
"nacli" to 0.0516f,
|
||||
"charcadet" to 0.0517f,
|
||||
"tadbulb" to 0.0518f,
|
||||
"wattrel" to 0.0519f,
|
||||
"maschiff" to 0.0520f,
|
||||
"shroodle" to 0.0521f,
|
||||
"bramblin" to 0.0522f,
|
||||
"toedscool" to 0.0523f,
|
||||
"klawf" to 0.0524f,
|
||||
"capsakid" to 0.0525f,
|
||||
"rellor" to 0.0526f,
|
||||
"flittle" to 0.0527f,
|
||||
"tinkatink" to 0.0528f,
|
||||
"wiglett" to 0.0529f,
|
||||
"bombirdier" to 0.0530f,
|
||||
"finizen" to 0.0531f,
|
||||
"varoom" to 0.0532f,
|
||||
"cyclizar" to 0.0533f,
|
||||
"orthworm" to 0.0534f,
|
||||
"glimmet" to 0.0535f,
|
||||
"greavard" to 0.0536f,
|
||||
"flamigo" to 0.0537f,
|
||||
"cetoddle" to 0.0538f,
|
||||
"veluza" to 0.0539f,
|
||||
"dondozo" to 0.0540f,
|
||||
"tatsugiri" to 0.0541f,
|
||||
"tatsugiri_droopy" to 0.0542f,
|
||||
"tatsugiri_stretchy" to 0.0543f,
|
||||
"greattusk" to 0.0544f,
|
||||
"screamtail" to 0.0545f,
|
||||
"brutebonnet" to 0.0546f,
|
||||
"fluttermane" to 0.0547f,
|
||||
"slitherwing" to 0.0548f,
|
||||
"sandyshocks" to 0.0549f,
|
||||
"irontreads" to 0.0550f,
|
||||
"ironbundle" to 0.0551f,
|
||||
"ironhands" to 0.0552f,
|
||||
"ironjugulis" to 0.0553f,
|
||||
"ironmoth" to 0.0554f,
|
||||
"ironthorns" to 0.0555f,
|
||||
"frigibax" to 0.0556f,
|
||||
"gimmighoul" to 0.0557f,
|
||||
"wochien" to 0.0558f,
|
||||
"chienpao" to 0.0559f,
|
||||
"tinglu" to 0.0560f,
|
||||
"chiyu" to 0.0561f,
|
||||
"roaringmoon" to 0.0562f,
|
||||
"ironvaliant" to 0.0563f,
|
||||
"koraidon" to 0.0564f,
|
||||
"miraidon" to 0.0565f,
|
||||
"walkingwake" to 0.0566f,
|
||||
"ironleaves" to 0.0567f,
|
||||
"poltchageist" to 0.0568f,
|
||||
"okidogi" to 0.0569f,
|
||||
"munkidori" to 0.0570f,
|
||||
"fezandipiti" to 0.0571f,
|
||||
"ogerpon" to 0.0572f,
|
||||
"gougingfire" to 0.0573f,
|
||||
"ragingbolt" to 0.0574f,
|
||||
"ironboulder" to 0.0575f,
|
||||
"ironcrown" to 0.0576f,
|
||||
"terapagos" to 0.0577f,
|
||||
"pecharunt" to 0.0578f
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets the model ID for a Pokémon species.
|
||||
*
|
||||
* @param species The species path string.
|
||||
* @return The model ID, or 0.0f if the species is not found.
|
||||
*/
|
||||
fun getModelId(species: String): Float {
|
||||
val modelId = speciesModelMap[species] ?: 0.0f
|
||||
//println("Getting model ID for species: $species at ID: $modelId")
|
||||
return modelId
|
||||
}
|
||||
}
|
||||
+45
@@ -18,6 +18,7 @@ import java.util.function.Function
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.RenderStateShard.*
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer
|
||||
|
||||
object CobblemonRenderLayers {
|
||||
val BERRY_LAYER = run {
|
||||
@@ -85,5 +86,49 @@ object CobblemonRenderLayers {
|
||||
multiPhaseParameters
|
||||
)
|
||||
};
|
||||
val EGG_LAYER = run {
|
||||
val multiPhaseParameters = RenderType.CompositeState.builder()
|
||||
.setLightmapState(LIGHTMAP)
|
||||
.setShaderState(RENDERTYPE_CUTOUT_SHADER)
|
||||
.setTextureState(RenderStateShard.TextureStateShard(
|
||||
cobblemonResource("textures/atlas/egg_patterns.png"),
|
||||
false,
|
||||
true
|
||||
))
|
||||
.setCullState(RenderStateShard.NO_CULL)
|
||||
.createCompositeState(true)
|
||||
|
||||
RenderType.create(
|
||||
"egg_patterns",
|
||||
DefaultVertexFormat.BLOCK,
|
||||
VertexFormat.Mode.QUADS,
|
||||
512,
|
||||
true,
|
||||
false,
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
|
||||
//For overlaying the 2D sprites for eggs in inventory
|
||||
val EGG_SPRITE_LAYER = run {
|
||||
val multiPhaseParameters = RenderType.CompositeState.builder()
|
||||
.setShaderState(POSITION_COLOR_TEX_LIGHTMAP_SHADER)
|
||||
.setTextureState(RenderStateShard.TextureStateShard(
|
||||
cobblemonResource("textures/atlas/egg_pattern_sprites.png"),
|
||||
false,
|
||||
false
|
||||
))
|
||||
.setCullState(RenderStateShard.NO_CULL)
|
||||
.createCompositeState(true)
|
||||
RenderType.create(
|
||||
"egg_patterns",
|
||||
DefaultVertexFormat.POSITION_TEX_COLOR,
|
||||
VertexFormat.Mode.QUADS,
|
||||
512,
|
||||
true,
|
||||
false,
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user