forked from mirrors/cobblemon
Compare commits
414
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf1054070c | ||
|
|
a02866bbed | ||
|
|
5418b8e148 | ||
|
|
5da16df5ae | ||
|
|
24fc4b05ab | ||
|
|
173902feb1 | ||
|
|
fd5f08fe84 | ||
|
|
d430ad6ca1 | ||
|
|
1309c18292 | ||
|
|
b6deebac97 | ||
|
|
ab0f2cbd79 | ||
|
|
62c7882469 | ||
|
|
b6c2c65a6c | ||
|
|
01206fb67a | ||
|
|
07af63db25 | ||
|
|
1047648f68 | ||
|
|
6523a610ff | ||
|
|
6d132e3f39 | ||
|
|
83711f2dcb | ||
|
|
18e1a767af | ||
|
|
6ae3efb461 | ||
|
|
84e1934134 | ||
|
|
9aed9342ee | ||
|
|
e4d0bf39ca | ||
|
|
b311d092de | ||
|
|
6f134a50e0 | ||
|
|
847fa6842e | ||
|
|
08aa4f29bd | ||
|
|
539218381b | ||
|
|
9dff39b4d7 | ||
|
|
1e578e94e5 | ||
|
|
ec35c9b9bb | ||
|
|
0deaeaeb7d | ||
|
|
a0531f2d2f | ||
|
|
ec3d9e1a56 | ||
|
|
80f5050ecd | ||
|
|
e32a2da8a4 | ||
|
|
e92609b6c7 | ||
|
|
abc0c72707 | ||
|
|
e55f46c990 | ||
|
|
6ee5714bf9 | ||
|
|
bb379764f7 | ||
|
|
3ec78d9d7e | ||
|
|
12973dd66a | ||
|
|
ed30e4cd6f | ||
|
|
32c260446e | ||
|
|
41f1eb699f | ||
|
|
e68a593e9e | ||
|
|
903ffe2143 | ||
|
|
1d6845d595 | ||
|
|
f8eb72e437 | ||
|
|
814f84ae42 | ||
|
|
904be475ef | ||
|
|
28a3b8edfe | ||
|
|
96ee23b7d1 | ||
|
|
5edecbe81b | ||
|
|
f357033404 | ||
|
|
4800ae6649 | ||
|
|
d3ce829e5b | ||
|
|
60b0690c1e | ||
|
|
82e31dd766 | ||
|
|
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 | ||
|
|
130e767e75 | ||
|
|
6d761fef4f | ||
|
|
737ce1314c | ||
|
|
7b89f82d45 | ||
|
|
b1a1d509ec | ||
|
|
2203d543a1 | ||
|
|
c129157687 | ||
|
|
276c49ac5c | ||
|
|
0698cf495e | ||
|
|
6047660405 | ||
|
|
aaf3495f83 | ||
|
|
ae6529c9fd | ||
|
|
45a96b2a42 | ||
|
|
f517a63cc9 | ||
|
|
1e2f685ee5 | ||
|
|
5245614804 | ||
|
|
8b97c3cb85 | ||
|
|
d607463116 | ||
|
|
07cc92eb18 | ||
|
|
4212903c67 | ||
|
|
be47dbcede | ||
|
|
9665eba7b0 | ||
|
|
9f83c7c72c | ||
|
|
9bb74358f7 | ||
|
|
e2219730e6 | ||
|
|
4daaf50b1a | ||
|
|
9556c57c69 | ||
|
|
f72b65bfac | ||
|
|
6121700c9b | ||
|
|
91c968e743 | ||
|
|
8fed6f4823 | ||
|
|
34e4b39d75 | ||
|
|
b79792ebbf | ||
|
|
7a0b294d3d | ||
|
|
ad1312d077 | ||
|
|
a21c70ce46 | ||
|
|
8865a71f4c | ||
|
|
142cadc187 | ||
|
|
66f4c2531d | ||
|
|
3f8ddd5f7d | ||
|
|
c076e14cbd | ||
|
|
d724eb068b | ||
|
|
d3ac263e29 | ||
|
|
a6b7fe1f17 | ||
|
|
d65d7b779f | ||
|
|
7ce25d8158 | ||
|
|
178d7293fb | ||
|
|
ba0bba2202 | ||
|
|
29d5ce5f0e | ||
|
|
ec714ccb84 | ||
|
|
b9ebfc17ed | ||
|
|
2e28792c92 | ||
|
|
ab0a129ba1 | ||
|
|
648d15403f | ||
|
|
3fe3f521ee | ||
|
|
0ff638a0ca | ||
|
|
485b5f46f6 | ||
|
|
b7c2063510 | ||
|
|
94d7ae5290 | ||
|
|
d75d5da250 | ||
|
|
d3cd1be24e | ||
|
|
d1f11316ec | ||
|
|
1e50bd999b | ||
|
|
0257f6c6a0 | ||
|
|
a3bc1cf72b | ||
|
|
e236632c67 | ||
|
|
4730d97898 | ||
|
|
36994a134d | ||
|
|
97899b3fa9 | ||
|
|
713bd6d6ef | ||
|
|
a3f272b2aa | ||
|
|
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 | ||
|
|
77d91ed979 | ||
|
|
b07856ed58 | ||
|
|
b7d2f38490 | ||
|
|
295185c1cf | ||
|
|
f0d8ee21d9 | ||
|
|
b203020b4d | ||
|
|
5962149da8 | ||
|
|
24101bed08 | ||
|
|
9907ba17c1 | ||
|
|
a6f0e19a44 | ||
|
|
7ee064b04d | ||
|
|
f2e4c735f7 | ||
|
|
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 | ||
|
|
5345bd321f | ||
|
|
eab8d0063b | ||
|
|
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 | ||
|
|
6877977234 | ||
|
|
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 | ||
|
|
60cb81362e | ||
|
|
088acb8e86 | ||
|
|
77f59f06cb | ||
|
|
a0882c6105 | ||
|
|
d50eb52172 | ||
|
|
32dbd1f8c9 | ||
|
|
bcc23ec4fc | ||
|
|
143fc8256b | ||
|
|
70d753d1d3 | ||
|
|
00a67b22df | ||
|
|
12811fe504 | ||
|
|
5e5cadd314 | ||
|
|
75b06f82f0 | ||
|
|
2ab050aa60 | ||
|
|
6d2f645e4a | ||
|
|
9f937e29f2 | ||
|
|
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 | ||
|
|
abc61a96c8 | ||
|
|
65529474cb | ||
|
|
7d756249ae | ||
|
|
b1d10822d4 | ||
|
|
09d984456d | ||
|
|
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 |
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
|
||||
### Developer
|
||||
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
|
||||
- Renamed heaps of things in the spawning system to make more sense.
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.inventory.MenuType.MenuSupplier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(MenuType.class)
|
||||
public interface MenuTypeInvoker {
|
||||
@Invoker("<init>")
|
||||
static <T extends AbstractContainerMenu> MenuType<T> create(MenuSupplier<T> factory, FeatureFlagSet requiredFeatures) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -56,10 +56,7 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.*
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -195,6 +192,8 @@ object Cobblemon {
|
||||
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
|
||||
}
|
||||
|
||||
this.loadConfig()
|
||||
|
||||
implementation.registerRecipeSerializers()
|
||||
implementation.registerRecipeTypes()
|
||||
implementation.registerPermissionValidator()
|
||||
@@ -221,7 +220,6 @@ object Cobblemon {
|
||||
ExperienceGroups.registerDefaults()
|
||||
CaptureCalculators.registerDefaults()
|
||||
|
||||
this.loadConfig()
|
||||
// CobblemonBlockPredicates.touch()
|
||||
CobblemonOrePlacedFeatures.register()
|
||||
CobblemonPlacedFeatures.register()
|
||||
@@ -358,8 +356,12 @@ object Cobblemon {
|
||||
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
|
||||
pokedexNbtFactory.setup(server)
|
||||
|
||||
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
|
||||
tmMoveFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
|
||||
if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
@@ -386,8 +388,14 @@ object Cobblemon {
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
|
||||
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
|
||||
)
|
||||
tmMovesMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
|
||||
@@ -18,6 +18,7 @@ object CobblemonActivities {
|
||||
val POKEMON_SLEEPING_ACTIVITY = register(Activity("pokemon_sleeping"))
|
||||
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
|
||||
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
|
||||
val GUARD_NEST = register(Activity("guard_nest"))
|
||||
|
||||
fun register(activity: Activity): Activity {
|
||||
activities.add(activity)
|
||||
|
||||
@@ -40,6 +40,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
|
||||
|
||||
@JvmField
|
||||
val TM_SHELF: BlockEntityType<TMShelfBlockEntity> = this.create("tm_shelf", BlockEntityType.Builder.of(::TMShelfBlockEntity, CobblemonBlocks.TM_SHELF).build(null))
|
||||
|
||||
@JvmField
|
||||
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
|
||||
|
||||
@@ -49,6 +52,13 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
|
||||
@JvmField
|
||||
val TM_BLOCK: BlockEntityType<TMBlockEntity> = this.create("tm_block", BlockEntityType.Builder.of(::TMBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK: BlockEntityType<GrottoBlockEntity> = this.create("grotto_block",
|
||||
BlockEntityType.Builder.of(::GrottoBlockEntity, CobblemonBlocks.GROTTO_BLOCK).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
@@ -83,6 +93,46 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
).build(null)
|
||||
)
|
||||
|
||||
val NEST: BlockEntityType<NestBlockEntity> = this.create("nest",
|
||||
BlockEntityType.Builder.of(
|
||||
::NestBlockEntity,
|
||||
CobblemonBlocks.NEST_STICK,
|
||||
CobblemonBlocks.NEST_LEAF,
|
||||
CobblemonBlocks.NEST_STONE,
|
||||
CobblemonBlocks.NEST_DEEPSLATE,
|
||||
CobblemonBlocks.NEST_BASALT,
|
||||
CobblemonBlocks.NEST_SNOW,
|
||||
CobblemonBlocks.NEST_NETHER,
|
||||
CobblemonBlocks.NEST_SOUL_SAND,
|
||||
CobblemonBlocks.NEST_WARM_OCEAN,
|
||||
CobblemonBlocks.NEST_SEAGRASS,
|
||||
CobblemonBlocks.NEST_LUSH,
|
||||
CobblemonBlocks.NEST_HAY,
|
||||
CobblemonBlocks.NEST_SAND,
|
||||
CobblemonBlocks.NEST_RED_SAND,
|
||||
CobblemonBlocks.NEST_CHORUS,
|
||||
|
||||
CobblemonBlocks.WATER_NEST,
|
||||
CobblemonBlocks.CAVE_NEST,
|
||||
CobblemonBlocks.BASIC_NEST
|
||||
).build(null))
|
||||
|
||||
val EGG: BlockEntityType<EggBlockEntity> = this.create("egg",
|
||||
BlockEntityType.Builder.of(
|
||||
::EggBlockEntity,
|
||||
CobblemonBlocks.EGG
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR: BlockEntityType<IncubatorBlockEntity> = this.create("incubator",
|
||||
BlockEntityType.Builder.of(
|
||||
::IncubatorBlockEntity,
|
||||
CobblemonBlocks.INCUBATOR
|
||||
).build(null)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
@@ -98,12 +148,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
|
||||
)
|
||||
|
||||
/*@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
|
||||
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
|
||||
CobblemonBlocks.LURE_CAKE
|
||||
).build(null)*/
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
|
||||
"lure_cake",
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import com.cobblemon.mod.common.block.tm.TMBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -32,6 +33,7 @@ import net.minecraft.util.valueproviders.UniformInt
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
@@ -243,31 +245,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@@ -276,6 +278,51 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = this.create("type_gem_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0f, 10.0f)))
|
||||
|
||||
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
|
||||
|
||||
@JvmField val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("type_gem_block_normal")
|
||||
@JvmField val TYPE_GEM_BLOCK_FIRE = typeGemBlock("type_gem_block_fire")
|
||||
@JvmField val TYPE_GEM_BLOCK_WATER = typeGemBlock("type_gem_block_water")
|
||||
@JvmField val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("type_gem_block_electric")
|
||||
@JvmField val TYPE_GEM_BLOCK_GRASS = typeGemBlock("type_gem_block_grass")
|
||||
@JvmField val TYPE_GEM_BLOCK_ICE = typeGemBlock("type_gem_block_ice")
|
||||
@JvmField val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("type_gem_block_fighting")
|
||||
@JvmField val TYPE_GEM_BLOCK_POISON = typeGemBlock("type_gem_block_poison")
|
||||
@JvmField val TYPE_GEM_BLOCK_GROUND = typeGemBlock("type_gem_block_ground")
|
||||
@JvmField val TYPE_GEM_BLOCK_FLYING = typeGemBlock("type_gem_block_flying")
|
||||
@JvmField val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("type_gem_block_psychic")
|
||||
@JvmField val TYPE_GEM_BLOCK_BUG = typeGemBlock("type_gem_block_bug")
|
||||
@JvmField val TYPE_GEM_BLOCK_ROCK = typeGemBlock("type_gem_block_rock")
|
||||
@JvmField val TYPE_GEM_BLOCK_GHOST = typeGemBlock("type_gem_block_ghost")
|
||||
@JvmField val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("type_gem_block_dragon")
|
||||
@JvmField val TYPE_GEM_BLOCK_DARK = typeGemBlock("type_gem_block_dark")
|
||||
@JvmField val TYPE_GEM_BLOCK_STEEL = typeGemBlock("type_gem_block_steel")
|
||||
@JvmField val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("type_gem_block_fairy")
|
||||
|
||||
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
|
||||
|
||||
@JvmField val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("type_gem_cluster_normal", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("type_gem_cluster_normal"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("type_gem_cluster_fire", TYPE_GEM_BLOCK_FIRE, cobblemonResource("type_gem_cluster_fire"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_WATER = typeGemCluster("type_gem_cluster_water", TYPE_GEM_BLOCK_WATER, cobblemonResource("type_gem_cluster_water"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("type_gem_cluster_electric", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("type_gem_cluster_electric"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("type_gem_cluster_grass", TYPE_GEM_BLOCK_GRASS, cobblemonResource("type_gem_cluster_grass"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ICE = typeGemCluster("type_gem_cluster_ice", TYPE_GEM_BLOCK_ICE, cobblemonResource("type_gem_cluster_ice"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("type_gem_cluster_fighting", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("type_gem_cluster_fighting"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_POISON = typeGemCluster("type_gem_cluster_poison", TYPE_GEM_BLOCK_POISON, cobblemonResource("type_gem_cluster_poison"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("type_gem_cluster_ground", TYPE_GEM_BLOCK_GROUND, cobblemonResource("type_gem_cluster_ground"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("type_gem_cluster_flying", TYPE_GEM_BLOCK_FLYING, cobblemonResource("type_gem_cluster_flying"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("type_gem_cluster_psychic", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("type_gem_cluster_psychic"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_BUG = typeGemCluster("type_gem_cluster_bug", TYPE_GEM_BLOCK_BUG, cobblemonResource("type_gem_cluster_bug"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("type_gem_cluster_rock", TYPE_GEM_BLOCK_ROCK, cobblemonResource("type_gem_cluster_rock"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("type_gem_cluster_ghost", TYPE_GEM_BLOCK_GHOST, cobblemonResource("type_gem_cluster_ghost"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("type_gem_cluster_dragon", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("type_gem_cluster_dragon"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_DARK = typeGemCluster("type_gem_cluster_dark", TYPE_GEM_BLOCK_DARK, cobblemonResource("type_gem_cluster_dark"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("type_gem_cluster_steel", TYPE_GEM_BLOCK_STEEL, cobblemonResource("type_gem_cluster_steel"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("type_gem_cluster_fairy", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("type_gem_cluster_fairy"))
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@JvmField
|
||||
@@ -482,9 +529,26 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
|
||||
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_GREEN)
|
||||
.sound(SoundType.METAL)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.strength(2F)
|
||||
.noOcclusion()
|
||||
.lightLevel { if (it.getValue(TMBlock.ON) as Boolean) 10 else 0 }
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
|
||||
|
||||
@JvmField
|
||||
val TM_SHELF = create("tm_shelf", TMShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
|
||||
|
||||
@@ -614,6 +678,61 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
|
||||
|
||||
val NEST_STICK = nestBlock(NestBlock.NestVariant.STICK)
|
||||
val NEST_LEAF = nestBlock(NestBlock.NestVariant.LEAF)
|
||||
val NEST_STONE = nestBlock(NestBlock.NestVariant.STONE)
|
||||
val NEST_DEEPSLATE = nestBlock(NestBlock.NestVariant.DEEPSLATE)
|
||||
val NEST_BASALT = nestBlock(NestBlock.NestVariant.BASALT)
|
||||
val NEST_SNOW = nestBlock(NestBlock.NestVariant.SNOW)
|
||||
|
||||
val NEST_NETHER = this.create(
|
||||
"nest_nether",
|
||||
NestBlock(NestBlock.NestVariant.NETHER, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 2 })
|
||||
)
|
||||
val NEST_SOUL_SAND = nestBlock(NestBlock.NestVariant.SOUL_SAND)
|
||||
val NEST_WARM_OCEAN = this.create(
|
||||
"nest_warm_ocean",
|
||||
NestBlock(NestBlock.NestVariant.WARM_OCEAN, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY).lightLevel { 6 })
|
||||
)
|
||||
val NEST_SEAGRASS = nestBlock(NestBlock.NestVariant.SEAGRASS)
|
||||
val NEST_LUSH = nestBlock(NestBlock.NestVariant.LUSH)
|
||||
val NEST_HAY = nestBlock(NestBlock.NestVariant.HAY)
|
||||
val NEST_SAND = nestBlock(NestBlock.NestVariant.SAND)
|
||||
val NEST_RED_SAND = nestBlock(NestBlock.NestVariant.RED_SAND)
|
||||
val NEST_CHORUS = nestBlock(NestBlock.NestVariant.CHORUS)
|
||||
|
||||
|
||||
|
||||
val BASIC_NEST = nestBlock(NestBlock.NestVariant.BASIC)
|
||||
val CAVE_NEST = nestBlock(NestBlock.NestVariant.CAVE)
|
||||
//val NETHER_NEST = nestBlock(NestBlock.NestVariant.NETHER)
|
||||
val WATER_NEST = nestBlock(NestBlock.NestVariant.WATER)
|
||||
|
||||
val EGG = this.create("egg", EggBlock(BlockBehaviour.Properties
|
||||
.ofFullCopy(Blocks.DRAGON_EGG)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.sound(SoundType.BONE_BLOCK)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = this.create("grotto_block", GrottoBlock(BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.STONE)
|
||||
.strength(1.5F)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR = this.create("incubator", IncubatorBlock(BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_LIGHT_GRAY)
|
||||
.sound(SoundType.METAL)
|
||||
.strength(Blocks.COAL_BLOCK.defaultDestroyTime(), Blocks.DIAMOND_ORE.explosionResistance)
|
||||
.noOcclusion()
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.lightLevel { state ->
|
||||
if (state.getValue(IncubatorBlock.HAS_EGG)) 5 else 0 // Emit 5 light when HAS_EGG is true
|
||||
}
|
||||
))
|
||||
|
||||
init {
|
||||
/**
|
||||
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
|
||||
@@ -642,6 +761,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
return block
|
||||
}
|
||||
|
||||
private fun nestBlock(variant: NestBlock.NestVariant): NestBlock {
|
||||
val identifier = cobblemonResource(variant.serializedName)
|
||||
val block = NestBlock(variant, BlockBehaviour.Properties.of().noOcclusion().pushReaction(PushReaction.DESTROY))
|
||||
return this.create(identifier.path, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls helper method from Vanilla
|
||||
*/
|
||||
@@ -677,4 +802,26 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
private fun typeGemBlock(name: String): Block {
|
||||
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0f, 6.0f)))
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemBlocks[identifier] = typeGemBlock
|
||||
|
||||
return typeGemBlock
|
||||
}
|
||||
|
||||
fun typeGemBlocks() = typeGemBlocks.toMap()
|
||||
|
||||
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
|
||||
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0f, 3.0f).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemClusters[identifier] = gemClusterBlock
|
||||
|
||||
return gemClusterBlock
|
||||
}
|
||||
|
||||
fun typeGemClusters() = typeGemClusters.toMap()
|
||||
}
|
||||
@@ -60,6 +60,8 @@ object CobblemonCommands {
|
||||
RenameBoxCommand.register(dispatcher)
|
||||
ChangeBoxWallpaperCommand.register(dispatcher)
|
||||
RunMolangScriptCommand.register(dispatcher)
|
||||
GivePokemonEggCommand.register(dispatcher)
|
||||
GiveAllPokemonEggCommand.register(dispatcher)
|
||||
SpectateBattleCommand.register(dispatcher)
|
||||
CobblemonConfigCommand.register(dispatcher)
|
||||
UnlockPCBoxWallpaperCommand.register(dispatcher)
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
@@ -10,6 +10,16 @@ package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.PotComponent
|
||||
import com.cobblemon.mod.common.item.components.*
|
||||
import com.cobblemon.mod.common.item.components.BaitEffectsComponent
|
||||
import com.cobblemon.mod.common.item.components.FlavourComponent
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.HeldItemCapableComponent
|
||||
import com.cobblemon.mod.common.item.components.PokemonItemComponent
|
||||
import com.cobblemon.mod.common.item.components.RodBaitComponent
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.item.components.EggComponent
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.component.DataComponentType
|
||||
@@ -69,6 +79,17 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.persistent(MobEffectsComponent.CODEC)
|
||||
.networkSynchronized(MobEffectsComponent.PACKET_CODEC)
|
||||
.build())
|
||||
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
|
||||
.persistent(TMMoveComponent.CODEC)
|
||||
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
|
||||
.build())
|
||||
val EGG_DATA: DataComponentType<EggComponent> = create("egg", DataComponentType.builder<EggComponent>()
|
||||
.persistent(EggComponent.CODEC) // For saving/loading
|
||||
.networkSynchronized(EggComponent.PACKET_CODEC) // For network synchronization
|
||||
.build())
|
||||
|
||||
|
||||
|
||||
|
||||
fun register() {
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
|
||||
@@ -80,6 +101,8 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:egg_data"), EGG_DATA)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.NestBlock
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
@@ -49,13 +50,13 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.item.Item.Properties
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.food.FoodProperties
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.HangingSignItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.Item.Properties
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.Rarity
|
||||
@@ -467,6 +468,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
@JvmField
|
||||
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
|
||||
@JvmField
|
||||
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@@ -588,6 +593,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
|
||||
|
||||
val NEST_STICK = blockItem(NestBlock.NestVariant.STICK.serializedName, CobblemonBlocks.NEST_STICK)
|
||||
val NEST_LEAF = blockItem(NestBlock.NestVariant.LEAF.serializedName, CobblemonBlocks.NEST_LEAF)
|
||||
val NEST_STONE = blockItem(NestBlock.NestVariant.STONE.serializedName, CobblemonBlocks.NEST_STONE)
|
||||
val NEST_DEEPSLATE = blockItem(NestBlock.NestVariant.DEEPSLATE.serializedName, CobblemonBlocks.NEST_DEEPSLATE)
|
||||
val NEST_BASALT = blockItem(NestBlock.NestVariant.BASALT.serializedName, CobblemonBlocks.NEST_BASALT)
|
||||
val NEST_SNOW = blockItem(NestBlock.NestVariant.SNOW.serializedName, CobblemonBlocks.NEST_SNOW)
|
||||
|
||||
val NEST_NETHER = blockItem(NestBlock.NestVariant.NETHER.serializedName, CobblemonBlocks.NEST_NETHER)
|
||||
val NEST_SOUL_SAND = blockItem(NestBlock.NestVariant.SOUL_SAND.serializedName, CobblemonBlocks.NEST_SOUL_SAND)
|
||||
val NEST_WARM_OCEAN = blockItem(NestBlock.NestVariant.WARM_OCEAN.serializedName, CobblemonBlocks.NEST_WARM_OCEAN)
|
||||
val NEST_SEAGRASS = blockItem(NestBlock.NestVariant.SEAGRASS.serializedName, CobblemonBlocks.NEST_SEAGRASS)
|
||||
val NEST_LUSH = blockItem(NestBlock.NestVariant.LUSH.serializedName, CobblemonBlocks.NEST_LUSH)
|
||||
val NEST_HAY = blockItem(NestBlock.NestVariant.HAY.serializedName, CobblemonBlocks.NEST_HAY)
|
||||
val NEST_SAND = blockItem(NestBlock.NestVariant.SAND.serializedName, CobblemonBlocks.NEST_SAND)
|
||||
val NEST_RED_SAND = blockItem(NestBlock.NestVariant.RED_SAND.serializedName, CobblemonBlocks.NEST_RED_SAND)
|
||||
val NEST_CHORUS = blockItem(NestBlock.NestVariant.CHORUS.serializedName, CobblemonBlocks.NEST_CHORUS)
|
||||
|
||||
val BASIC_NEST = blockItem(NestBlock.NestVariant.BASIC.serializedName, CobblemonBlocks.BASIC_NEST)
|
||||
val CAVE_NEST = blockItem(NestBlock.NestVariant.CAVE.serializedName, CobblemonBlocks.CAVE_NEST)
|
||||
val WATER_NEST = blockItem(NestBlock.NestVariant.WATER.serializedName, CobblemonBlocks.WATER_NEST)
|
||||
|
||||
@JvmField
|
||||
val INCUBATOR = blockItem("incubator", CobblemonBlocks.INCUBATOR)
|
||||
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = blockItem("grotto_block", CobblemonBlocks.GROTTO_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
|
||||
|
||||
@@ -917,7 +949,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
val DESTINY_KNOT = heldItem("destiny_knot", null, if (Cobblemon.config.canDestinyKnotBreak) Properties().durability(Cobblemon.config.destinyKnotDurability) else Properties())
|
||||
@JvmField
|
||||
val DRAGON_FANG = heldItem("dragon_fang")
|
||||
@JvmField
|
||||
@@ -1173,6 +1205,93 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = blockItem("type_gem_core", CobblemonBlocks.TYPE_GEM_CORE)
|
||||
/* @JvmField
|
||||
val TYPE_GEM_BLOCK = blockItem("type_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER = blockItem("type_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER)
|
||||
@JvmField
|
||||
val LARGE_TYPE_GEM = blockItem("large_type_gem", CobblemonBlocks.LARGE_TYPE_GEM)
|
||||
@JvmField
|
||||
val MEDIUM_TYPE_GEM = blockItem("medium_type_gem", CobblemonBlocks.MEDIUM_TYPE_GEM)
|
||||
@JvmField
|
||||
val SMALL_TYPE_GEM = blockItem("small_type_gem", CobblemonBlocks.SMALL_TYPE_GEM)*/
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = blockItem("type_gem_cluster_normal", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = blockItem("type_gem_cluster_fire", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = blockItem("type_gem_cluster_water", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("type_gem_cluster_electric", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = blockItem("type_gem_cluster_grass", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = blockItem("type_gem_cluster_ice", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("type_gem_cluster_fighting", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = blockItem("type_gem_cluster_poison", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = blockItem("type_gem_cluster_ground", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = blockItem("type_gem_cluster_flying", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("type_gem_cluster_psychic", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = blockItem("type_gem_cluster_bug", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = blockItem("type_gem_cluster_rock", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = blockItem("type_gem_cluster_ghost", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = blockItem("type_gem_cluster_dragon", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = blockItem("type_gem_cluster_dark", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = blockItem("type_gem_cluster_steel", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = blockItem("type_gem_cluster_fairy", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = blockItem("type_gem_block_normal", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = blockItem("type_gem_block_fire", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = blockItem("type_gem_block_water", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("type_gem_block_electric", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = blockItem("type_gem_block_grass", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = blockItem("type_gem_block_ice", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = blockItem("type_gem_block_fighting", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = blockItem("type_gem_block_poison", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = blockItem("type_gem_block_ground", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = blockItem("type_gem_block_flying", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("type_gem_block_psychic", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = blockItem("type_gem_block_bug", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = blockItem("type_gem_block_rock", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = blockItem("type_gem_block_ghost", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = blockItem("type_gem_block_dragon", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = blockItem("type_gem_block_dark", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = blockItem("type_gem_block_steel", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = blockItem("type_gem_block_fairy", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
|
||||
Component.translatable("item.minecraft.fishing_rod").blue(),
|
||||
@@ -1397,6 +1516,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
@JvmField
|
||||
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Item.Properties()))
|
||||
@JvmField
|
||||
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
@@ -1437,6 +1560,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
@JvmField
|
||||
val POKEMON_EGG = eggItem("egg", CobblemonBlocks.EGG, Item.Properties().stacksTo(1))
|
||||
|
||||
private fun eggItem(name: String, block: Block, settings: Item.Properties = Item.Properties()): BlockItem = this.create(name, BlockItem(block, settings))
|
||||
|
||||
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
|
||||
|
||||
@@ -1530,9 +1657,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return create(name, RegionalFoodItem(properties))
|
||||
}
|
||||
|
||||
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
|
||||
private fun heldItem(name: String, remappedName: String? = null, settings:Item.Properties = Item.Properties()): CobblemonItem = create(
|
||||
name,
|
||||
CobblemonItem(Item.Properties()).also {
|
||||
CobblemonItem(settings).also {
|
||||
if (remappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, remappedName)
|
||||
}
|
||||
|
||||
@@ -10,15 +10,18 @@ package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
object CobblemonMemories {
|
||||
val memories = mutableMapOf<String, MemoryModuleType<*>>()
|
||||
@@ -33,6 +36,11 @@ object CobblemonMemories {
|
||||
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
|
||||
val DIALOGUES = register<List<ActiveDialogue>>("npc_dialogues")
|
||||
val ACTIVE_ACTION_EFFECT = register<ActionEffectContext>("active_action_effect")
|
||||
val POTENTIAL_MATE = register<PokemonEntity>("potential_mate")
|
||||
val MATE_SCAN_COOLDOWN = register("mate_scan_cooldown", PrimitiveCodec.BOOL)
|
||||
val NEST_LOCATION = register<BlockPos>("nest_location")
|
||||
val PATERNAL_NEST_LOCATION = register<BlockPos>("paternal_nest")
|
||||
val IRRITATED_AT = register<UUID>("irritated_at")
|
||||
val POKEMON_FLYING = register("pokemon_flying", PrimitiveCodec.BOOL)
|
||||
val POKEMON_DROWSY = register("pokemon_drowsy", PrimitiveCodec.BOOL)
|
||||
val POKEMON_SLEEPING = register("pokemon_sleeping", PrimitiveCodec.BOOL)
|
||||
@@ -41,6 +49,16 @@ object CobblemonMemories {
|
||||
val TARGETED_BATTLE_POKEMON = register<UUID>("targeted_battle_pokemon")
|
||||
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
|
||||
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
|
||||
val EGG_THIEF = register<Player>("egg_thief") // Stores the player stealing the egg
|
||||
val EGG_LOCATION = register<BlockPos>("egg_location") // Stores the last known location of the egg
|
||||
val EGG_IN_NEST = register("egg_in_nest", PrimitiveCodec.BOOL)
|
||||
val READY_TO_GRAB_EGG = register("ready_to_grab_egg", PrimitiveCodec.BOOL)
|
||||
val HOLDING_EGG = register("holding_egg", PrimitiveCodec.BOOL)
|
||||
val EGG_SEARCH_COUNTDOWN = register ("egg_search_countdown", PrimitiveCodec.BOOL)
|
||||
val NESTING_MATERIAL_LOCATION = register<BlockPos>("nesting_material_location")
|
||||
val SENSED_NEST_MATERIAL = register<ItemEntity>("sensed_nest_material")
|
||||
val IDEAL_NEST_LOCATION = register<BlockPos>("ideal_nest_location")
|
||||
val READY_TO_BUILD_NEST = register("ready_to_build_nest", PrimitiveCodec.BOOL)
|
||||
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
|
||||
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
|
||||
@@ -10,9 +10,8 @@ package com.cobblemon.mod.common
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.block.tm.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.client.gui.screens.MenuScreens
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
@@ -22,6 +21,7 @@ import net.minecraft.world.inventory.MenuType
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
val TM_MACHINE = MenuType.register("tmm_screen", ::TMMScreenHandler)
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.CraftBlankTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.CraftTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
@@ -44,6 +46,8 @@ import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
|
||||
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
|
||||
import com.cobblemon.mod.common.client.net.storage.RemoveClientPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.SwapClientPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.party.InitializePartyHandler
|
||||
@@ -67,6 +71,7 @@ import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
@@ -108,9 +113,9 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
@@ -142,6 +147,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.breeding.TriggerEggHatchPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -150,6 +156,7 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMove
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.UpdateGrottoBlockPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
@@ -171,6 +178,12 @@ import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPa
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
@@ -188,7 +201,9 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.breeding.TriggerEggHatchHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -197,6 +212,7 @@ import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveS
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.UpdateGrottoBlockHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
|
||||
@@ -222,6 +238,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonToPartyHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonToPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
@@ -229,6 +251,7 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -385,6 +408,8 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BreedingRuleRegistrySyncPacket.ID, BreedingRuleRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(HabitatSyncPacket.ID, HabitatSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
@@ -560,9 +585,19 @@ object CobblemonNetwork {
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
// Breeding Packets
|
||||
list.add(PacketRegisterInfo(TriggerEggHatchPacket.ID, TriggerEggHatchPacket::decode, TriggerEggHatchHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
|
||||
// TMs
|
||||
list.add(PacketRegisterInfo(CraftBlankTMPacket.ID, CraftBlankTMPacket::decode, CraftBlankTMPacketHandler))
|
||||
list.add(PacketRegisterInfo(CraftTMPacket.ID, CraftTMPacket::decode, CraftTMPacketHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(UpdateGrottoBlockPacket.ID, UpdateGrottoBlockPacket::decode, UpdateGrottoBlockHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("incense_sweet")
|
||||
@JvmField
|
||||
val INCENSE_SWEET = create(INCENSE_SWEET_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.INCENSE_SWEET), 0, 1))
|
||||
@JvmField
|
||||
val GROTTO_BLOCK_KEY: ResourceKey<PoiType> = createKey("grotto_block")
|
||||
@JvmField
|
||||
val GROTTO_BLOCK = create(GROTTO_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.GROTTO_BLOCK), 0, 1))
|
||||
|
||||
|
||||
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
|
||||
@@ -25,6 +22,11 @@ object CobblemonSensors {
|
||||
|
||||
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
|
||||
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
|
||||
val MATE_SENSOR = register("mate_sensor", ::MateSensor)
|
||||
val NEST_SENSOR = register("nest_sensor", ::NestSensor)
|
||||
val EGG_SENSOR = register("egg_sensor", :: EggSensor)
|
||||
val NEST_MATERIAL_SENSOR = register("nest_material_sensor", :: NestMaterialSensor)
|
||||
val IDEAL_NEST_LOCATION_SENSOR = register("ideal_nest_location_sensor", ::IdealNestPlacementSensor)
|
||||
// val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
|
||||
val POKEMON_DROWSY = register("pokemon_drowsy", ::DrowsySensor)
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val POKEDEX_SCAN_ZOOM_INCREMENT = this.create("item.pokedex.scan_zoom_increment")
|
||||
|
||||
@JvmField
|
||||
val BREEDING_EGG_HATCH = this.create("breeding.egg_hatch")
|
||||
|
||||
@JvmField
|
||||
val ITEM_USE = this.create("item.use")
|
||||
@JvmField
|
||||
@@ -195,6 +198,18 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val TM_USE = this.create("tm.use")
|
||||
@JvmField
|
||||
val TMM_CRAFT = this.create("tmm.craft")
|
||||
@JvmField
|
||||
val TMM_CRAFT_BLANK = this.create("tmm.craft_blank")
|
||||
@JvmField
|
||||
val TMM_OFF = this.create("tmm.off")
|
||||
@JvmField
|
||||
val TMM_ON = this.create("tmm.on")
|
||||
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
|
||||
@JvmField
|
||||
|
||||
+3
-39
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Optional
|
||||
import java.util.*
|
||||
|
||||
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
|
||||
|
||||
@@ -29,42 +29,6 @@ class PlantTumblestoneCriterion(
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
return context.block.canGrow(context.pos, player.level())
|
||||
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
|
||||
}
|
||||
}
|
||||
|
||||
///**
|
||||
// * A criterion condition for when a tumblestone block is planted
|
||||
// * @param id The identifier of the criterion
|
||||
// * @param predicate The predicate for the criterion
|
||||
// *
|
||||
// * @author Aethen
|
||||
// * @since 02/28/2024
|
||||
// */
|
||||
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
// override fun toJson(json: JsonObject) {
|
||||
// // Add properties to json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// override fun fromJson(json: JsonObject) {
|
||||
// // Parse properties from json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks if the tumblestone block can grow at the given position
|
||||
// * @param player The player that planted the tumblestone block
|
||||
// * @param context The context of the criterion
|
||||
// * @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
// */
|
||||
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * The context of the [PlantTumblestoneCriterionCondition]
|
||||
// * @param pos The position of the tumblestone block
|
||||
// * @param tumbleStoneBlock The tumblestone block
|
||||
// */
|
||||
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.AttackEggThiefTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class AttackEggThiefTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(AttackEggThiefTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class BreedAtNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(BreedAtNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BuildNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class BuildNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(BuildNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GuardNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class GuardNestTaskConfig : SingleTaskConfig {
|
||||
// todo make new variables
|
||||
companion object {
|
||||
const val BREED = "breed"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(GuardNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.PATERNAL_NEST_LOCATION),
|
||||
it.registered(MemoryModuleType.NEAREST_VISIBLE_PLAYER), // if player is invisible they do not patrol
|
||||
it.registered(MemoryModuleType.ANGRY_AT),
|
||||
it.absent(MemoryModuleType.ATTACK_TARGET),
|
||||
it.registered(CobblemonMemories.IRRITATED_AT)
|
||||
).apply(it) { lookTarget, walkTarget, paternalNest, nearestVisiblePlayer, angryAt, attackTarget, irritatedAt ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (paternalNest.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val nestCenter = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// todo maybe cancel task if player is close to nest and set angry at task to that player
|
||||
val nearestPlayerOptional = entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER)
|
||||
val player = nearestPlayerOptional.orElse(null) as? ServerPlayer
|
||||
val irritatedAtTarget = entity.brain.getMemory(CobblemonMemories.IRRITATED_AT)
|
||||
|
||||
if (player != null) {
|
||||
val playerDistanceSquared = player.position().distanceToSqr(nestCenter)
|
||||
|
||||
if (!irritatedAtTarget.isPresent && playerDistanceSquared <= 15 * 15) {
|
||||
// Player is within 15 blocks, set angry and cry
|
||||
entity.cry()
|
||||
|
||||
// set irritated at target to player near egg
|
||||
irritatedAt.set(player.uuid)
|
||||
|
||||
// Display angry particles above parent
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
}
|
||||
|
||||
if (playerDistanceSquared <= 8 * 8) {
|
||||
// Player is within 8 blocks, attack
|
||||
entity.cry()
|
||||
|
||||
// Display angry particles above parent
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.ANGRY_VILLAGER,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
angryAt.set(player.uuid)
|
||||
attackTarget.set(player)
|
||||
return@Trigger false // Cancel the task
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Scan for a valid walkable position within an 8-block sphere
|
||||
val validPosition = findValidWalkablePosition(world, nestCenter, entity, 8)
|
||||
if (validPosition == null) {
|
||||
return@Trigger false // No valid position found
|
||||
}
|
||||
|
||||
// Set the entity's walk and look targets
|
||||
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findValidWalkablePosition(
|
||||
world: ServerLevel,
|
||||
center: Vec3,
|
||||
entity: PathfinderMob,
|
||||
radius: Int
|
||||
): Vec3? {
|
||||
val random = world.random
|
||||
|
||||
// Try a number of random positions within the radius
|
||||
repeat(20) { // Attempt up to 20 random positions
|
||||
val offsetX = random.nextDouble() * 2 * radius - radius
|
||||
val offsetY = random.nextDouble() * 2 * radius - radius
|
||||
val offsetZ = random.nextDouble() * 2 * radius - radius
|
||||
val candidate = center.add(offsetX, offsetY, offsetZ)
|
||||
|
||||
// Convert to block position
|
||||
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
|
||||
|
||||
// Check if the position is walkable and not inside a solid block
|
||||
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
|
||||
entity.navigation.isStableDestination(blockPos)
|
||||
) {
|
||||
return Vec3.atCenterOf(blockPos) // Return the valid position
|
||||
}
|
||||
}
|
||||
|
||||
return null // No valid position found
|
||||
}*/
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
class HuntPlayerTaskConfig : SingleTaskConfig {
|
||||
// todo make new variables
|
||||
companion object {
|
||||
const val BREED = "breed"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(MemoryModuleType.NEAREST_PLAYERS), // this will be null most of the time
|
||||
it.present(MemoryModuleType.ANGRY_AT),
|
||||
it.registered(MemoryModuleType.ATTACK_TARGET),
|
||||
it.registered(MemoryModuleType.DISTURBANCE_LOCATION),
|
||||
).apply(it) { lookTarget, walkTarget, nearestPlayers, angryAt, attackTarget, disturbanceLocation ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
val angryAtUuid = (angryAt.value() as? IdF<UUID?>)?.value() ?: return@Trigger false
|
||||
val disturbancePos = (disturbanceLocation.value() as? IdF<BlockPos>)?.value()
|
||||
|
||||
val players = (nearestPlayers.value() as? IdF<List<Player?>?>)?.value()
|
||||
val targetPlayer = players?.find { it?.uuid == angryAtUuid }
|
||||
|
||||
if (targetPlayer != null) {
|
||||
// If the angry player is nearby, set them as the attack target
|
||||
entity.cry()
|
||||
attackTarget.set(targetPlayer)
|
||||
return@Trigger true
|
||||
}
|
||||
|
||||
// If the angry player is not nearby, search around the Pokémon
|
||||
val maxTicksToSearch = 200L
|
||||
if (time > maxTicksToSearch) {
|
||||
// Timeout after 200 ticks
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// If no disturbance location or it is caused by a nearby player, skip
|
||||
if (disturbancePos != null) {
|
||||
val disturberCausingDisturbance = players?.any {
|
||||
it?.position()?.distanceToSqr(Vec3.atCenterOf(disturbancePos))!! <= 4.0 * 4.0
|
||||
}
|
||||
disturberCausingDisturbance?.let { it1 ->
|
||||
if (!it1) {
|
||||
// Pathfind toward disturbance location
|
||||
walkTarget.set(WalkTarget(Vec3.atCenterOf(disturbancePos), speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(Vec3.atCenterOf(disturbancePos)))
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no valid disturbance location, pathfind randomly within proximity
|
||||
val validPosition = findValidWalkablePosition(world, entity.position(), entity, 8)
|
||||
if (validPosition == null) {
|
||||
return@Trigger false // No valid position found
|
||||
}
|
||||
|
||||
walkTarget.set(WalkTarget(validPosition, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(validPosition.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findValidWalkablePosition(
|
||||
world: ServerLevel,
|
||||
center: Vec3,
|
||||
entity: PathfinderMob,
|
||||
radius: Int
|
||||
): Vec3? {
|
||||
val random = world.random
|
||||
|
||||
// Try a number of random positions within the radius
|
||||
repeat(20) { // Attempt up to 20 random positions
|
||||
val offsetX = random.nextDouble() * 2 * radius - radius
|
||||
val offsetY = random.nextDouble() * 2 * radius - radius
|
||||
val offsetZ = random.nextDouble() * 2 * radius - radius
|
||||
val candidate = center.add(offsetX, offsetY, offsetZ)
|
||||
|
||||
// Convert to block position
|
||||
val blockPos = BlockPos(candidate.x.toInt(), candidate.y.toInt(), candidate.z.toInt())
|
||||
|
||||
// Check if the position is walkable and not inside a solid block
|
||||
if (world.getBlockState(blockPos).isSolidRender(world, blockPos).not() &&
|
||||
entity.navigation.isStableDestination(blockPos)
|
||||
) {
|
||||
return Vec3.atCenterOf(blockPos) // Return the valid position
|
||||
}
|
||||
}
|
||||
|
||||
return null // No valid position found
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToIdealNestLocationTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToIdealNestLocationTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToIdealNestLocationTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestMaterialTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToNestMaterialTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToNestMaterialTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PathToNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpEggTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PickUpEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PickUpEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PickUpNestMaterialTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PickUpNestMaterialTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "breeds", true).asExpressible()
|
||||
val speedMultiplier = numberVariable(BREED, "speed_multiplier", 0.6).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
speedMultiplier
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PickUpNestMaterialTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.LOOK_TARGET), // todo This might need to be it.absent but only it.present works atm
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE)
|
||||
).apply(it) { lookTarget, walkTarget, nestMemory, mateMemory ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
// Don't do anything if the pokemon has a breeding cooldown
|
||||
if ((entity as PokemonEntity).pokemon.breedingCooldown != 0) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
//val nestLocation = it.get(nestMemory)
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
|
||||
//val mate = it.get(mateMemory) as? PokemonEntity ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// todo try to find a way to only have pairs of mates instead of an orgy
|
||||
|
||||
// Create a target vector at the nest location
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check if Pokémon are close enough to the nest
|
||||
if (entity.distanceToSqr(targetVec) <= 2.0 && mate.distanceToSqr(targetVec) <= 2.0) {
|
||||
return@Trigger false // Task ends when both are close enough
|
||||
}
|
||||
|
||||
*//*if ((entity as PokemonEntity).tethering.tetheringId != null) {
|
||||
val test1 = 1 // for breakpointing here to test my pastured mons
|
||||
}*//*
|
||||
|
||||
// try to get the main pokemon to path and look at the nest
|
||||
|
||||
walkTarget.set(WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
// try to find a way to get the mate to pathfind and look at the nest too...
|
||||
mate.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(targetVec, speedMultiplier.resolveFloat(), 1)
|
||||
)
|
||||
mate.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceEggInNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PlaceEggInNestTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(PlaceEggInNestTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.RetrieveEggTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class RetrieveEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(RetrieveEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.breeding.Egg
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.cobblemon.mod.common.breeding.BreedingLogicManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.BreedAtNestTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.SearchForEggTask
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.kinds.IdF
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SearchForEggTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val BREED = "breed_at_nest" // Category
|
||||
}
|
||||
|
||||
val condition = booleanVariable(BREED, "can_breed", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return WrapperLivingEntityTask(SearchForEggTask.create(), PokemonEntity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/*override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean() || entity !is PokemonEntity) return null
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEST_LOCATION),
|
||||
it.present(CobblemonMemories.POTENTIAL_MATE),
|
||||
it.absent(MemoryModuleType.WALK_TARGET)
|
||||
).apply(it) { nestMemory, mateMemory, walkTarget ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
|
||||
// Retrieve nest location and mate entity from memory
|
||||
val nestLocation: BlockPos = (nestMemory.value() as? IdF<BlockPos>)?.value() ?: return@Trigger false
|
||||
val mate = (mateMemory.value() as? IdF<PokemonEntity>)?.value() ?: return@Trigger false
|
||||
|
||||
// Convert nest location to Vec3
|
||||
val targetVec = Vec3.atCenterOf(nestLocation)
|
||||
|
||||
// Check proximity to the nest
|
||||
if (entity.distanceToSqr(targetVec) > 2.0 && mate.distanceToSqr(targetVec) > 2.0) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
// Access the nest block entity
|
||||
val nestBlockEntity = world.getBlockEntity(nestLocation) as? NestBlockEntity
|
||||
?: return@Trigger false
|
||||
|
||||
// double check to make sure the nest is not taken yet and cancel if it is
|
||||
if (nestBlockEntity.getItem(0) != ItemStack.EMPTY) return@Trigger false
|
||||
|
||||
// Generate the egg using BreedingLogicManager
|
||||
val parentOne = (entity as PokemonEntity).pokemon
|
||||
val parentTwo = mate.pokemon
|
||||
val breedingResult = BreedingLogicManager.breed(parentOne, parentTwo)
|
||||
val form = BreedingLogicManager.calculateForm(parentOne, parentTwo)
|
||||
|
||||
// Ensure the breeding result is valid
|
||||
val egg = breedingResult.pokemon?.let { result ->
|
||||
Egg(
|
||||
species = result.species.resourceIdentifier,
|
||||
parentOne = parentOne,
|
||||
parentTwo = parentTwo,
|
||||
form = form.name.lowercase(),
|
||||
timeToHatch = 6000F
|
||||
)
|
||||
} ?: return@Trigger false
|
||||
|
||||
// Set the egg in the nest
|
||||
nestBlockEntity.setItem(egg.createEggItemStack())
|
||||
|
||||
// set the parents of the nest if it/they are wild so they guard it
|
||||
if (nestBlockEntity.ownerUUID == null)
|
||||
nestBlockEntity.parents = Pair((entity as PokemonEntity).uuid, mate.uuid)
|
||||
|
||||
|
||||
// set breeding cooldown for both
|
||||
parentOne.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentOne)
|
||||
parentTwo.breedingCooldown = BreedingLogicManager.resetBreedingCooldown(parentTwo)
|
||||
|
||||
// Display heart particles above parents
|
||||
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
entity.getRandomX(1.0), // Particle X position with slight randomness
|
||||
entity.getRandomY() + 0.5, // Slightly raised Y position
|
||||
entity.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * entity.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * entity.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * entity.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
(world as net.minecraft.server.level.ServerLevel).sendParticles(
|
||||
net.minecraft.core.particles.ParticleTypes.HEART,
|
||||
mate.getRandomX(1.0), // Particle X position with slight randomness
|
||||
mate.getRandomY() + 0.5, // Slightly raised Y position
|
||||
mate.getRandomZ(1.0), // Particle Z position with slight randomness
|
||||
4 + kotlin.random.Random.nextInt(2), // Particle count
|
||||
0.2 * mate.random.nextGaussian(), // X velocity with slight variance
|
||||
0.1 * mate.random.nextGaussian(), // Y velocity with upwards variance
|
||||
0.2 * mate.random.nextGaussian(), // Z velocity with slight variance
|
||||
0.02 // Particle speed
|
||||
)
|
||||
|
||||
// Notify nearby players
|
||||
world.players().forEach { player ->
|
||||
player.displayClientMessage("An egg has been laid in the nest!".green(), true)
|
||||
}
|
||||
|
||||
// todo maybe have them fall asleep after?
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -94,9 +94,22 @@ interface TaskConfig {
|
||||
cobblemonResource("point_to_spawn") to PointToSpawnTaskConfig::class.java,
|
||||
cobblemonResource("eat_grass") to EatGrassTaskConfig::class.java,
|
||||
cobblemonResource("find_air") to FindAirTaskConfig::class.java,
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("path_to_nest") to PathToNestTaskConfig::class.java,
|
||||
cobblemonResource("path_to_nest_material") to PathToNestMaterialTaskConfig::class.java,
|
||||
cobblemonResource("breed_at_nest") to BreedAtNestTaskConfig::class.java,
|
||||
cobblemonResource("guard_nest") to GuardNestTaskConfig::class.java,
|
||||
cobblemonResource("search_for_egg") to SearchForEggTaskConfig::class.java,
|
||||
cobblemonResource("attack_egg_thief") to AttackEggThiefTaskConfig::class.java,
|
||||
cobblemonResource("retrieve_egg") to RetrieveEggTaskConfig::class.java,
|
||||
cobblemonResource("pick_up_egg") to PickUpEggTaskConfig::class.java,
|
||||
cobblemonResource("pick_up_nest_material") to PickUpNestMaterialTaskConfig::class.java,
|
||||
cobblemonResource("path_to_ideal_nest_location") to PathToIdealNestLocationTaskConfig::class.java,
|
||||
cobblemonResource("place_egg_in_nest") to PlaceEggInNestTaskConfig::class.java,
|
||||
cobblemonResource("build_nest") to BuildNestTaskConfig::class.java,
|
||||
cobblemonResource("hunt_player") to HuntPlayerTaskConfig::class.java,
|
||||
)
|
||||
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.breeding
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.Optional
|
||||
|
||||
data class BreedingRule(
|
||||
val parent1rules: ParentRules,
|
||||
val parent2rules: ParentRules,
|
||||
|
||||
// The resulting baby data (all strings except hasItem will appear in ParentRules)
|
||||
val species: String?,
|
||||
val form: String?,
|
||||
val nature: String?,
|
||||
val ability: String?,
|
||||
|
||||
val moves: Moves,
|
||||
val ivs: Ivs,
|
||||
val gender: String?,
|
||||
|
||||
// If you want a separate pokeball string field:
|
||||
val pokeball: String?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BreedingRule> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
// Parent rules
|
||||
ParentRules.CODEC.fieldOf("parent1rules")
|
||||
.forGetter<BreedingRule> { it.parent1rules },
|
||||
|
||||
ParentRules.CODEC.fieldOf("parent2rules")
|
||||
.forGetter<BreedingRule> { it.parent2rules },
|
||||
|
||||
// ---- All optional string fields for the baby data ----
|
||||
Codec.STRING.optionalFieldOf("species")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.species },
|
||||
|
||||
Codec.STRING.optionalFieldOf("form")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.form },
|
||||
|
||||
Codec.STRING.optionalFieldOf("nature")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.nature },
|
||||
|
||||
Codec.STRING.optionalFieldOf("ability")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.ability },
|
||||
|
||||
// Moves & IVs can be empty objects but must exist in the JSON
|
||||
Moves.CODEC.fieldOf("moves")
|
||||
.forGetter<BreedingRule> { it.moves },
|
||||
|
||||
Ivs.CODEC.fieldOf("ivs")
|
||||
.forGetter<BreedingRule> { it.ivs },
|
||||
|
||||
Codec.STRING.optionalFieldOf("gender")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.gender },
|
||||
|
||||
// If you want pokeball to be a string instead of ResourceLocation
|
||||
Codec.STRING.optionalFieldOf("pokeball")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<BreedingRule> { it.pokeball }
|
||||
|
||||
).apply(builder, ::BreedingRule)
|
||||
}
|
||||
}
|
||||
|
||||
data class ParentRules(
|
||||
val species: String?,
|
||||
val form: String?,
|
||||
val ability: String?,
|
||||
val gender: String?,
|
||||
|
||||
// hasMove is also just a string now
|
||||
val hasMove: String?,
|
||||
|
||||
// hasItem stays as a ResourceLocation?
|
||||
val hasItem: ResourceLocation?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<ParentRules> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.optionalFieldOf("species")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.species },
|
||||
|
||||
Codec.STRING.optionalFieldOf("form")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.form },
|
||||
|
||||
Codec.STRING.optionalFieldOf("ability")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.ability },
|
||||
|
||||
Codec.STRING.optionalFieldOf("gender")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.gender },
|
||||
|
||||
Codec.STRING.optionalFieldOf("hasMove")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.hasMove },
|
||||
|
||||
// Keep hasItem as a ResourceLocation
|
||||
ResourceLocation.CODEC.optionalFieldOf("hasItem")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<ParentRules> { it.hasItem }
|
||||
|
||||
).apply(builder, ::ParentRules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Moves(
|
||||
val move1: String?,
|
||||
val move2: String?,
|
||||
val move3: String?,
|
||||
val move4: String?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Moves> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.STRING.optionalFieldOf("move1")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move1 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move2")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move2 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move3")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move3 },
|
||||
|
||||
Codec.STRING.optionalFieldOf("move4")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Moves> { it.move4 }
|
||||
|
||||
).apply(builder, ::Moves)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Ivs(
|
||||
val hp: Int?,
|
||||
val attack: Int?,
|
||||
val defence: Int?,
|
||||
val special_attack: Int?,
|
||||
val special_defence: Int?,
|
||||
val speed: Int?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Ivs> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.INT.optionalFieldOf("hp")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.hp },
|
||||
|
||||
Codec.INT.optionalFieldOf("attack")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.attack },
|
||||
|
||||
Codec.INT.optionalFieldOf("defence")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.defence },
|
||||
|
||||
Codec.INT.optionalFieldOf("special_attack")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.special_attack },
|
||||
|
||||
Codec.INT.optionalFieldOf("special_defence")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.special_defence },
|
||||
|
||||
Codec.INT.optionalFieldOf("speed")
|
||||
.xmap({ it.orElse(null) }, { Optional.ofNullable(it) })
|
||||
.forGetter<Ivs> { it.speed }
|
||||
).apply(builder, ::Ivs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.breeding
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.breeding.BreedingRuleRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object BreedingRules : JsonDataRegistry<BreedingRule> {
|
||||
override val id = cobblemonResource("breeding_rules")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<BreedingRules>()
|
||||
override val typeToken: TypeToken<BreedingRule> = TypeToken.get(BreedingRule::class.java)
|
||||
override val resourcePath = "breeding_rules"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val registry = mutableMapOf<ResourceLocation, BreedingRule>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
// Send the entire list of BreedingRule entries to the player
|
||||
BreedingRuleRegistrySyncPacket(this.registry.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, BreedingRule>) {
|
||||
// Clear existing data and load new data from JSON
|
||||
registry.clear()
|
||||
data.forEach { (id, breedingRule) ->
|
||||
registry[id] = breedingRule
|
||||
}
|
||||
println("Loaded Breeding Rules: ${registry.size}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a single BreedingRule that matches the given mother/father conditions.
|
||||
* Returns the first matching BreedingRule, or null if no match is found.
|
||||
*/
|
||||
fun getBreedingRule(mother: Pokemon, father: Pokemon): BreedingRule? {
|
||||
// 1. Prepare the actual ParentRules objects for mother/father.
|
||||
val motherRules = createParentRules(mother)
|
||||
val fatherRules = createParentRules(father)
|
||||
|
||||
// 2. Find all matching breeding rules in the registry.
|
||||
// A rule matches if parent1rules & parent2rules match the mother/father conditions.
|
||||
// If multiple rules match, we just return the first (or you could randomize).
|
||||
val matchingRules = registry.values.firstOrNull { rule ->
|
||||
rule.species != null && (
|
||||
(checkParentRules(rule.parent1rules, motherRules) && checkParentRules(rule.parent2rules, fatherRules)) ||
|
||||
(checkParentRules(rule.parent1rules, fatherRules) && checkParentRules(rule.parent2rules, motherRules))
|
||||
)
|
||||
}
|
||||
|
||||
// 3. If none match, return null. Otherwise return the first match.
|
||||
return matchingRules
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a BreedingRule.ParentRules object from an actual Pokémon instance.
|
||||
* Adjust as needed if you'd like more robust checking (e.g. multiple moves, alt forms, etc.).
|
||||
*/
|
||||
private fun createParentRules(parent: Pokemon): BreedingRule.ParentRules {
|
||||
// NOTE: Adjust these calls if your Pokemon class has different ways to get form/ability
|
||||
val speciesId = parent.species.name.lowercase()
|
||||
val formId = parent.form.name.lowercase() // If you store form as a ResourceLocation
|
||||
val abilityId = parent.ability.name.lowercase() // If you store ability as a ResourceLocation
|
||||
|
||||
return BreedingRule.ParentRules(
|
||||
species = speciesId,
|
||||
form = formId,
|
||||
ability = abilityId,
|
||||
gender = parent.gender.name.lowercase(),
|
||||
hasMove = parent.moveSet.getMoves().firstOrNull()?.template?.name,
|
||||
hasItem = parent.heldItemNoCopy().item.let {
|
||||
BuiltInRegistries.ITEM.getKey(it) // If empty, might be minecraft:air
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a parent's actual rules match the expected parent rules from the data.
|
||||
*/
|
||||
private fun checkParentRules(
|
||||
expected: BreedingRule.ParentRules,
|
||||
actual: BreedingRule.ParentRules
|
||||
): Boolean {
|
||||
return (expected.species == null || expected.species == actual.species) &&
|
||||
(expected.form == null || expected.form == actual.form) &&
|
||||
(expected.ability == null || expected.ability == actual.ability) &&
|
||||
(expected.gender == null || expected.gender == actual.gender) &&
|
||||
(expected.hasMove == null || expected.hasMove == actual.hasMove) &&
|
||||
(expected.hasItem == null || expected.hasItem == actual.hasItem)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingEligibilityEvent
|
||||
import com.cobblemon.mod.common.api.events.breeding.BreedingResultEvent
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
@@ -271,6 +273,12 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
|
||||
|
||||
//Breeding
|
||||
@JvmField
|
||||
val BREEDING_RESULT = EventObservable<BreedingResultEvent>()
|
||||
@JvmField
|
||||
val BREEDING_ELIGIBLE = EventObservable<BreedingEligibilityEvent>()
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
//
|
||||
// Riding
|
||||
|
||||
+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,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
|
||||
class TypeReturnIcon(
|
||||
val x: Number,
|
||||
val y: Number,
|
||||
val centeredX: Boolean = false,
|
||||
val small: Boolean = false,
|
||||
val secondaryOffset: Float = 15F,
|
||||
val doubleCenteredOffset: Float = 7.5F,
|
||||
val opacity: Float = 1F
|
||||
) {
|
||||
companion object {
|
||||
private const val TYPE_ICON_DIAMETER = 36
|
||||
private const val SCALE = 0.5F
|
||||
|
||||
private val typeReturnResource = cobblemonResource("textures/gui/type_return.png")
|
||||
private val smallTypeReturnResource = cobblemonResource("textures/gui/type_return_small.png")
|
||||
}
|
||||
|
||||
fun render(context: GuiGraphics) {
|
||||
val diameter = if (small) (TYPE_ICON_DIAMETER / 2) else TYPE_ICON_DIAMETER
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (small) smallTypeReturnResource else typeReturnResource,
|
||||
x = (x.toFloat()) / SCALE - 9,
|
||||
y = y.toFloat() / SCALE,
|
||||
height = diameter,
|
||||
width = diameter,
|
||||
alpha = opacity,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
data class Habitat(
|
||||
val structure: ResourceLocation,
|
||||
val spawnPool: List<WeightedGroup>
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Habitat> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("structure").forGetter { it.structure },
|
||||
WeightedGroup.CODEC.listOf().fieldOf("spawnPool").forGetter { it.spawnPool }
|
||||
).apply(instance, ::Habitat)
|
||||
}
|
||||
|
||||
val STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC)
|
||||
|
||||
val BLANK_HABITAT = Habitat(
|
||||
structure = cobblemonResource("blank"),
|
||||
spawnPool = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
fun flatten(): List<ResourceLocation> {
|
||||
return spawnPool.flatMap { it.spawns.map { s -> s.species } }
|
||||
}
|
||||
}
|
||||
|
||||
data class WeightedGroup(
|
||||
val weight: Int = 1,
|
||||
val spawns: List<WeightedSpecies>
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<WeightedGroup> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight },
|
||||
WeightedSpecies.CODEC.listOf().fieldOf("spawns").forGetter { it.spawns }
|
||||
).apply(instance, ::WeightedGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WeightedSpecies(
|
||||
val species: ResourceLocation,
|
||||
val weight: Int = 1
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<WeightedSpecies> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("species").forGetter { it.species },
|
||||
Codec.INT.optionalFieldOf("weight", 1).forGetter { it.weight }
|
||||
).apply(instance, ::WeightedSpecies)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.HabitatSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object Habitats : JsonDataRegistry<Habitat> {
|
||||
override val id = cobblemonResource("habitats")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<Habitats>()
|
||||
override val typeToken: TypeToken<Habitat> = TypeToken.get(Habitat::class.java)
|
||||
override val resourcePath = "habitats"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val table = mutableMapOf<ResourceLocation, Habitat>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
HabitatSyncPacket(table.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Habitat>) {
|
||||
table.clear()
|
||||
table.putAll(data)
|
||||
|
||||
println("[Habitats] Reloaded ${table.size} habitat(s):")
|
||||
for ((structureId, habitat) in table) {
|
||||
println(" - ${structureId}:")
|
||||
if (habitat.spawnPool.isEmpty()) {
|
||||
println(" (No spawn entries)")
|
||||
} else {
|
||||
habitat.spawnPool.forEachIndexed { i, group ->
|
||||
println(" • Group $i (weight: ${group.weight}):")
|
||||
group.spawns.forEach { entry ->
|
||||
println(" - ${entry.species} (weight: ${entry.weight})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getHabitat(structureId: ResourceLocation): Habitat? {
|
||||
return table[structureId]
|
||||
}
|
||||
|
||||
fun getSpawnOptions(structureId: ResourceLocation): List<ResourceLocation> {
|
||||
return table[structureId]?.flatten() ?: emptyList()
|
||||
}
|
||||
}
|
||||
+1
-7
@@ -92,10 +92,4 @@ class NPCProvider(npc: NPCEntity): EntityProvider {
|
||||
|
||||
interface EntityProvider {
|
||||
val entities: List<Entity>
|
||||
}
|
||||
|
||||
//class MoveAnimationKeyframe(
|
||||
// val sound: ResourceLocation? = null,
|
||||
// val animation: List<AnimationWithMoments> = listOf(),
|
||||
// val effects: List<ActionEffect> = listOf()
|
||||
//)
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.nest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
|
||||
import com.cobblemon.mod.common.block.NestBlock
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.BiomeTags
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
|
||||
/**
|
||||
* Contains the different Nest variants and util methods to get their item, block, biome, and materials from.
|
||||
*
|
||||
* @author Plastered_Crab
|
||||
* @since Feb 7th, 2025
|
||||
*/
|
||||
enum class Nest : StringRepresentable {
|
||||
|
||||
STICK,
|
||||
LEAF,
|
||||
STONE,
|
||||
DEEPSLATE,
|
||||
SNOW,
|
||||
LUSH,
|
||||
SEAGRASS,
|
||||
WARM_OCEAN,
|
||||
SAND,
|
||||
RED_SAND,
|
||||
NETHER,
|
||||
BASALT,
|
||||
SOUL_SAND,
|
||||
CHORUS;
|
||||
//HAY;
|
||||
|
||||
// Crab Note: todo maybe we want these to be a List of BlockItems?
|
||||
fun item(): BlockItem = when(this) {
|
||||
STICK -> CobblemonItems.NEST_STICK
|
||||
LEAF -> CobblemonItems.NEST_LEAF
|
||||
STONE -> CobblemonItems.NEST_STONE
|
||||
DEEPSLATE -> CobblemonItems.NEST_DEEPSLATE
|
||||
SNOW -> CobblemonItems.NEST_SNOW
|
||||
LUSH -> CobblemonItems.NEST_LUSH
|
||||
SEAGRASS -> CobblemonItems.NEST_SEAGRASS
|
||||
WARM_OCEAN -> CobblemonItems.NEST_WARM_OCEAN
|
||||
SAND -> CobblemonItems.NEST_SAND
|
||||
RED_SAND -> CobblemonItems.NEST_RED_SAND
|
||||
NETHER -> CobblemonItems.NEST_NETHER
|
||||
BASALT -> CobblemonItems.NEST_BASALT
|
||||
SOUL_SAND -> CobblemonItems.NEST_SOUL_SAND
|
||||
CHORUS -> CobblemonItems.NEST_CHORUS
|
||||
//HAY -> CobblemonItems.NEST_HAY
|
||||
}
|
||||
|
||||
// todo make a map of ideal placement blocks for each nest
|
||||
fun placementBlocks(): List<Block> = when (this) {
|
||||
STICK -> listOf(Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.PODZOL, Blocks.COARSE_DIRT)
|
||||
LEAF -> listOf(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.BIRCH_LEAVES, Blocks.ACACIA_LEAVES, Blocks.AZALEA_LEAVES, Blocks.MANGROVE_LEAVES, Blocks.CHERRY_LEAVES)
|
||||
STONE -> listOf(Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.GRANITE, Blocks.DRIPSTONE_BLOCK, Blocks.COBBLESTONE, Blocks.ICE, Blocks.PACKED_ICE, Blocks.BLUE_ICE)
|
||||
DEEPSLATE -> listOf(Blocks.DEEPSLATE, Blocks.COBBLED_DEEPSLATE)
|
||||
SNOW -> listOf(Blocks.SNOW_BLOCK, Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.COARSE_DIRT) // todo dirt, grass, and coarse dirt ONLY if replacing the location of snowlayer
|
||||
LUSH -> listOf(Blocks.MOSS_BLOCK, Blocks.CLAY)
|
||||
SEAGRASS -> listOf(Blocks.DIRT, Blocks.STONE, Blocks.ANDESITE, Blocks.DIORITE, Blocks.MUD, Blocks.CLAY, Blocks.ANDESITE, Blocks.SAND, Blocks.GRAVEL) // todo (all only if surrounded by water AND not in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
|
||||
WARM_OCEAN -> listOf(Blocks.SAND, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.FIRE_CORAL_BLOCK, Blocks.HORN_CORAL_BLOCK, Blocks.TUBE_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BUBBLE_CORAL_BLOCK) // todo (all only if surrounded by water AND only when in any of: Warm Ocean, Lukewarm Ocean, or Deep Lukewarm Ocean biomes)
|
||||
SAND -> listOf(Blocks.SAND, Blocks.SANDSTONE, Blocks.SMOOTH_SANDSTONE) // todo (only if NOT surrounded by water)
|
||||
RED_SAND -> listOf(Blocks.RED_SAND, Blocks.RED_SANDSTONE, Blocks.SMOOTH_RED_SANDSTONE)
|
||||
NETHER -> listOf(Blocks.NETHERRACK, Blocks.WARPED_NYLIUM, Blocks.CRIMSON_NYLIUM, Blocks.NETHER_WART_BLOCK, Blocks.WARPED_WART_BLOCK, Blocks.MAGMA_BLOCK)
|
||||
BASALT -> listOf(Blocks.BASALT, Blocks.BLACKSTONE, Blocks.SMOOTH_BASALT)
|
||||
SOUL_SAND -> listOf(Blocks.SOUL_SAND, Blocks.SOUL_SOIL)
|
||||
CHORUS -> listOf(Blocks.END_STONE, Blocks.PURPUR_BLOCK, Blocks.END_STONE_BRICKS)
|
||||
//HAY -> listOf(Blocks.HAY_BLOCK, Blocks.GRASS_BLOCK)
|
||||
}
|
||||
|
||||
// todo make a map or something for each nest that can be waterlogged and not
|
||||
/**
|
||||
* Determines if a nest can be waterlogged.
|
||||
* Some nests, like those made of water-related materials, can be placed in water.
|
||||
*/
|
||||
fun isWaterloggable(): Boolean = when (this) {
|
||||
SEAGRASS, WARM_OCEAN -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this nest type can be placed based on **biome tags**.
|
||||
*/
|
||||
fun isValidBiome(world: ServerLevel, pos: BlockPos): Boolean {
|
||||
val biomeHolder: Holder<Biome> = world.getBiome(pos)
|
||||
|
||||
return when (this) {
|
||||
STICK -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STICK_NEST)
|
||||
LEAF -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LEAF_NEST_TREES)
|
||||
STONE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_STONE_NEST)
|
||||
DEEPSLATE -> biomeHolder.`is`(CobblemonBiomeTags.HAS_DEEPSLATE_NEST_CAVES)
|
||||
SNOW -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SNOW_NEST)
|
||||
LUSH -> biomeHolder.`is`(CobblemonBiomeTags.HAS_LUSH_NEST_CAVES)
|
||||
SEAGRASS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SEAGRASS_NEST)
|
||||
WARM_OCEAN -> biomeHolder.`is`(CobblemonBiomeTags.HAS_WARM_OCEAN_NEST)
|
||||
SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SAND_NEST)
|
||||
RED_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_RED_SAND_NEST)
|
||||
NETHER -> biomeHolder.`is`(CobblemonBiomeTags.HAS_NETHER_NEST)
|
||||
BASALT -> biomeHolder.`is`(CobblemonBiomeTags.HAS_BASALT_NEST)
|
||||
SOUL_SAND -> biomeHolder.`is`(CobblemonBiomeTags.HAS_SOUL_SAND_NEST)
|
||||
CHORUS -> biomeHolder.`is`(CobblemonBiomeTags.HAS_CHORUS_NEST)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Crab Note: todo maybe we want these to be a List of Items?
|
||||
fun materials(): Item = when(this) {
|
||||
STICK -> Items.STICK
|
||||
LEAF -> Items.OAK_SAPLING
|
||||
STONE -> Items.COBBLESTONE
|
||||
DEEPSLATE -> Items.COBBLED_DEEPSLATE
|
||||
SNOW -> Items.SNOWBALL
|
||||
LUSH -> Items.MOSS_BLOCK
|
||||
SEAGRASS -> Items.KELP
|
||||
WARM_OCEAN -> Items.SEA_PICKLE
|
||||
SAND -> Items.SAND
|
||||
RED_SAND -> Items.RED_SAND
|
||||
NETHER -> Items.WARPED_ROOTS
|
||||
BASALT -> Items.BASALT
|
||||
SOUL_SAND -> Items.SOUL_SAND
|
||||
CHORUS -> Items.CHORUS_FRUIT
|
||||
//HAY -> Items.WHEAT
|
||||
}
|
||||
|
||||
// Crab Note: todo maybe we want to redo this if we have lists of items needed for each?
|
||||
/*fun craftedNestResult(itemStack: ItemStack): BlockItem? = when(itemStack) {
|
||||
ItemStack(Items.STICK, 4) -> CobblemonItems.NEST_STICK
|
||||
ItemStack(Items.OAK_SAPLING, 4) -> CobblemonItems.NEST_LEAF
|
||||
ItemStack(Items.COBBLESTONE, 4) -> CobblemonItems.NEST_STONE
|
||||
ItemStack(Items.COBBLED_DEEPSLATE, 4) -> CobblemonItems.NEST_DEEPSLATE
|
||||
ItemStack(Items.SNOWBALL, 4) -> CobblemonItems.NEST_SNOW
|
||||
ItemStack(Items.MOSS_BLOCK, 4) -> CobblemonItems.NEST_LUSH
|
||||
ItemStack(Items.KELP, 4) -> CobblemonItems.NEST_SEAGRASS
|
||||
ItemStack(Items.SEA_PICKLE, 4) -> CobblemonItems.NEST_WARM_OCEAN
|
||||
ItemStack(Items.SAND, 4) -> CobblemonItems.NEST_SAND
|
||||
ItemStack(Items.RED_SAND, 4) -> CobblemonItems.NEST_RED_SAND
|
||||
ItemStack(Items.WARPED_ROOTS, 4) -> CobblemonItems.NEST_NETHER
|
||||
ItemStack(Items.BASALT, 4) -> CobblemonItems.NEST_BASALT
|
||||
ItemStack(Items.SOUL_SAND, 4) -> CobblemonItems.NEST_SOUL_SAND
|
||||
ItemStack(Items.CHORUS_FRUIT, 4) -> CobblemonItems.NEST_CHORUS
|
||||
ItemStack(Items.WHEAT, 4) -> CobblemonItems.NEST_HAY
|
||||
else -> null
|
||||
}*/
|
||||
|
||||
override fun getSerializedName() = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<Nest> = StringRepresentable.fromValues(::values)
|
||||
|
||||
private val materialRequirements: Map<Item, Int> = mapOf(
|
||||
Items.STICK to 2,
|
||||
Items.OAK_SAPLING to 2,
|
||||
Items.COBBLESTONE to 2,
|
||||
Items.COBBLED_DEEPSLATE to 2,
|
||||
Items.SNOWBALL to 2,
|
||||
Items.MOSS_BLOCK to 2,
|
||||
Items.KELP to 2,
|
||||
Items.SEA_PICKLE to 2,
|
||||
Items.SAND to 2,
|
||||
Items.RED_SAND to 2,
|
||||
Items.WARPED_ROOTS to 2,
|
||||
Items.BASALT to 2,
|
||||
Items.SOUL_SAND to 2,
|
||||
Items.CHORUS_FRUIT to 2,
|
||||
//Items.WHEAT to 2
|
||||
)
|
||||
|
||||
/**
|
||||
* Determines if a held stack is sufficient to craft a nest and returns the crafted item and leftover count.
|
||||
* @param heldStack The item stack in the Pokémon's hand.
|
||||
* @return A Pair of (crafted nest block item, remaining materials in hand) or null if crafting is not possible.
|
||||
*/
|
||||
fun craftedNestResult(heldStack: ItemStack): Pair<BlockItem, Int>? {
|
||||
val requiredAmount = materialRequirements[heldStack.item] ?: return null // Check if item is a valid material
|
||||
|
||||
// Ensure Pokémon has enough materials
|
||||
if (heldStack.count >= requiredAmount) {
|
||||
val remaining = heldStack.count - requiredAmount
|
||||
|
||||
// Find the nest type that matches the material
|
||||
val matchingNest = entries.firstOrNull { it.materials() == heldStack.item } ?: return null
|
||||
|
||||
return Pair(matchingNest.item(), remaining)
|
||||
}
|
||||
|
||||
return null // Not enough materials
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
|
||||
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
|
||||
@@ -59,7 +59,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
//.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
|
||||
.registerTypeAdapter(NPCInteractConfiguration::class.java, NPCInteractConfigurationAdapter)
|
||||
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
|
||||
|
||||
@@ -31,6 +31,9 @@ object CobblemonPermissions {
|
||||
val GIVE_POKEMON_SELF = this.create("${GIVE_POKEMON_BASE}.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val GIVE_POKEMON_OTHER = this.create("${GIVE_POKEMON_BASE}.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
val GIVE_POKEMON_EGG = this.create("${COMMAND_PREFIX}givepokemonegg", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
private const val HEAL_POKEMON_BASE = "${COMMAND_PREFIX}healpokemon"
|
||||
@JvmStatic
|
||||
val HEAL_POKEMON_SELF = this.create("$HEAL_POKEMON_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokedex
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -21,6 +24,7 @@ import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.UUID
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
class PokedexManager(
|
||||
override val uuid: UUID,
|
||||
|
||||
@@ -110,7 +110,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
@@ -187,6 +187,22 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
*/
|
||||
fun getByIdentifier(identifier: ResourceLocation) = this.speciesByIdentifier[identifier]
|
||||
|
||||
/**
|
||||
* Finds the first [Species] in an evolution line
|
||||
*
|
||||
* @param species The [Species] evolution line to use
|
||||
* @return The [Species] if existing.
|
||||
*/
|
||||
fun getFirstEvolution(species: Species): Species {
|
||||
var returnSpecies = species
|
||||
|
||||
while (returnSpecies.preEvolution != null) {
|
||||
returnSpecies = returnSpecies.preEvolution!!.species
|
||||
}
|
||||
|
||||
return returnSpecies
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the currently loaded species.
|
||||
*
|
||||
|
||||
+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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
@@ -20,7 +21,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
class Interpreter(
|
||||
val loadMove: (JsonElement, Learnset) -> Boolean
|
||||
val loadMove: (JsonElement, Learnset) -> Boolean
|
||||
) {
|
||||
companion object {
|
||||
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
|
||||
@@ -28,10 +29,10 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
val str = element.takeIf { it.isJsonPrimitive }?.asString ?: return@Interpreter false
|
||||
if (str.startsWith(prefix)) {
|
||||
Moves.getByName(str.substringAfter(":"))
|
||||
?.let {
|
||||
list(learnset).add(it)
|
||||
return@Interpreter true
|
||||
}
|
||||
?.let {
|
||||
list(learnset).add(it)
|
||||
return@Interpreter true
|
||||
}
|
||||
}
|
||||
return@Interpreter false
|
||||
}
|
||||
@@ -67,11 +68,11 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
|
||||
val interpreters = mutableListOf(
|
||||
tmInterpreter,
|
||||
eggInterpreter,
|
||||
tutorInterpreter,
|
||||
levelUpInterpreter,
|
||||
formChangeInterpreter
|
||||
tmInterpreter,
|
||||
eggInterpreter,
|
||||
tutorInterpreter,
|
||||
levelUpInterpreter,
|
||||
formChangeInterpreter
|
||||
)
|
||||
}
|
||||
|
||||
@@ -87,11 +88,25 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
val formChangeMoves = mutableListOf<MoveTemplate>()
|
||||
|
||||
fun getLevelUpMovesUpTo(level: Int) = levelUpMoves
|
||||
.entries
|
||||
.filter { it.key <= level }
|
||||
.sortedBy { it.key }
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
.entries
|
||||
.filter { it.key <= level }
|
||||
.sortedBy { it.key }
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
|
||||
fun tmLearnableMoves(): MutableList<MoveTemplate> {
|
||||
val moves = mutableListOf<MoveTemplate>()
|
||||
|
||||
moves.addAll(tmMoves)
|
||||
moves.addAll(tutorMoves)
|
||||
moves.addAll(eggMoves)
|
||||
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
|
||||
moves.addAll(evolutionMoves)
|
||||
moves.addAll(formChangeMoves)
|
||||
|
||||
return moves
|
||||
}
|
||||
|
||||
|
||||
// We only sync level up moves atm
|
||||
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|
||||
@@ -68,7 +68,7 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, CompoundTagAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
|
||||
|
||||
+1
@@ -66,6 +66,7 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawnablePosition>() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.position.NestSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* A spawning condition that applies to [NestSpawnablePosition]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawningCondition: SpawningCondition<NestSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = NestSpawnablePosition::class.java
|
||||
|
||||
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(spawnablePosition: NestSpawnablePosition): Boolean {
|
||||
if (!super.fits(spawnablePosition)) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> spawnablePosition.nearbyBlockTypes.any { cond.fits(it, spawnablePosition.blockRegistry) } }) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
super.copyFrom(other, merger)
|
||||
if (other is NestSpawningCondition) {
|
||||
neededNearbyBlocks = merger.merge(neededNearbyBlocks, other.neededNearbyBlocks)?.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
val containsNullValues = neededNearbyBlocks != null && neededNearbyBlocks!!.any {it == null}
|
||||
return super.isValid() && !containsNullValues
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "nest"
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* The grotto influence will normalize bucket weights and prevent non-habitat spawns from spawning.
|
||||
*
|
||||
* This influence exists once even if many grotto blocks are detected in the spawning zone.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since April 20th, 2025
|
||||
*/
|
||||
class GrottoImpactedSpawningInfluence(
|
||||
val structureIds: Set<ResourceLocation>
|
||||
): BucketNormalizingInfluence(tier = 3) {
|
||||
companion object {
|
||||
const val HABITAT_LABEL = "habitat"
|
||||
}
|
||||
|
||||
val markers = structureIds.map(ResourceLocation::toString)
|
||||
|
||||
override fun affectSpawnablePosition(spawnablePosition: SpawnablePosition) {
|
||||
spawnablePosition.markers.addAll(markers)
|
||||
}
|
||||
|
||||
override fun affectSpawnable(detail: SpawnDetail, spawnablePosition: SpawnablePosition) = HABITAT_LABEL in detail.labels
|
||||
|
||||
// TODO potentially insert spawns? Grotto block could supply some spawns
|
||||
|
||||
/*companion object {
|
||||
private val LOGGER = LoggerFactory.getLogger("GrottoInfluence")
|
||||
}
|
||||
|
||||
override fun affectSpawnable(detail: SpawnDetail, spawnablePosition: SpawningContext): Boolean {
|
||||
if (detail !is PokemonSpawnDetail) return true
|
||||
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return false
|
||||
|
||||
// If strict filtering enabled, reject anything not in allowed list
|
||||
if (allowedSpawns != null && !allowedSpawns.contains(detailId)) {
|
||||
LOGGER.info("[GrottoInfluence] Rejecting ${detailId.path} — not in current moon phase group.")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun affectWeight(detail: SpawnDetail, spawnablePosition: SpawningContext, weight: Float): Float {
|
||||
if (detail !is PokemonSpawnDetail) return weight
|
||||
val detailId = ResourceLocation.tryParse(detail.pokemon.species!!) ?: return weight
|
||||
|
||||
val boostTargetList = allowedSpawns ?: Habitats.getHabitat(structureId)?.spawnPool?.flatten() ?: emptyList()
|
||||
return if (boostTargetList.contains(detailId)) {
|
||||
LOGGER.info("[GrottoInfluence] Boosting spawn weight for ${detailId.path} (structure: $structureId)")
|
||||
weight * 100f
|
||||
} else {
|
||||
weight
|
||||
}
|
||||
}*/
|
||||
}
|
||||
+57
-48
@@ -6,51 +6,60 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
///*
|
||||
// * Copyright (C) 2023 Cobblemon Contributors
|
||||
// *
|
||||
// * This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// * License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
// */
|
||||
//
|
||||
//package com.cobblemon.mod.common.api.spawning.influence.detector
|
||||
//
|
||||
//import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
//import com.cobblemon.mod.common.api.spawning.influence.GrottoImpactedSpawningInfluence
|
||||
//import com.cobblemon.mod.common.api.spawning.influence.UnconditionalZoneSpawningInfluence
|
||||
//import com.cobblemon.mod.common.api.spawning.influence.ZoneSpawningInfluence
|
||||
//import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
//import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
//import com.cobblemon.mod.common.util.toBlockPos
|
||||
//import net.minecraft.core.BlockPos
|
||||
//import net.minecraft.core.Holder
|
||||
//import net.minecraft.server.level.ServerLevel
|
||||
//import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
//import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
//import net.minecraft.world.level.block.state.BlockState
|
||||
//
|
||||
//object GrottoBlockDetector : SpawningInfluenceDetector {
|
||||
// @JvmField
|
||||
// val RANGE: Int = 48
|
||||
//
|
||||
// override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): List<ZoneSpawningInfluence> {
|
||||
// val world = input.world
|
||||
// val searchRange = maxOf(RANGE, input.length * 2, input.height * 2)
|
||||
// val grottoBlockPositions = world.poiManager.findAll(
|
||||
// { holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
|
||||
// { true },
|
||||
// input.getCenter().toBlockPos(),
|
||||
// searchRange,
|
||||
// PoiManager.Occupancy.ANY
|
||||
// ).toList()
|
||||
//
|
||||
// return if (grottoBlockPositions.isEmpty()) {
|
||||
// emptyList()
|
||||
// } else {
|
||||
// listOf(UnconditionalZoneSpawningInfluence(influence = GrottoImpactedSpawningInfluence()))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun detectFromBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<ZoneSpawningInfluence>()
|
||||
//}
|
||||
package com.cobblemon.mod.common.api.spawning.influence.detector
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.spawning.influence.GrottoImpactedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
object GrottoBlockDetector : SpawningInfluenceDetector {
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): List<SpawningZoneInfluence> {
|
||||
val world = input.world
|
||||
val searchRange = maxOf(RANGE, input.length * 2, input.height * 2)
|
||||
val grottoBlockPositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
|
||||
{ true },
|
||||
input.getCenter().toBlockPos(),
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
val structureIds = grottoBlockPositions.mapNotNull {
|
||||
val isLoaded = world.isLoaded(it)
|
||||
if (isLoaded) {
|
||||
val blockEntity = world.getBlockEntity(it) as? GrottoBlockEntity ?: return@mapNotNull null
|
||||
blockEntity.detectedStructure
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
return if (grottoBlockPositions.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(
|
||||
UnconditionalSpawningZoneInfluence(
|
||||
influence = GrottoImpactedSpawningInfluence(
|
||||
structureIds = structureIds
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun detectFromBlock(world: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
+2
-1
@@ -33,7 +33,8 @@ interface SpawningInfluenceDetector {
|
||||
val detectors = mutableSetOf<SpawningInfluenceDetector>(
|
||||
LureCakeDetector,
|
||||
SaccharineHoneyLogDetector,
|
||||
IncenseSweetDetector
|
||||
IncenseSweetDetector,
|
||||
GrottoBlockDetector
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.nest
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* A spawning cause that is embellished with nest information.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawnCause(
|
||||
spawner: Spawner,
|
||||
entity: Entity?
|
||||
) : SpawnCause(spawner, entity) {
|
||||
// Crab Note: todo what do we put here? a reference to the nest entity causing this spawn cause?
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.position.NestSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
|
||||
/**
|
||||
* A spawner that takes in a [FishingSpawnCause] and spawns things.
|
||||
*
|
||||
* @author Plastered_Crab
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
open class NestSpawner(
|
||||
override val name: String = "nest",
|
||||
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
) : TriggerSpawner<NestSpawnCause>(name, pool) {
|
||||
override fun parseSpawnablePosition(cause: NestSpawnCause, world: ServerLevel, pos: BlockPos): NestSpawnablePosition? {
|
||||
val spawnablePosition = NestSpawnablePosition(
|
||||
cause = cause,
|
||||
world = world,
|
||||
pos = pos,
|
||||
influences = influences
|
||||
)
|
||||
return spawnablePosition
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.position
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.nest.NestSpawnCause
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
/**
|
||||
* A spawnable position that was generated for a nest spawn action.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class NestSpawnablePosition(
|
||||
cause: NestSpawnCause,
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
influences: MutableList<SpawningInfluence>
|
||||
) : TriggerSpawnablePosition(
|
||||
cause = cause,
|
||||
world = world,
|
||||
position = pos,
|
||||
light = world.getMaxLocalRawBrightness(pos),
|
||||
skyLight = world.getMaxLocalRawBrightness(pos.above()),
|
||||
canSeeSky = world.canSeeSkyFromBelowWater(pos),
|
||||
influences = influences
|
||||
) {
|
||||
val nearbyBlocks = world.getBlockStates(AABB.ofSize(pos.toVec3d(), 10.0, 10.0, 10.0))
|
||||
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.map { it.block }.distinct().toList() }
|
||||
}
|
||||
+2
-2
@@ -76,7 +76,7 @@ abstract class SpawnablePosition {
|
||||
abstract val skyLight: Int
|
||||
/** Whether or not the sky is visible at this location. */
|
||||
abstract val canSeeSky: Boolean
|
||||
/** A list of [SpawningInfluence]s that apply due to this specific position. This generally shouldn't be consumed; use [getAllInfluences].*/
|
||||
/** A list of [SpawningInfluence]s that apply due to this specific position.*/
|
||||
abstract val influences: MutableList<SpawningInfluence>
|
||||
/** Gets a cache of structures by block coordinates, grouped by chunk. */
|
||||
abstract fun getStructureCache(pos: BlockPos): StructureChunkCache
|
||||
@@ -192,7 +192,7 @@ abstract class SpawnablePosition {
|
||||
/**
|
||||
* Gets all influences that apply to this spawnable position, including the cause, and does something with them.
|
||||
*
|
||||
* Technically we could simple make a list of influences + cause and run that, but influences MUST be able
|
||||
* Technically we could simply make a list of influences + cause and run that, but influences MUST be able
|
||||
* to run extremely frequently in performance-critical code, so we don't want to have to allocate a list.
|
||||
*/
|
||||
fun applyInfluences(extraInfluences: List<SpawningInfluence>? = null, usage: (SpawningInfluence) -> Unit) {
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnerManager
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class GrottoSpawner(
|
||||
name: String,
|
||||
fallbackSpawns: SpawnPool,
|
||||
manager: SpawnerManager
|
||||
) : AreaSpawner(name, fallbackSpawns, manager) {
|
||||
|
||||
companion object {
|
||||
private val cache = ConcurrentHashMap<BlockPos, Pair<SpawnPool, Long>>()
|
||||
private const val CACHE_DURATION_MS = 10_000
|
||||
}
|
||||
|
||||
override var ticksBetweenSpawns: Float = 120F
|
||||
|
||||
// todo use this getArea to try to use a new GrottoSpawnCause and use that to send the structure center so spawns are around the structures
|
||||
override fun getZoneInput(cause: SpawnCause): SpawningZoneInput? {
|
||||
// if (cause is GrottoSpawnCause) {
|
||||
// val center = cause.structureCenter
|
||||
// return SpawningArea(
|
||||
// cause = cause,
|
||||
// world = server()?.getLevel(cause.entityWorldId) ?: return null,
|
||||
// baseX = center.x - 8,
|
||||
// baseY = center.y - 4,
|
||||
// baseZ = center.z - 8,
|
||||
// length = 16,
|
||||
// height = 8,
|
||||
// width = 16
|
||||
// )
|
||||
// }
|
||||
return null
|
||||
}
|
||||
|
||||
override fun run(cause: SpawnCause): List<SpawnAction<*>> {
|
||||
//println("[GrottoSpawner] run() starting for cause: $cause")
|
||||
val area = getZoneInput(cause)
|
||||
//println("[GrottoSpawner] getArea() result: $area")
|
||||
val constrainedArea = if (area != null) constrainArea(area) else null
|
||||
//println("[GrottoSpawner] constrainArea() result: $constrainedArea")
|
||||
|
||||
if (constrainedArea != null) {
|
||||
val zone = spawningZoneGenerator.generate(this, constrainedArea)
|
||||
val spawnablePositions = spawnablePositionResolver.resolve(this, spawnablePositionCalculators, zone)
|
||||
val worldInfluences = zone.unconditionalInfluences
|
||||
val bucket = chooseBucket(cause, this.influences + cause + worldInfluences)
|
||||
val spawnActions = getSpawningSelector().select(this, bucket, spawnablePositions, max = 1)
|
||||
return spawnActions
|
||||
}
|
||||
|
||||
//println("[GrottoSpawner] Area is null or invalid, skipping spawn")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// todo test tick
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
ticksUntilNextSpawn -= tickTimerMultiplier
|
||||
if (ticksUntilNextSpawn > 0) return
|
||||
|
||||
val server = server() ?: return
|
||||
val world = server.overworld() ?: return
|
||||
|
||||
for (player in world.players()) {
|
||||
val searchOrigin = player.blockPosition()
|
||||
val poiManager = world.poiManager
|
||||
val grottoPOIs = poiManager.findAll(
|
||||
{ it.`is`(CobblemonPoiTypes.GROTTO_BLOCK_KEY) },
|
||||
{ true },
|
||||
searchOrigin,
|
||||
128, // Search radius
|
||||
PoiManager.Occupancy.ANY
|
||||
)
|
||||
|
||||
for (grottoPos in grottoPOIs) {
|
||||
val blockEntity = world.getBlockEntity(grottoPos) as? GrottoBlockEntity ?: continue
|
||||
val structureCenter = blockEntity.structureCenter ?: continue
|
||||
|
||||
// val cause = GrottoSpawnCause(this, chooseBucket(), player, structureCenter, grottoPos)
|
||||
// val result = run(cause)
|
||||
//
|
||||
// //println("[GrottoSpawner] Grotto at $grottoPos spawn result: $result")
|
||||
//
|
||||
// if (result != null) {
|
||||
// val (context, detail) = result
|
||||
// val action = detail.doSpawn(context)
|
||||
// //println("[GrottoSpawner] doSpawn: $detail -> $action")
|
||||
// val completion = action.complete()
|
||||
// //println("[GrottoSpawner] spawn complete: $completion")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
ticksUntilNextSpawn = ticksBetweenSpawns
|
||||
}
|
||||
|
||||
// todo we want to make it so that it ignores conditions of the area and just use the SpawnPool of the JSON registry that has been cycled during that moon cycle
|
||||
// override fun getSpawningSelector(): SpawningSelector {
|
||||
// return object : SpawningSelector {
|
||||
// override fun select(spawner: Spawner, bucket: SpawnBucket, contexts: List<SpawningContext>): Pair<SpawningContext, SpawnDetail>? {
|
||||
// val pool = when {
|
||||
// spawner is GrottoSpawner && contexts.firstOrNull()?.cause is GrottoSpawnCause ->
|
||||
// spawner.getSpawnPool(contexts.first().cause)
|
||||
// else -> spawner.getSpawnPool()
|
||||
// }
|
||||
//
|
||||
// for (spawnablePosition in contexts) {
|
||||
// val details = pool.details.filterIsInstance<PokemonSpawnDetail>()
|
||||
// if (details.isEmpty()) continue
|
||||
//
|
||||
// val chosen = weightedRandom(details) { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
|
||||
// //println("[GrottoSpawner] Selected ${chosen.pokemon.species} for spawn")
|
||||
// return spawnablePosition to chosen
|
||||
// }
|
||||
//
|
||||
// //println("[GrottoSpawner] No spawnable Pokémon found.")
|
||||
// return null
|
||||
// }
|
||||
//
|
||||
// override fun getTotalWeights(spawner: Spawner, contexts: List<SpawningContext>): Map<SpawnDetail, Float> {
|
||||
// return spawner.getSpawnPool().details.associateWith { it.weight.takeIf { w -> w > 0 } ?: 1.0f }
|
||||
// }
|
||||
//
|
||||
// private fun <T> weightedRandom(entries: List<T>, weightFn: (T) -> Float): T {
|
||||
// val totalWeight = entries.sumOf { weightFn(it).toDouble() }
|
||||
// var roll = Math.random() * totalWeight
|
||||
// for (entry in entries) {
|
||||
// roll -= weightFn(entry)
|
||||
// if (roll <= 0) return entry
|
||||
// }
|
||||
// return entries.last()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
override fun getSpawnPool(): SpawnPool {
|
||||
return spawns
|
||||
}
|
||||
|
||||
// todo new getSpawnPool
|
||||
// fun getSpawnPool(cause: SpawnCause): SpawnPool {
|
||||
// val server = server() ?: return spawns
|
||||
// val world = server.overworld() ?: return spawns
|
||||
// val now = System.currentTimeMillis()
|
||||
//
|
||||
// if (cause !is GrottoSpawnCause) return spawns
|
||||
//
|
||||
// val center = cause.grottoPos
|
||||
// val targetEntity = world.getChunk(center)?.getBlockEntity(center) as? GrottoBlockEntity ?: return spawns
|
||||
//
|
||||
// val structure = targetEntity.detectedStructure ?: return spawns
|
||||
// val spawnGroup = targetEntity.currentSpawnGroup
|
||||
// if (spawnGroup.isEmpty()) return spawns
|
||||
//
|
||||
// val cached = cache[center]
|
||||
// if (cached != null && now - cached.second <= CACHE_DURATION_MS) {
|
||||
// println("[GrottoSpawner] Cache hit at $center — using group: $spawnGroup")
|
||||
// return cached.first
|
||||
// }
|
||||
//
|
||||
// val habitat = Habitats.getHabitat(structure)
|
||||
// if (habitat == null) {
|
||||
// println("[GrottoSpawner] No habitat found for structure: $structure")
|
||||
// return spawns
|
||||
// }
|
||||
//
|
||||
// val selectedGroup = habitat.spawnPool.find { group ->
|
||||
// group.spawns.toSet() == spawnGroup.toSet()
|
||||
// } ?: return spawns.also {
|
||||
// /*println("[GrottoSpawner] Could not match spawn group at $center to any group in habitat: $structure")
|
||||
// println(" ↳ Current spawnGroup: $spawnGroup")
|
||||
// println(" ↳ Habitat groups:")
|
||||
// habitat.spawnPool.forEachIndexed { i, g ->
|
||||
// println(" [$i]: ${g.spawns}")
|
||||
// }*/
|
||||
// }
|
||||
//
|
||||
// val poolCacheName = "grotto_pool_${center.x}_${center.y}_${center.z}"
|
||||
// val pool = SpawnPool(poolCacheName)
|
||||
// for (entry in selectedGroup.spawns) {
|
||||
// val player = cause.entity as? ServerPlayer
|
||||
// val party = player?.party()?.filterNotNull()
|
||||
// val maxLevel = party?.maxOfOrNull { it.level } ?: 20
|
||||
// val variation = 5
|
||||
//
|
||||
// val minLevel = max(1, maxLevel - variation)
|
||||
// val maxLevelFinal = min(100, maxLevel + variation)
|
||||
// val level = (minLevel..maxLevelFinal).random()
|
||||
//
|
||||
// val detail = PokemonSpawnDetail().apply {
|
||||
// pokemon.species = entry.species.toString()
|
||||
// weight = entry.weight.toFloat()
|
||||
// pokemon.level = level
|
||||
// }
|
||||
//
|
||||
// pool.details.add(detail)
|
||||
// }
|
||||
//
|
||||
// /*println("[GrottoSpawner] Constructing pool for Grotto at ${cause.structureCenter}")
|
||||
// println(" ↳ Detected structure: $structure")
|
||||
// println(" ↳ Spawn group: $spawnGroup")
|
||||
// println(" ↳ Selected group: $selectedGroup")
|
||||
// println(" ↳ Pool size: ${pool.details.size}")*/
|
||||
//
|
||||
// cache[center] = pool to now
|
||||
// return pool
|
||||
// }
|
||||
}
|
||||
@@ -75,6 +75,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
remove(pokemon)
|
||||
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
|
||||
set(position, pokemon)
|
||||
|
||||
// sync TM moves here
|
||||
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+2
@@ -27,6 +27,7 @@ data class GeneralPlayerData(
|
||||
var starterSelected: Boolean,
|
||||
var starterUUID: UUID?,
|
||||
var keyItems: MutableSet<ResourceLocation>,
|
||||
var tmSet: MutableSet<ResourceLocation>,
|
||||
var battleTheme: ResourceLocation?,
|
||||
val extraData: MutableMap<String, PlayerDataExtension>,
|
||||
) : InstancedPlayerData {
|
||||
@@ -44,6 +45,7 @@ data class GeneralPlayerData(
|
||||
starterSelected,
|
||||
starterUUID,
|
||||
advancementData.totalBattleVictoryCount == 0,
|
||||
tmSet,
|
||||
battleTheme
|
||||
)
|
||||
}
|
||||
|
||||
+22
@@ -8,10 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.MODID
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Executors
|
||||
@@ -66,6 +68,14 @@ open class PlayerInstancedDataStoreManager {
|
||||
.infiniteIterations()
|
||||
.tracker(ServerTaskTracker)
|
||||
.build()
|
||||
|
||||
saveTasks[PlayerInstancedDataStoreTypes.TM_MOVES] = ScheduledTask.Builder()
|
||||
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.TM_MOVES) }
|
||||
.delay(30f)
|
||||
.interval(120f)
|
||||
.infiniteIterations()
|
||||
.tracker(ServerTaskTracker)
|
||||
.build()
|
||||
}
|
||||
|
||||
open fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
|
||||
@@ -126,4 +136,16 @@ open class PlayerInstancedDataStoreManager {
|
||||
open fun getPokedexData(playerId: UUID): PokedexManager {
|
||||
return get(playerId, PlayerInstancedDataStoreTypes.POKEDEX) as PokedexManager
|
||||
}
|
||||
|
||||
fun getTMData(player: ServerPlayer): TMMoveManager {
|
||||
return getTMData(player.uuid)
|
||||
}
|
||||
|
||||
fun getTMData(playerId: UUID): TMMoveManager {
|
||||
val data = get(playerId, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
if (data !is TMMoveManager) {
|
||||
Cobblemon.LOGGER.warn("getTMData: No TMMoveManager found for $playerId (got ${data?.javaClass?.name})")
|
||||
}
|
||||
return data as TMMoveManager
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -27,11 +28,22 @@ object PlayerInstancedDataStoreTypes {
|
||||
ClientPokedexManager::runAction,
|
||||
ClientPokedexManager::runIncremental
|
||||
))
|
||||
val TM_MOVES = register(PlayerInstancedDataStoreType(
|
||||
cobblemonResource("tm_moves"),
|
||||
ClientTMMoveManager::decode,
|
||||
ClientTMMoveManager::runAction,
|
||||
ClientTMMoveManager::runIncremental
|
||||
))
|
||||
|
||||
fun register(type: PlayerInstancedDataStoreType): PlayerInstancedDataStoreType {
|
||||
println("Registering PlayerInstancedDataStoreType: ${type.id}")
|
||||
types[type.id] = type
|
||||
return type
|
||||
}
|
||||
|
||||
fun getTypeById(id: ResourceLocation) = types[id]
|
||||
fun getTypeById(id: ResourceLocation): PlayerInstancedDataStoreType? {
|
||||
val type = types[id]
|
||||
if (type == null) println("Unknown PlayerInstancedDataStoreType ID: $id")
|
||||
return type
|
||||
}
|
||||
}
|
||||
+1
@@ -36,6 +36,7 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf(),
|
||||
tmSet = mutableSetOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.location
|
||||
)}
|
||||
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf(),
|
||||
tmSet = mutableSetOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.location
|
||||
)}
|
||||
|
||||
|
||||
+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.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveJsonBackend :
|
||||
JsonBackedPlayerDataStoreBackend<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
|
||||
.create()
|
||||
|
||||
override val classToken = TypeToken.get(TMMoveManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
override fun initialize(store: TMMoveManager) {
|
||||
store.initialize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
|
||||
}
|
||||
}
|
||||
+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.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mongodb.client.MongoClient
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveMongoBackend(
|
||||
mongoClient: MongoClient,
|
||||
databaseName: String,
|
||||
collectionName: String
|
||||
) : MongoBackedPlayerDataStoreBackend<TMMoveManager>(
|
||||
mongoClient,
|
||||
databaseName,
|
||||
collectionName,
|
||||
PlayerInstancedDataStoreTypes.TM_MOVES
|
||||
) {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
|
||||
.create()
|
||||
override val classToken = TypeToken.get(TMMoveManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
override fun initialize(store: TMMoveManager) {
|
||||
store.initialize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
|
||||
}
|
||||
}
|
||||
+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.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveNbtBackend : NbtBackedPlayerData<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
|
||||
override val codec = TMMoveManager.CODEC
|
||||
override val defaultData = TMMoveJsonBackend.defaultDataFunc
|
||||
}
|
||||
+4
@@ -34,6 +34,7 @@ data class ClientGeneralPlayerData(
|
||||
var starterSelected: Boolean = false,
|
||||
var starterUUID: UUID? = null,
|
||||
var showChallengeLabel: Boolean = true,
|
||||
var tmSet: MutableSet<ResourceLocation> = mutableSetOf(),
|
||||
val battleTheme: ResourceLocation? = null
|
||||
) : ClientInstancedPlayerData {
|
||||
|
||||
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
|
||||
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
|
||||
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
|
||||
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
|
||||
buf.writeCollection(tmSet) { pb, value -> pb.writeIdentifier(value)}
|
||||
}
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
|
||||
@@ -56,6 +58,7 @@ data class ClientGeneralPlayerData(
|
||||
val showChallengeLabel = buffer.readBoolean()
|
||||
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
|
||||
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
|
||||
val tmSet = buffer.readCollection({ mutableSetOf<ResourceLocation>() }) { it.readIdentifier() }
|
||||
val battleTheme = buffer.readNullable { it.readIdentifier() }
|
||||
val data = ClientGeneralPlayerData(
|
||||
resetStarterPrompt,
|
||||
@@ -64,6 +67,7 @@ data class ClientGeneralPlayerData(
|
||||
starterSelected,
|
||||
starterUUID,
|
||||
showChallengeLabel,
|
||||
tmSet,
|
||||
battleTheme
|
||||
)
|
||||
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.client
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class ClientTMMoveManager(
|
||||
val learnedTMs: MutableSet<ResourceLocation>
|
||||
) : ClientInstancedPlayerData {
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf) {
|
||||
buf.writeCollection(learnedTMs) { _, id -> buf.writeString(id.toString()) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(buf: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
|
||||
val tms = buf.readCollection(
|
||||
{ mutableSetOf<ResourceLocation>() },
|
||||
{ buf.readString().let(ResourceLocation::tryParse) ?: ResourceLocation("minecraft", "empty") }
|
||||
)
|
||||
return SetClientPlayerDataPacket(
|
||||
PlayerInstancedDataStoreTypes.TM_MOVES,
|
||||
ClientTMMoveManager(tms)
|
||||
)
|
||||
}
|
||||
|
||||
fun runAction(data: ClientInstancedPlayerData) {
|
||||
if (data !is ClientTMMoveManager) return
|
||||
CobblemonClient.clientTMMoveData = data
|
||||
}
|
||||
|
||||
fun runIncremental(data: ClientInstancedPlayerData) {
|
||||
if (data !is ClientTMMoveManager) return
|
||||
CobblemonClient.clientTMMoveData.learnedTMs.addAll(data.learnedTMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,40 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val HAS_REVIVAL_HERBS = create("has_feature/revival_herbs")
|
||||
|
||||
// Nests
|
||||
@JvmField
|
||||
val HAS_STICK_NEST = create("has_nest/nest_stick")
|
||||
@JvmField
|
||||
val HAS_STICK_NEST_TREES = create("has_nest/nest_stick_trees")
|
||||
@JvmField
|
||||
val HAS_LEAF_NEST_TREES = create("has_nest/nest_leaf_trees")
|
||||
@JvmField
|
||||
val HAS_SNOW_NEST = create("has_nest/nest_snow")
|
||||
@JvmField
|
||||
val HAS_SAND_NEST = create("has_nest/nest_sand")
|
||||
@JvmField
|
||||
val HAS_RED_SAND_NEST = create("has_nest/nest_red_sand")
|
||||
@JvmField
|
||||
val HAS_SEAGRASS_NEST = create("has_nest/nest_seagrass")
|
||||
@JvmField
|
||||
val HAS_WARM_OCEAN_NEST = create("has_nest/nest_warm_ocean")
|
||||
@JvmField
|
||||
val HAS_STONE_NEST = create("has_nest/nest_stone")
|
||||
@JvmField
|
||||
val HAS_STONE_NEST_CAVES = create("has_nest/nest_stone_caves")
|
||||
@JvmField
|
||||
val HAS_DEEPSLATE_NEST_CAVES = create("has_nest/nest_deepslate_caves")
|
||||
@JvmField
|
||||
val HAS_LUSH_NEST_CAVES = create("has_nest/nest_lush_caves")
|
||||
@JvmField
|
||||
val HAS_BASALT_NEST = create("has_nest/nest_basalt")
|
||||
@JvmField
|
||||
val HAS_NETHER_NEST = create("has_nest/nest_nether")
|
||||
@JvmField
|
||||
val HAS_SOUL_SAND_NEST = create("has_nest/nest_soul_sand")
|
||||
@JvmField
|
||||
val HAS_CHORUS_NEST = create("has_nest/nest_chorus")
|
||||
|
||||
// Has Ore tags
|
||||
@JvmField
|
||||
val HAS_DAWN_STONE_ORE = create("has_ore/ore_dawn_stone_normal")
|
||||
|
||||
@@ -89,6 +89,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val TUMBLESTONE_BRICKS = createTag("tumblestone_bricks")
|
||||
@JvmField val TUMBLESTONES = createTag("tumblestones")
|
||||
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
|
||||
@JvmField val TYPE_GEM_BLOCKS = createTag("type_gem_blocks")
|
||||
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
|
||||
@JvmField val WALL_SIGNS = createTag("wall_signs")
|
||||
@JvmField val WATER_STONE_ORES = createTag("water_stone_ores")
|
||||
|
||||
@@ -110,6 +110,7 @@ object CobblemonItemTags {
|
||||
@JvmField val ZINC_INGREDIENTS = create("zinc_ingredients")
|
||||
|
||||
// Held Item Tags
|
||||
@JvmField val DEFAULTING_POKE_BALLS = create("defaulting_poke_balls")
|
||||
@JvmField val CONSUMED_IN_NPC_BATTLE = create("held/consumed_in_npc_battle")
|
||||
@JvmField val CONSUMED_IN_PVP_BATTLE = create("held/consumed_in_pvp_battle")
|
||||
@JvmField val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
abstract class AbstractTMMoveManager {
|
||||
abstract val uuid: UUID
|
||||
abstract val learnedTMs: MutableSet<ResourceLocation>
|
||||
abstract val storeType: PlayerInstancedDataStoreType
|
||||
abstract fun toClientData(): ClientInstancedPlayerData
|
||||
abstract fun toClientDataFrom(set: Set<ResourceLocation>): ClientInstancedPlayerData
|
||||
|
||||
open fun learn(tmId: ResourceLocation): Boolean {
|
||||
if (learnedTMs.add(tmId)) {
|
||||
syncClient(setOf(tmId))
|
||||
sendTMToast(tmId)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun sendTMToast(tmId: ResourceLocation) {
|
||||
val player = uuid.getPlayer() ?: return
|
||||
val tm = TechnicalMachines.tmMap[tmId] ?: return
|
||||
|
||||
val packet = ToastPacket(
|
||||
title = Component.literal("New TM Learned"), // todp lang for this
|
||||
description = tm.translatedMoveName(),
|
||||
icon = tm.createItemStack(),
|
||||
frameTexture = ResourceLocation.parse("minecraft:toast/advancement"),
|
||||
progress = -1F,
|
||||
progressColor = 0x00FF00,
|
||||
uuid = UUID.nameUUIDFromBytes(("tm_toast:${tmId}").toByteArray()),
|
||||
behaviour = ToastPacket.Behaviour.SHOW_OR_UPDATE,
|
||||
durationMs = 4000L
|
||||
)
|
||||
|
||||
player.sendPacket(packet)
|
||||
}
|
||||
|
||||
protected fun syncClient(updateSet: Set<ResourceLocation> = learnedTMs) {
|
||||
uuid.getPlayer()?.sendPacket(
|
||||
SetClientPlayerDataPacket(
|
||||
type = storeType,
|
||||
playerData = toClientDataFrom(updateSet),
|
||||
isIncremental = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
open fun markDirty() {}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.tms.obtain.ImpossibleObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PokemonHasMoveObtainMethod
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
interface ObtainMethod {
|
||||
val passive: Boolean
|
||||
|
||||
/**
|
||||
* Checks if this [ObtainMethod] matches the given player.
|
||||
*/
|
||||
fun matches(player: ServerPlayer): Boolean
|
||||
|
||||
/**
|
||||
* Serializes this [ObtainMethod] to the buffer.
|
||||
*/
|
||||
fun writeToBuffer(buffer: RegistryFriendlyByteBuf)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Deserializes an [ObtainMethod] from the buffer.
|
||||
*/
|
||||
fun readFromBuffer(buffer: RegistryFriendlyByteBuf): ObtainMethod {
|
||||
val variant = buffer.readUtf()
|
||||
return when (variant) {
|
||||
"cobblemon:impossible" -> ImpossibleObtainMethod()
|
||||
"cobblemon:none" -> NoneObtainMethod()
|
||||
"cobblemon:advancement" -> {
|
||||
val advancement = buffer.readResourceLocation()
|
||||
PlayerHasAdvancementObtainMethod(advancement)
|
||||
}
|
||||
"cobblemon:y_level" -> {
|
||||
val yLevel = buffer.readVarInt()
|
||||
val operator = buffer.readUtf()
|
||||
PlayerYObtainMethod(yLevel, operator)
|
||||
}
|
||||
"cobblemon:pokemon_knows" -> {
|
||||
val moveId = buffer.readUtf()
|
||||
PokemonHasMoveObtainMethod(moveId)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown ObtainMethod variant: $variant")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A type adapter for [ObtainMethod]s.
|
||||
* For the default implementation see [CobblemonObtainMethodAdapter].
|
||||
*
|
||||
* @author Apion
|
||||
* @since November 22nd, 2023
|
||||
*/
|
||||
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
|
||||
/**
|
||||
* Register a [ObtainMethod] to be used by this adapter.
|
||||
*
|
||||
* @param type The [KClass] of the [ObtainMethod].
|
||||
* @param identifier The expected [ResourceLocation] in the parsed JSON.
|
||||
*/
|
||||
fun register(type: KClass<out ObtainMethod>, identifier: ResourceLocation)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.tms.AbstractTMMoveManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveManager(
|
||||
override val uuid: UUID,
|
||||
override val learnedTMs: MutableSet<ResourceLocation> = mutableSetOf()
|
||||
) : AbstractTMMoveManager(), InstancedPlayerData {
|
||||
|
||||
override val storeType = PlayerInstancedDataStoreTypes.TM_MOVES
|
||||
|
||||
override fun initialize() {}
|
||||
|
||||
override fun toClientData(): ClientTMMoveManager {
|
||||
return ClientTMMoveManager(learnedTMs.toMutableSet())
|
||||
}
|
||||
|
||||
override fun toClientDataFrom(set: Set<ResourceLocation>): ClientTMMoveManager {
|
||||
return ClientTMMoveManager(set.toMutableSet())
|
||||
}
|
||||
|
||||
fun syncTMsFromPokemon(pokemon: Pokemon) {
|
||||
val learnableTMs = TechnicalMachines.tmMap.values
|
||||
.filter { tm -> pokemon.allAccessibleMoves.contains(tm.moveName) }
|
||||
|
||||
for (tm in learnableTMs) {
|
||||
learn(tm.id)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<TMMoveManager> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.xmap(UUID::fromString, UUID::toString)
|
||||
.fieldOf("uuid")
|
||||
.forGetter { it.uuid },
|
||||
Codec.list(ResourceLocation.CODEC)
|
||||
.xmap({ it.toMutableSet() }, { it.toList() })
|
||||
.fieldOf("learnedTMs")
|
||||
.forGetter { it.learnedTMs }
|
||||
).apply(instance) { uuid, learnedTMs ->
|
||||
TMMoveManager(uuid, learnedTMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class TechnicalMachine(
|
||||
val moveName: MoveTemplate,
|
||||
val recipe: List<TechnicalMachineRecipe>?,
|
||||
val obtainMethods: List<ObtainMethod> = emptyList(),
|
||||
val type: String
|
||||
) {
|
||||
lateinit var id: ResourceLocation
|
||||
/*
|
||||
val move: MoveTemplate? = Moves.getByName(moveName.displayName.toString()).also {
|
||||
if (it == null) {
|
||||
Cobblemon.LOGGER.error("MoveTemplate not found for moveName: $moveName")
|
||||
}
|
||||
}*/
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Filters all available [TechnicalMachines] based on three optional arguments
|
||||
*
|
||||
* @param search A [String] to check against move names
|
||||
* @param type An [ElementalType] to check for
|
||||
* @param pokemon A [Pokemon] to check the [Learnset] of
|
||||
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
|
||||
*/
|
||||
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?, player: Player? = null): MutableSet<TechnicalMachine> {
|
||||
val tms = TechnicalMachines.tmMap.values.toMutableSet()
|
||||
|
||||
type?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (ElementalTypes.get(tm.type) != type) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pokemon?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (!pokemon.species.moves.tmLearnableMoves().contains(tm.moveName)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (!tm.translatedMoveName().string.contains(search, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player?.let {
|
||||
val learnedTMs = CobblemonClient.clientTMMoveData?.learnedTMs ?: return@let
|
||||
tms.retainAll { tm -> learnedTMs.contains(tm.id) }
|
||||
}
|
||||
|
||||
return tms
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks this [TechnicalMachine] for the player.
|
||||
*
|
||||
* @param player The [ServerPlayer] to give this [TechnicalMachine] to.
|
||||
* @return Whether the player was successfully granted the [TechnicalMachine]
|
||||
*/
|
||||
fun unlock(player: ServerPlayer): Boolean {
|
||||
Cobblemon.playerDataManager.getGenericData(player) // .get(player, ).tmSet.add(id)// .playerData.get(player).tmSet.add(id)
|
||||
if (!obtainMethods.any { it is NoneObtainMethod }) {
|
||||
player.sendSystemMessage(lang("tms.unlock_tm", moveName.displayName))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [MutableComponent] of the translated move name of this [TechnicalMachine]
|
||||
*
|
||||
* @return This [TechnicalMachine]'s move name, translated
|
||||
*/
|
||||
fun translatedMoveName(): MutableComponent = moveName.displayName
|
||||
|
||||
fun createItemStack(): ItemStack {
|
||||
val stack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
||||
TMMoveComponent.setTMMove(stack, moveName)
|
||||
return stack
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
|
||||
*/
|
||||
data class TechnicalMachineRecipe(
|
||||
val item: ResourceLocation,
|
||||
val count: Int
|
||||
)
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.item.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.registry.ItemTagCondition
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
|
||||
.registerTypeAdapter(MoveTemplate::class.java, MoveTemplateAdapter)
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
|
||||
override val resourcePath = "tms"
|
||||
override val id = cobblemonResource("technical_machines")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<TechnicalMachines>()
|
||||
|
||||
val tmMap = mutableMapOf<ResourceLocation, TechnicalMachine>()
|
||||
val moveToTM = mutableMapOf<MoveTemplate, TechnicalMachine>()
|
||||
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
|
||||
val passiveTms = mutableMapOf<ResourceLocation, TechnicalMachine>()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, TechnicalMachine>) {
|
||||
data.forEach { (id, tm) ->
|
||||
//TODO remove the logging
|
||||
Cobblemon.LOGGER.info("Processing TM: $id with moveName: ${tm.moveName}")
|
||||
if (tm.moveName == null) {
|
||||
Cobblemon.LOGGER.error("Failed to resolve move for TM: $id with moveName: ${tm.moveName}")
|
||||
} else {
|
||||
Cobblemon.LOGGER.info("Resolved Move for TM: $id -> ${tm.moveName.name}")
|
||||
}
|
||||
tmMap[id] = tm
|
||||
tm.id = id
|
||||
|
||||
moveToTM[tm.moveName] = tm
|
||||
|
||||
// Check for passive ObtainMethods
|
||||
//if (tm.obtainMethods.any { it.passive }) passiveTms[id] = tm
|
||||
}
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) { }
|
||||
|
||||
fun checkPassives(player: ServerPlayer) {
|
||||
val playerTms = Cobblemon.playerDataManager.getGenericData(player).tmSet
|
||||
passiveTms.forEach { (id, tm) ->
|
||||
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,10 @@ class ElementalType(
|
||||
val displayName: MutableComponent,
|
||||
val hue: Int,
|
||||
val textureXMultiplier: Int,
|
||||
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png")
|
||||
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
|
||||
val primaryColor: Int,
|
||||
val secondaryColor: Int,
|
||||
val typeGem: ResourceLocation
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
@@ -22,134 +23,174 @@ object ElementalTypes {
|
||||
name = "normal",
|
||||
displayName = Component.translatable("cobblemon.type.normal"),
|
||||
hue = 0xDDDDCF,
|
||||
textureXMultiplier = 0
|
||||
textureXMultiplier = 0,
|
||||
primaryColor = 0xCCCCC1,
|
||||
secondaryColor = 0xF9FFFC
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "fire",
|
||||
displayName = Component.translatable("cobblemon.type.fire"),
|
||||
hue = 0xE55C32,
|
||||
textureXMultiplier = 1
|
||||
textureXMultiplier = 1,
|
||||
primaryColor = 0xFF8766,
|
||||
secondaryColor = 0xFFD9A5
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "water",
|
||||
displayName = Component.translatable("cobblemon.type.water"),
|
||||
hue = 0x4A9BE8,
|
||||
textureXMultiplier = 2
|
||||
textureXMultiplier = 2,
|
||||
primaryColor = 0x7F9DFF,
|
||||
secondaryColor = 0xA5EEFF
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "grass",
|
||||
displayName = Component.translatable("cobblemon.type.grass"),
|
||||
hue = 0x4DBC3C,
|
||||
textureXMultiplier = 3
|
||||
textureXMultiplier = 3,
|
||||
primaryColor = 0x66CC99,
|
||||
secondaryColor = 0xD6FFA5
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "electric",
|
||||
displayName = Component.translatable("cobblemon.type.electric"),
|
||||
hue = 0xEFD128,
|
||||
textureXMultiplier = 4
|
||||
textureXMultiplier = 4,
|
||||
primaryColor = 0xFFCF3F,
|
||||
secondaryColor = 0xFFFFBF
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "ice",
|
||||
displayName = Component.translatable("cobblemon.type.ice"),
|
||||
hue = 0x6BC3EF,
|
||||
textureXMultiplier = 5
|
||||
textureXMultiplier = 5,
|
||||
primaryColor = 0x8CC7FF,
|
||||
secondaryColor = 0xD6FFFF
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "fighting",
|
||||
displayName = Component.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xC44C5C,
|
||||
textureXMultiplier = 6
|
||||
textureXMultiplier = 6,
|
||||
primaryColor = 0xCC6673,
|
||||
secondaryColor = 0xFFADA5
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "poison",
|
||||
displayName = Component.translatable("cobblemon.type.poison"),
|
||||
hue = 0xA24BD8,
|
||||
textureXMultiplier = 7
|
||||
textureXMultiplier = 7,
|
||||
primaryColor = 0x8D66CC,
|
||||
secondaryColor = 0xEDA5FF
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "ground",
|
||||
displayName = Component.translatable("cobblemon.type.ground"),
|
||||
hue = 0xD89950,
|
||||
textureXMultiplier = 8
|
||||
textureXMultiplier = 8,
|
||||
primaryColor = 0xE5A27E,
|
||||
secondaryColor = 0xFFE1A5
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "flying",
|
||||
displayName = Component.translatable("cobblemon.type.flying"),
|
||||
hue = 0xBCC1FF,
|
||||
textureXMultiplier = 9
|
||||
textureXMultiplier = 9,
|
||||
primaryColor = 0x8CB6FF,
|
||||
secondaryColor = 0xE8DDFF
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "psychic",
|
||||
displayName = Component.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xD86AD6,
|
||||
textureXMultiplier = 10
|
||||
textureXMultiplier = 10,
|
||||
primaryColor = 0xE48CFF,
|
||||
secondaryColor = 0xFFCCF6
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "bug",
|
||||
displayName = Component.translatable("cobblemon.type.bug"),
|
||||
hue = 0xA2C831,
|
||||
textureXMultiplier = 11
|
||||
textureXMultiplier = 11,
|
||||
primaryColor = 0xAFCC66,
|
||||
secondaryColor = 0xF6FFBF
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "rock",
|
||||
displayName = Component.translatable("cobblemon.type.rock"),
|
||||
hue = 0xAA9666,
|
||||
textureXMultiplier = 12
|
||||
textureXMultiplier = 12,
|
||||
primaryColor = 0x997F4C,
|
||||
secondaryColor = 0xDBC6A4
|
||||
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "ghost",
|
||||
displayName = Component.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9572E5,
|
||||
textureXMultiplier = 13
|
||||
textureXMultiplier = 13,
|
||||
primaryColor = 0x8E8CFF,
|
||||
secondaryColor = 0xE8A5FF
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "dragon",
|
||||
displayName = Component.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x535DE8,
|
||||
textureXMultiplier = 14
|
||||
textureXMultiplier = 14,
|
||||
primaryColor = 0xBA8CFF,
|
||||
secondaryColor = 0xA5E5FF
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "dark",
|
||||
displayName = Component.translatable("cobblemon.type.dark"),
|
||||
hue = 0x5C6CB2,
|
||||
textureXMultiplier = 15
|
||||
textureXMultiplier = 15,
|
||||
primaryColor = 0x5966B2,
|
||||
secondaryColor = 0xA5B1FF
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "steel",
|
||||
displayName = Component.translatable("cobblemon.type.steel"),
|
||||
hue = 0xC3CCE0,
|
||||
textureXMultiplier = 16
|
||||
textureXMultiplier = 16,
|
||||
primaryColor = 0x858EB2,
|
||||
secondaryColor = 0xF4FFFD
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "fairy",
|
||||
displayName = Component.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xEA727E,
|
||||
textureXMultiplier = 17
|
||||
textureXMultiplier = 17,
|
||||
primaryColor = 0xFF8CB6,
|
||||
secondaryColor = 0xFFD0CC
|
||||
)
|
||||
|
||||
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int): ElementalType {
|
||||
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int, primaryColor: Int, secondaryColor: Int): ElementalType {
|
||||
return ElementalType(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
hue = hue,
|
||||
textureXMultiplier = textureXMultiplier
|
||||
textureXMultiplier = textureXMultiplier,
|
||||
primaryColor = primaryColor,
|
||||
secondaryColor = secondaryColor,
|
||||
typeGem = cobblemonResource("${name}_gem")
|
||||
).also {
|
||||
allTypes.add(it)
|
||||
}
|
||||
|
||||
@@ -14,9 +14,11 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.battles.actor.MultiPokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -297,6 +299,99 @@ object BattleBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun pveDoubles(
|
||||
player: ServerPlayer,
|
||||
pokemonEntity1: PokemonEntity,
|
||||
pokemonEntity2: PokemonEntity,
|
||||
leadingPokemon: UUID? = null,
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_DOUBLES,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
fleeDistance: Float = Cobblemon.config.defaultFleeDistance,
|
||||
party: PartyStore = player.party()
|
||||
): BattleStartResult {
|
||||
// Prepare the player's team
|
||||
val playerTeam = party.toBattleTeam(
|
||||
clone = cloneParties,
|
||||
healPokemon = healFirst,
|
||||
leadingPokemon = leadingPokemon
|
||||
).sortedBy { it.health <= 0 }
|
||||
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
|
||||
// Combine both wild Pokémon into a single BattleActor
|
||||
val wildTeam = listOf(
|
||||
BattlePokemon(pokemonEntity1.pokemon),
|
||||
BattlePokemon(pokemonEntity2.pokemon)
|
||||
)
|
||||
val wildActor = MultiPokemonBattleActor(pokemonList = wildTeam, fleeDistance = Cobblemon.config.defaultFleeDistance)
|
||||
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
// Validate player's team
|
||||
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
)
|
||||
}
|
||||
|
||||
// Validate opponent Pokémon entities
|
||||
if (pokemonEntity1.battleId != null) {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
if (pokemonEntity2.battleId != null) {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
// Set battle themes
|
||||
playerActor.battleTheme = pokemonEntity1.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
println("Player actor: ${playerActor.pokemonList.size} Pokémon")
|
||||
println("WildActor Pokémon: ${wildActor.pokemonList.map { it.effectedPokemon.species.translatedName }}")
|
||||
|
||||
val side1 = BattleSide(playerActor)
|
||||
val side2 = BattleSide(wildActor)
|
||||
|
||||
println("Side1 actors: ${side1.actors.map { it.uuid }}")
|
||||
println("Side2 actors: ${side2.actors.map { it.uuid }}")
|
||||
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = side1,
|
||||
side2 = side2
|
||||
).ifSuccessful { battle ->
|
||||
println("Battle started with ID: ${battle.battleId}")
|
||||
if (!cloneParties) {
|
||||
pokemonEntity1.battleId = battle.battleId
|
||||
pokemonEntity2.battleId = battle.battleId
|
||||
}
|
||||
}.ifErrored {
|
||||
println("Battle start failed: ${it.errors}")
|
||||
}
|
||||
} else {
|
||||
println("Battle failed due to validation errors: ${errors.errors}")
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to create a PvE battle against the given Pokémon.
|
||||
*
|
||||
|
||||
+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,129 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.block.entity.GrottoBlockEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.EntityBlock
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.SoundType
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.storage.loot.LootContext
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import java.util.*
|
||||
|
||||
class GrottoBlock(properties: Properties) : Block(properties), EntityBlock {
|
||||
var level: Level? = null
|
||||
var pos: BlockPos? = null
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return GrottoBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
level: Level,
|
||||
state: BlockState,
|
||||
type: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
return if (!level.isClientSide && type == CobblemonBlockEntities.GROTTO_BLOCK) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
GrottoBlockEntity.TICKER as BlockEntityTicker<T>
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
|
||||
if (!level.isClientSide) {
|
||||
val be = level.getBlockEntity(pos) as? GrottoBlockEntity ?: return InteractionResult.PASS
|
||||
return be.onUse(player, hitResult)
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun getDrops(state: BlockState, params: LootParams.Builder): MutableList<ItemStack> {
|
||||
val blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY) as? GrottoBlockEntity
|
||||
val player = params.getOptionalParameter(LootContextParams.THIS_ENTITY) as? Player
|
||||
|
||||
if (player?.isCreative == true) return mutableListOf()
|
||||
|
||||
val mimic = blockEntity?.mimickedState?.block ?: return mutableListOf()
|
||||
return mutableListOf(ItemStack(mimic))
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape {
|
||||
return RenderShape.INVISIBLE
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getCollisionShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getVisualShape(
|
||||
state: BlockState,
|
||||
world: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
override fun getOcclusionShape(state: BlockState, level: BlockGetter, pos: BlockPos): VoxelShape? {
|
||||
return Shapes.block()
|
||||
}
|
||||
|
||||
override fun useShapeForLightOcclusion(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun spawnDestroyParticles(level: Level, player: Player, pos: BlockPos, state: BlockState) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
|
||||
val mimicState = blockEntity?.mimickedState ?: return
|
||||
mimicState.block.spawnDestroyParticles(level, player, pos, mimicState)
|
||||
}
|
||||
|
||||
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, isMoving: Boolean) {
|
||||
super.onPlace(state, level, pos, oldState, isMoving)
|
||||
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
|
||||
blockEntity?.updateLevelAndPos(level, pos)
|
||||
}
|
||||
|
||||
override fun getSoundType(state: BlockState): SoundType {
|
||||
val level = this.level ?: return super.getSoundType(state)
|
||||
val pos = this.pos ?: return super.getSoundType(state)
|
||||
val blockEntity = level.getBlockEntity(pos) as? GrottoBlockEntity
|
||||
val mimicState = blockEntity?.mimickedState ?: return super.getSoundType(state)
|
||||
return mimicState.soundType
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
@@ -42,6 +43,8 @@ abstract class GrowableStoneBlock(
|
||||
val nextStage: Block?
|
||||
) : DirectionalBlock(settings) {
|
||||
|
||||
abstract val growthChance: Int
|
||||
|
||||
private val upShape: VoxelShape = Block.box(
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
@@ -95,20 +98,26 @@ abstract class GrowableStoneBlock(
|
||||
.setValue(FACING, Direction.DOWN))
|
||||
}
|
||||
|
||||
abstract fun canGrow(pos: BlockPos, world: BlockGetter): Boolean
|
||||
abstract fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
|
||||
if (world.random.nextInt(growthChance) == 0 && canGrow(state, pos, world)) {
|
||||
val block = nextStage
|
||||
|
||||
if (block != null) {
|
||||
val newState = block.defaultBlockState().setValue(FACING, state.getValue(FACING)) as BlockState
|
||||
var newState = block.defaultBlockState()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
for (property in state.properties) {
|
||||
if (newState.hasProperty(property)) {
|
||||
newState = newState.setValue(property as Property<Comparable<Any>>, state.getValue(property) as Comparable<Any>)
|
||||
}
|
||||
}
|
||||
|
||||
world.setBlockAndUpdate(pos, newState)
|
||||
}
|
||||
}
|
||||
@@ -155,14 +164,4 @@ abstract class GrowableStoneBlock(
|
||||
else -> upShape
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.EggBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.AirItem
|
||||
import net.minecraft.world.item.EggItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.PickaxeItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class IncubatorBlock(settings: Properties) : BaseEntityBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val FACING: DirectionProperty = DirectionProperty.create("facing", { dir -> dir.axis.isHorizontal })
|
||||
val HAS_EGG: BooleanProperty = BooleanProperty.create("has_egg")
|
||||
val CODEC: MapCodec<IncubatorBlock> = simpleCodec(::IncubatorBlock) as MapCodec<IncubatorBlock>
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, net.minecraft.core.Direction.NORTH)
|
||||
.setValue(HAS_EGG, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
|
||||
return Shapes.or(
|
||||
// Base
|
||||
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0),
|
||||
// Top
|
||||
Shapes.box(2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0, 1.0, 14.0 / 16.0),
|
||||
// Unified Glass Section (Covers all four glass sides as one box)
|
||||
Shapes.box(2.5 / 16.0, 3.0 / 16.0, 2.5 / 16.0, 13.5 / 16.0, 14.0 / 16.0, 13.5 / 16.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
if (placer is Player) {
|
||||
blockEntity?.ownerUUID = placer.uuid
|
||||
}
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, HAS_EGG)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): IncubatorBlockEntity {
|
||||
return IncubatorBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
// Ensure this logic only runs on the server
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS // Client-side only needs to show the hand animation
|
||||
}
|
||||
|
||||
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL // Cast safely to ServerPlayer
|
||||
|
||||
val entity = level.getBlockEntity(blockPos) as? IncubatorBlockEntity ?: return InteractionResult.FAIL
|
||||
|
||||
val egg = entity.getItem(0).get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
|
||||
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
|
||||
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
// if the person interacting is not the owner of one of the parents
|
||||
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
return blockEntity?.getComparatorOutput() ?: 0
|
||||
}
|
||||
|
||||
|
||||
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
|
||||
// Retrieve the block entity at the given position
|
||||
val blockEntity = level.getBlockEntity(pos) as? IncubatorBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
|
||||
|
||||
// Check if the player is the owner of the nest
|
||||
val isOwner = blockEntity.ownerUUID == player.uuid
|
||||
|
||||
// Check if there's an egg present in the nest
|
||||
val hasEgg = !blockEntity.getItem(0).isEmpty
|
||||
|
||||
return if (!isOwner && hasEgg) {
|
||||
// Reduce destroy speed by 80%, making it 5 times harder to break
|
||||
super.getDestroyProgress(state, player, level, pos) * 0.2f
|
||||
} else {
|
||||
// Use the default destroy speed
|
||||
super.getDestroyProgress(state, player, level, pos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
val entity = level.getBlockEntity(pos) as? IncubatorBlockEntity
|
||||
val itemStack = player.mainHandItem
|
||||
|
||||
entity?.let {
|
||||
if (!player.isCreative) {
|
||||
it.dropItem(level, pos, state)
|
||||
if (itemStack.item is PickaxeItem) {
|
||||
val itemEntity = ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.INCUBATOR.defaultInstance)
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(level, pos, state, player)
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
||||
return createTickerHelper(blockEntityType, CobblemonBlockEntities.INCUBATOR, IncubatorBlockEntity.TICKER)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.IncubatorBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.NestBlockEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.RenderShape
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.PickaxeItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class NestBlock(val variant: NestVariant, properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
|
||||
|
||||
init {
|
||||
registerDefaultState(
|
||||
stateDefinition.any()
|
||||
.setValue(BlockStateProperties.WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
enum class NestVariant(val id: String) : StringRepresentable {
|
||||
STICK("nest_stick"),
|
||||
LEAF("nest_leaf"),
|
||||
STONE("nest_stone"),
|
||||
DEEPSLATE("nest_deepslate"),
|
||||
BASALT("nest_basalt"),
|
||||
SNOW("nest_snow"),
|
||||
NETHER("nest_nether"),
|
||||
SOUL_SAND("nest_soul_sand"),
|
||||
WARM_OCEAN("nest_warm_ocean"),
|
||||
SEAGRASS("nest_seagrass"),
|
||||
LUSH("nest_lush"),
|
||||
HAY("nest_hay"),
|
||||
SAND("nest_sand"),
|
||||
RED_SAND("nest_red_sand"),
|
||||
CHORUS("nest_chorus"),
|
||||
|
||||
CAVE("cave_nest"),
|
||||
//NETHER("nether_nest"),
|
||||
WATER("water_nest"),
|
||||
BASIC("basic_nest");
|
||||
|
||||
override fun getSerializedName() = id
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext): VoxelShape {
|
||||
return when (variant) {
|
||||
NestVariant.STICK -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base
|
||||
Shapes.box(2.8 / 16.0, 0.0 / 16.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
|
||||
Shapes.box(12.0 / 16.0, 3.0 / 16.0, 11.5 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 15.5 / 16.0), // Stick 1
|
||||
Shapes.box(13.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 12.0 / 16.0), // Stick 2
|
||||
Shapes.box(1.5 / 16.0, 2.0 / 16.0, 12.0 / 16.0, 4.5 / 16.0, 6.0 / 16.0, 12.0 / 16.0), // Stick 3
|
||||
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 1.0 / 16.0, 9.0 / 16.0), // Stick 4
|
||||
Shapes.box(4.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 11.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Stick 5
|
||||
Shapes.box(9.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0, 15.0 / 16.0, 3.0 / 16.0, 6.0 / 16.0), // Stick 6
|
||||
Shapes.box(6.0 / 16.0, 3.0 / 16.0, 0.5 / 16.0, 6.0 / 16.0, 6.0 / 16.0, 4.5 / 16.0) // Stick 7
|
||||
)
|
||||
NestVariant.LEAF -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.2 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 4.2 / 16.0, 13.0 / 16.0), // Base of the nest
|
||||
Shapes.box(2.8 / 16.0, 0.0, 2.8 / 16.0, 13.2 / 16.0, 4.4 / 16.0, 13.2 / 16.0), // Hat layer
|
||||
Shapes.box(11.975 / 16.0, 6.0 / 16.0, 12.0 / 16.0, 11.975 / 16.0, 10.0 / 16.0, 15.0 / 16.0), // Leaf 1
|
||||
Shapes.box(12.025 / 16.0, 5.525 / 16.0, 6.0 / 16.0, 16.025 / 16.0, 5.525 / 16.0, 9.0 / 16.0), // Leaf 2
|
||||
Shapes.box(12.025 / 16.0, 8.0 / 16.0, 4.0 / 16.0, 12.025 / 16.0, 12.0 / 16.0, 7.0 / 16.0), // Leaf 3
|
||||
Shapes.box(0.0 / 16.0, 5.0 / 16.0, 11.975 / 16.0, 3.0 / 16.0, 9.0 / 16.0, 11.975 / 16.0), // Leaf 4
|
||||
Shapes.box(5.25 / 16.0, 7.75 / 16.0, 12.025 / 16.0, 9.25 / 16.0, 10.75 / 16.0, 12.025 / 16.0), // Leaf 5
|
||||
Shapes.box(6.025 / 16.0, 10.0 / 16.0, 2.25 / 16.0, 6.025 / 16.0, 14.0 / 16.0, 5.25 / 16.0), // Leaf 6
|
||||
Shapes.box(0.525 / 16.0, 8.0 / 16.0, 6.5 / 16.0, 3.525 / 16.0, 12.0 / 16.0, 6.5 / 16.0), // Leaf 7
|
||||
Shapes.box(13.975 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 15.975 / 16.0, 3.0 / 16.0, 5.0 / 16.0) // Leaf 8
|
||||
)
|
||||
NestVariant.STONE -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(10.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, 0.0 / 16.0, 5.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 9.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 5.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, -0.1 / 16.0, 11.0 / 16.0, 8.0 / 16.0, 4.9 / 16.0, 15.0 / 16.0)
|
||||
)
|
||||
NestVariant.DEEPSLATE -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.05 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 3.05 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(2.0 / 16.0, 0.0 / 16.0, 2.0 / 16.0, 6.0 / 16.0, 4.0 / 16.0, 5.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, -0.05 / 16.0, 5.0 / 16.0, 5.0 / 16.0, 5.95 / 16.0, 9.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, -0.1 / 16.0, 7.0 / 16.0, 15.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(10.0 / 16.0, -0.1 / 16.0, 14.0 / 16.0, 11.0 / 16.0, 3.9 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(8.0 / 16.0, -0.125 / 16.0, 11.0 / 16.0, 11.0 / 16.0, 6.875 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(8.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 5.0 / 16.0)
|
||||
)
|
||||
NestVariant.BASALT -> Shapes.or(
|
||||
Shapes.box(4.0 / 16.0, -0.1 / 16.0, 4.0 / 16.0, 12.0 / 16.0, 1.9 / 16.0, 12.0 / 16.0),
|
||||
Shapes.box(10.55 / 16.0, 0.0 / 16.0, 5.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
|
||||
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 10.95 / 16.0, 12.55 / 16.0, 5.0 / 16.0, 12.95 / 16.0),
|
||||
Shapes.box(2.55 / 16.0, 0.0 / 16.0, 9.95 / 16.0, 4.55 / 16.0, 5.0 / 16.0, 10.95 / 16.0),
|
||||
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 9.95 / 16.0, 13.55 / 16.0, 8.8 / 16.0, 13.95 / 16.0),
|
||||
Shapes.box(9.55 / 16.0, -0.2 / 16.0, 0.95 / 16.0, 13.55 / 16.0, 3.8 / 16.0, 4.95 / 16.0),
|
||||
Shapes.box(0.55 / 16.0, -0.2 / 16.0, 5.95 / 16.0, 4.55 / 16.0, 5.8 / 16.0, 9.95 / 16.0),
|
||||
Shapes.box(1.55 / 16.0, -0.2 / 16.0, 2.95 / 16.0, 4.55 / 16.0, 8.8 / 16.0, 5.95 / 16.0),
|
||||
Shapes.box(3.45 / 16.0, -0.2 / 16.0, 11.05 / 16.0, 6.45 / 16.0, 11.8 / 16.0, 14.05 / 16.0),
|
||||
Shapes.box(11.55 / 16.0, -0.1 / 16.0, 4.95 / 16.0, 14.55 / 16.0, 5.9 / 16.0, 7.95 / 16.0)
|
||||
)
|
||||
NestVariant.SNOW -> Shapes.or(
|
||||
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0, 12.0 / 16.0, 4.0 / 16.0, 11.0 / 16.0),
|
||||
Shapes.box(3.0 / 16.0, 1.1 / 16.0, 3.0 / 16.0, 13.0 / 16.0, 6.1 / 16.0, 13.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, 1.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0),
|
||||
Shapes.box(1.0 / 16.0, 1.0 / 16.0, 8.0 / 16.0, 5.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(5.0 / 16.0, 5.0 / 16.0, 11.0 / 16.0, 7.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0),
|
||||
Shapes.box(0.25 / 16.0, 7.6 / 16.0, 10.0 / 16.0, 5.25 / 16.0, 14.6 / 16.0, 10.0 / 16.0),
|
||||
Shapes.box(13.5 / 16.0, 4.6 / 16.0, 6.0 / 16.0, 16.5 / 16.0, 8.6 / 16.0, 6.0 / 16.0),
|
||||
Shapes.box(11.0 / 16.0, 4.6 / 16.0, 12.75 / 16.0, 11.0 / 16.0, 10.6 / 16.0, 16.75 / 16.0),
|
||||
Shapes.box(0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 16.0 / 16.0)
|
||||
)
|
||||
NestVariant.CAVE -> Shapes.or(
|
||||
Shapes.box(0.25, 0.0, 0.25, 0.75, 0.375, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 0.875)
|
||||
)
|
||||
NestVariant.NETHER -> Shapes.or(
|
||||
Shapes.box(2.0 / 16.0, 0.0, 2.0 / 16.0, 14.0 / 16.0, 2.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(0.0 / 16.0, 0.0, 6.0 / 16.0, 4.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0), // Left extension
|
||||
Shapes.box(7.0 / 16.0, 0.0, 0.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0), // Front extension
|
||||
Shapes.box(11.0 / 16.0, 0.0, 11.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0) // Back right extension
|
||||
)
|
||||
NestVariant.WATER -> Shapes.box(0.2, 0.0, 0.2, 0.8, 0.3, 0.8)
|
||||
NestVariant.BASIC -> Shapes.box(0.1, 0.0, 0.1, 0.9, 0.45, 0.9)
|
||||
NestVariant.SOUL_SAND -> Shapes.or(
|
||||
Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 15.0 / 16.0), // Base layer
|
||||
Shapes.box(2.0 / 16.0, 2.0 / 16.0, 2.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Inner rim
|
||||
Shapes.box(12.0 / 16.0, 0.0 / 16.0, 10.0 / 16.0, 16.0 / 16.0, 8.0 / 16.0, 14.0 / 16.0), // Side bump 1
|
||||
Shapes.box(3.0 / 16.0, 0.0 / 16.0, 13.0 / 16.0, 6.0 / 16.0, 7.0 / 16.0, 16.0 / 16.0), // Side bump 2
|
||||
Shapes.box(1.0 / 16.0, 2.0 / 16.0, 3.0 / 16.0, 4.0 / 16.0, 6.0 / 16.0, 6.0 / 16.0) // Side bump 3
|
||||
)
|
||||
NestVariant.WARM_OCEAN -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 13.0 / 16.0)
|
||||
NestVariant.SEAGRASS -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 14.0 / 16.0), // Main base
|
||||
Shapes.box(1.0 / 16.0, 0.0, 7.0 / 16.0, 5.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0), // Side protrusion 1
|
||||
Shapes.box(5.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 2.0 / 16.0, 9.0 / 16.0), // Side protrusion 2
|
||||
Shapes.box(6.0 / 16.0, 0.0, 13.0 / 16.0, 16.0 / 16.0, 6.0 / 16.0, 14.0 / 16.0) // Backside bump
|
||||
)
|
||||
NestVariant.LUSH -> Shapes.or(
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 12.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Main base
|
||||
Shapes.box(0.0 / 16.0, 0.0, 0.0 / 16.0, 4.0 / 16.0, 4.0 / 16.0, 12.0 / 16.0), // Side extension 1
|
||||
Shapes.box(0.0 / 16.0, 0.0, 12.0 / 16.0, 12.0 / 16.0, 6.0 / 16.0, 16.0 / 16.0), // Side extension 2
|
||||
Shapes.box(12.0 / 16.0, 0.0, 8.0 / 16.0, 16.0 / 16.0, 4.0 / 16.0, 16.0 / 16.0), // Side extension 3
|
||||
Shapes.box(12.0 / 16.0, 0.0, 0.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 8.0 / 16.0) // Side extension 4
|
||||
)
|
||||
NestVariant.HAY -> Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 13.0 / 16.0, 4.0 / 16.0, 13.0 / 16.0)
|
||||
NestVariant.SAND -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
|
||||
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
|
||||
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
|
||||
)
|
||||
NestVariant.RED_SAND -> Shapes.or(
|
||||
Shapes.box(3.0 / 16.0, 0.0, 3.0 / 16.0, 14.0 / 16.0, 4.0 / 16.0, 14.0 / 16.0), // Main nest base
|
||||
Shapes.box(13.0 / 16.0, 0.0, 12.0 / 16.0, 16.0 / 16.0, 5.0 / 16.0, 16.0 / 16.0), // Large rock back right
|
||||
Shapes.box(10.0 / 16.0, 0.0, 12.0 / 16.0, 13.0 / 16.0, 3.0 / 16.0, 16.0 / 16.0), // Medium rock back center
|
||||
Shapes.box(14.0 / 16.0, 0.0, 9.0 / 16.0, 16.0 / 16.0, 2.0 / 16.0, 12.0 / 16.0), // Small rock right side
|
||||
Shapes.box(4.0 / 16.0, 0.0, 0.0 / 16.0, 7.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0) // Small rock front left
|
||||
)
|
||||
NestVariant.CHORUS -> Shapes.box(1.0 / 16.0, 0.0, 1.0 / 16.0, 15.0 / 16.0, 4.0 / 16.0, 15.0 / 16.0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, isMoving: Boolean) {
|
||||
super.onPlace(state, level, pos, oldState, isMoving)
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(BlockStateProperties.WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun neighborChanged(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
block: Block,
|
||||
fromPos: BlockPos,
|
||||
isMoving: Boolean
|
||||
) {
|
||||
super.neighborChanged(state, level, pos, block, fromPos, isMoving)
|
||||
|
||||
// Check if the block below is air
|
||||
val blockBelowPos = pos.below()
|
||||
if (fromPos == blockBelowPos && level.getBlockState(blockBelowPos).isAir) {
|
||||
level.destroyBlock(pos, false)
|
||||
}
|
||||
|
||||
checkAndBreakIfSubmerged(state, level, pos)
|
||||
}
|
||||
|
||||
private fun checkAndBreakIfSubmerged(state: BlockState, levelAccessor: LevelAccessor, pos: BlockPos) {
|
||||
val fluidState = levelAccessor.getFluidState(pos)
|
||||
|
||||
if ((fluidState.type == Fluids.WATER || fluidState.isSource) && !canBeWaterlogged()) {
|
||||
if (levelAccessor is Level) {
|
||||
levelAccessor.destroyBlock(pos, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun canBeWaterlogged(): Boolean {
|
||||
return variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
// Ensure this logic only runs on the server
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
val serverPlayer = player as? ServerPlayer ?: return InteractionResult.FAIL
|
||||
|
||||
val entity = level.getBlockEntity(blockPos) as? NestBlockEntity ?: return InteractionResult.FAIL
|
||||
|
||||
val egg = entity.getItem().get(CobblemonItemComponents.EGG_DATA)?.egg
|
||||
|
||||
// if the player interacting with the nest is not the owner of the nest we do not want them taking the egg or hatching
|
||||
if (egg != null && entity.ownerUUID != player.uuid && entity.ownerUUID != null){
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
// if the parent owner ID is null then something is wrong (maybe something happened to one of the parent datas)
|
||||
if (egg != null && egg?.parentOne?.getOwnerUUID() == null || egg?.parentTwo?.getOwnerUUID() == null) {
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
// if the person interacting is not the owner of one of the parents
|
||||
if (egg != null && player.uuid != egg?.parentOne?.getOwnerUUID() || player.uuid != egg?.parentTwo?.getOwnerUUID()) {
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
return entity.onUse(blockState, level, blockPos, serverPlayer, InteractionHand.MAIN_HAND, blockHitResult)
|
||||
}
|
||||
|
||||
override fun getDestroyProgress(state: BlockState, player: Player, level: BlockGetter, pos: BlockPos): Float {
|
||||
// Retrieve the block entity at the given position
|
||||
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity ?: return super.getDestroyProgress(state, player, level, pos)
|
||||
|
||||
// Check if the player is the owner of the nest
|
||||
val isOwner = blockEntity.ownerUUID == player.uuid
|
||||
|
||||
// Check if there's an egg present in the nest
|
||||
val hasEgg = !blockEntity.getItem(0).isEmpty
|
||||
|
||||
return if (!isOwner && hasEgg) {
|
||||
// Reduce destroy speed making it 5 times harder to break
|
||||
super.getDestroyProgress(state, player, level, pos) * 0.2f
|
||||
} else {
|
||||
super.getDestroyProgress(state, player, level, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// todo we want destroying to take longer if it is not their nest
|
||||
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
val entity = level.getBlockEntity(pos) as? NestBlockEntity
|
||||
|
||||
// todo we do silk touch now
|
||||
/*entity?.let {
|
||||
if (!player.isCreative) {
|
||||
val itemEntity = when (this.variant) {
|
||||
NestVariant.BASIC -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.BASE_NEST.defaultInstance)
|
||||
NestVariant.WATER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.WATER_NEST.defaultInstance)
|
||||
NestVariant.CAVE -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.CAVE_NEST.defaultInstance)
|
||||
NestVariant.NETHER -> ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, CobblemonItems.NETHER_NEST.defaultInstance)
|
||||
}
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
it.dropItem( level, pos, state)
|
||||
}*/
|
||||
|
||||
return super.playerWillDestroy(level, pos, state, player)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState) = NestBlockEntity(pos, state)
|
||||
|
||||
override fun getRenderShape(blockState: BlockState) = RenderShape.MODEL
|
||||
|
||||
override fun canPlaceLiquid(
|
||||
player: Player?,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
fluid: Fluid
|
||||
): Boolean {
|
||||
return (variant == NestVariant.WATER || variant == NestVariant.SEAGRASS || variant == NestVariant.WARM_OCEAN) && super.canPlaceLiquid(player, level, pos, state, fluid)
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state.getValue(BlockStateProperties.WATERLOGGED)) {
|
||||
Fluids.WATER.getSource(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? NestBlockEntity
|
||||
if (placer is Player) {
|
||||
blockEntity?.ownerUUID = placer.uuid
|
||||
}
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
}
|
||||
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
val fluidState = ctx.level.getFluidState(ctx.clickedPos)
|
||||
|
||||
// If the nest variant can be waterlogged, set it accordingly
|
||||
if (canBeWaterlogged()) {
|
||||
return defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, fluidState.type == Fluids.WATER)
|
||||
}
|
||||
|
||||
// Prevent placement if the block is submerged in water
|
||||
if (!canBeWaterlogged() && fluidState.type == Fluids.WATER) {
|
||||
return null
|
||||
}
|
||||
|
||||
return defaultBlockState()
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
||||
return createTickerHelper(blockEntityType, CobblemonBlockEntities.NEST, NestBlockEntity.TICKER)
|
||||
}
|
||||
|
||||
|
||||
override fun codec(): MapCodec<NestBlock> {
|
||||
return RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.fieldOf("variant").forGetter { block -> block.variant.id }, // Serializes the variant
|
||||
Codec.BOOL.optionalFieldOf("waterlogged", false).forGetter { block ->
|
||||
block.defaultBlockState().getValue(BlockStateProperties.WATERLOGGED)
|
||||
}
|
||||
).apply(instance) { variant, waterlogged ->
|
||||
val block = NestBlock(NestVariant.valueOf(variant.uppercase()), Properties.of().noOcclusion())
|
||||
block.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, waterlogged)
|
||||
block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<TMShelfBlock> = simpleCodec(::TMShelfBlock)
|
||||
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
|
||||
val SLOT_OCCUPIED_PROPERTIES = (0 until 14).map {
|
||||
BooleanProperty.create("slot_${it}_occupied")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val baseState = stateDefinition.any().setValue(FACING, Direction.NORTH)
|
||||
registerDefaultState(SLOT_OCCUPIED_PROPERTIES.fold(baseState) { state, prop ->
|
||||
state.setValue(prop, false)
|
||||
})
|
||||
}
|
||||
|
||||
override fun codec() = CODEC
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return TMShelfBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
SLOT_OCCUPIED_PROPERTIES.forEach { builder.add(it) }
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState {
|
||||
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState, level: Level, pos: BlockPos,
|
||||
player: Player, hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return InteractionResult.PASS
|
||||
return entity.handleUseWithoutItem(state, level, pos, player, hit)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack, state: BlockState, level: Level, pos: BlockPos,
|
||||
player: Player, hand: InteractionHand, hit: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
return entity.handleUseItem(stack, state, level, pos, player, hit)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState) = true
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
|
||||
return entity?.lastInteractedSlot?.plus(1) ?: 0
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState, level: BlockGetter,
|
||||
pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
|
||||
): VoxelShape = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
@@ -44,15 +44,25 @@ class TumblestoneBlock(
|
||||
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
|
||||
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
|
||||
).apply(it, ::TumblestoneBlock) }
|
||||
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
|
||||
override val growthChance = 5
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.DOWN)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun canGrow(pos: BlockPos, world: BlockGetter): Boolean {
|
||||
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
|
||||
if (stage == MAX_STAGE) return false
|
||||
val iterator: Iterator<BlockPos> =
|
||||
BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))
|
||||
@@ -66,6 +76,8 @@ class TumblestoneBlock(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
|
||||
|
||||
override fun codec(): MapCodec<out DirectionalBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
|
||||
class TypeGemBlock(
|
||||
settings: Properties,
|
||||
stage: Int,
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<TypeGemBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
PrimitiveCodec.INT.fieldOf("stage").forGetter { it.stage },
|
||||
PrimitiveCodec.INT.fieldOf("height").forGetter { it.height },
|
||||
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
|
||||
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
|
||||
).apply(it, ::TypeGemBlock) }
|
||||
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val MAX_STAGE = STAGE_1
|
||||
|
||||
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
|
||||
val FACING: DirectionProperty = DirectionalBlock.FACING
|
||||
}
|
||||
|
||||
override val growthChance = 1
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.DOWN) // Ensure facing is properly registered
|
||||
.setValue(SHOULD_GROW, true)
|
||||
)
|
||||
}
|
||||
|
||||
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
|
||||
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean {
|
||||
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
|
||||
}
|
||||
|
||||
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (canGrow(state, pos, level)) {
|
||||
var nextState = nextStage?.defaultBlockState()
|
||||
if (nextState != null) {
|
||||
if (nextState.hasProperty(FACING)) {
|
||||
nextState = nextState.setValue(FACING, state.getValue(FACING)) // Preserve FACING for core
|
||||
}
|
||||
level.setBlockAndUpdate(pos, nextState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out DirectionalBlock?>? = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(FACING, SHOULD_GROW) // Ensure FACING is registered
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user