forked from mirrors/cobblemon
11360 lines
1001 KiB
Diff
11360 lines
1001 KiB
Diff
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt b/common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
|
|
index 1178252d98..ce511a54e2 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/Cobblemon.kt
|
|
@@ -62,6 +62,8 @@ 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.TMMoveMongoBackend
|
|
+import com.cobblemon.mod.common.api.storage.player.adapter.TMMoveNbtBackend
|
|
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
|
|
@@ -109,13 +111,7 @@ import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
|
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
|
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
|
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
|
-import com.cobblemon.mod.common.util.DataKeys
|
|
-import com.cobblemon.mod.common.util.cobblemonResource
|
|
-import com.cobblemon.mod.common.util.ifDedicatedServer
|
|
-import com.cobblemon.mod.common.util.isLaterVersion
|
|
-import com.cobblemon.mod.common.util.party
|
|
-import com.cobblemon.mod.common.util.requestWallpapers
|
|
-import com.cobblemon.mod.common.util.server
|
|
+import com.cobblemon.mod.common.util.*
|
|
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
|
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
|
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
|
@@ -244,6 +240,8 @@ object Cobblemon {
|
|
|
|
DATA_SYNCHRONIZED.subscribe {
|
|
storage.onPlayerDataSync(it)
|
|
+ it.tmList().scheduleFullSyncFromStores(it.party(), it.pc())
|
|
+ it.pokedex().scheduleFullSyncFromStores(it.party(), it.pc())
|
|
playerDataManager.syncAllToPlayer(it)
|
|
starterHandler.handleJoin(it)
|
|
it.requestWallpapers()
|
|
@@ -306,6 +304,7 @@ object Cobblemon {
|
|
this.config.maxPokemonLevel,
|
|
this.config.maxPokemonFriendship,
|
|
this.config.maxDynamaxLevel,
|
|
+ this.config.unlockAllMoveDexMovesByDefault
|
|
).sendToPlayer(player)
|
|
}
|
|
|
|
@@ -375,8 +374,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)
|
|
@@ -403,8 +406,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.")
|
|
@@ -588,6 +597,7 @@ object Cobblemon {
|
|
this.implementation.registerCommandArgument(cobblemonResource("mark"), MarkArgumentType::class, SingletonArgumentInfo.contextFree(MarkArgumentType::mark))
|
|
this.implementation.registerCommandArgument(cobblemonResource("transform_type"), TransformTypeArgumentType::class, SingletonArgumentInfo.contextFree(TransformTypeArgumentType::transformType))
|
|
this.implementation.registerCommandArgument(cobblemonResource("model_part"), ModelPartArgumentType::class, SingletonArgumentInfo.contextFree(ModelPartArgumentType::modelPart))
|
|
+ this.implementation.registerCommandArgument(cobblemonResource("tm"), TmArgumentType::class, SingletonArgumentInfo.contextFree(TmArgumentType::tm))
|
|
}
|
|
|
|
}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
|
|
index d6f751cb7d..bf3622c86c 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
|
|
@@ -39,6 +39,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))
|
|
|
|
@@ -48,6 +51,8 @@ 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_MACHINE: BlockEntityType<TMMachineBlockEntity> = this.create("tm_machine", BlockEntityType.Builder.of(::TMMachineBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
|
|
|
@JvmField
|
|
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
|
|
index c330a9c7dc..a185e7bf84 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
|
|
@@ -22,6 +22,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.tmmachine.TMMachineBlock
|
|
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
|
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
|
import com.cobblemon.mod.common.mixin.invoker.*
|
|
@@ -249,31 +250,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
|
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
|
|
|
|
@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)))
|
|
@@ -282,6 +283,87 @@ 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).requiresCorrectToolForDrops()))
|
|
+
|
|
+ private val typeGemBlocks = mutableMapOf<ResourceLocation, TypeGemBlock>()
|
|
+
|
|
+ @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("normal_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("type_gem_cluster_fire", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_WATER = typeGemCluster("type_gem_cluster_water", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("type_gem_cluster_electric", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("type_gem_cluster_grass", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_ICE = typeGemCluster("type_gem_cluster_ice", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("type_gem_cluster_fighting", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_POISON = typeGemCluster("type_gem_cluster_poison", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("type_gem_cluster_ground", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("type_gem_cluster_flying", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("type_gem_cluster_psychic", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_BUG = typeGemCluster("type_gem_cluster_bug", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("type_gem_cluster_rock", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("type_gem_cluster_ghost", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("type_gem_cluster_dragon", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_DARK = typeGemCluster("type_gem_cluster_dark", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("type_gem_cluster_steel", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"))
|
|
+ @JvmField
|
|
+ val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("type_gem_cluster_fairy", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"))
|
|
+
|
|
@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
|
|
@@ -456,6 +538,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
|
@JvmField
|
|
val SOUL_CAMPFIRE = create("soul_campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{9}, true))
|
|
|
|
+
|
|
@JvmField
|
|
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
|
@JvmField
|
|
@@ -584,9 +667,25 @@ 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)) 13 else 0 }))
|
|
|
|
+ val TM_MACHINE = create(
|
|
+ "tm_machine",
|
|
+ TMMachineBlock(
|
|
+ BlockBehaviour.Properties.of()
|
|
+ .mapColor(MapColor.COLOR_GREEN)
|
|
+ .sound(SoundType.METAL)
|
|
+ .pushReaction(PushReaction.BLOCK)
|
|
+ .strength(2F)
|
|
+ .noOcclusion()
|
|
+ )
|
|
+ )
|
|
+
|
|
+
|
|
@JvmField
|
|
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
|
|
|
|
+ @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).requiresCorrectToolForDrops()))
|
|
|
|
@@ -890,4 +989,28 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
|
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
|
|
return this.create(name, block)
|
|
}
|
|
+
|
|
+ private fun typeGemBlock(name: String): TypeGemBlock {
|
|
+ val typeGemBlock = this.create(name, TypeGemBlock(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))
|
|
+
|
|
+ TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
|
|
+
|
|
+ val identifier = cobblemonResource(name)
|
|
+ typeGemClusters[identifier] = gemClusterBlock
|
|
+
|
|
+ return gemClusterBlock
|
|
+ }
|
|
+
|
|
+ fun typeGemClusters() = typeGemClusters.toMap()
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
|
|
index 095bb58e4f..7ba55575bf 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
|
|
@@ -71,10 +71,12 @@ object CobblemonCommands {
|
|
MarkGiveAllCommand.register(dispatcher)
|
|
PcSearchCommand.register(dispatcher)
|
|
PcTakeCommand.register(dispatcher)
|
|
+ GiveTmCommand.register(dispatcher)
|
|
+ TmCommand.register(dispatcher)
|
|
|
|
// Possibly lock down registration if and only if under dev environment or running in an environment
|
|
// with a certain system environment variable set
|
|
CobblemonInfoCommand.register(dispatcher)
|
|
CalculateSeatPositionCommand.register(dispatcher)
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonImplementation.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonImplementation.kt
|
|
index cafec5cb04..79c07adc50 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonImplementation.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonImplementation.kt
|
|
@@ -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
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
|
|
index b3cf065118..bb3997bf27 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
|
|
@@ -69,6 +69,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
|
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
|
.build())
|
|
|
|
+ @JvmField
|
|
+ val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
|
|
+ .persistent(TMMoveComponent.CODEC)
|
|
+ .networkSynchronized(TMMoveComponent.PACKET_CODEC)
|
|
+ .build())
|
|
+
|
|
@JvmField
|
|
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
|
|
.persistent(IngredientComponent.CODEC)
|
|
@@ -101,6 +107,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
|
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
|
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ride_boosts"), RIDE_BOOST)
|
|
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
|
+ Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
|
|
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)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
|
|
index f086370495..3fedfb24d2 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
|
|
@@ -457,6 +457,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)
|
|
@@ -1208,6 +1212,83 @@ 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_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(),
|
|
@@ -1432,44 +1513,49 @@ 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(Properties()))
|
|
+ @JvmField
|
|
+ val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
|
|
|
|
// Type Gems
|
|
@JvmField
|
|
- val NORMAL_GEM = noSettingsItem("normal_gem")
|
|
+ val NORMAL_GEM = this.create("normal_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL, Item.Properties()))
|
|
@JvmField
|
|
- val FIRE_GEM = noSettingsItem("fire_gem")
|
|
+ val FIRE_GEM = this.create("fire_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE, Item.Properties()))
|
|
@JvmField
|
|
- val WATER_GEM = noSettingsItem("water_gem")
|
|
+ val WATER_GEM = this.create("water_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_WATER, Item.Properties()))
|
|
@JvmField
|
|
- val GRASS_GEM = noSettingsItem("grass_gem")
|
|
+ val GRASS_GEM = this.create("grass_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS, Item.Properties()))
|
|
@JvmField
|
|
- val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
|
+ val ELECTRIC_GEM = this.create("electric_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC, Item.Properties()))
|
|
@JvmField
|
|
- val ICE_GEM = noSettingsItem("ice_gem")
|
|
+ val ICE_GEM = this.create("ice_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ICE, Item.Properties()))
|
|
@JvmField
|
|
- val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
|
+ val FIGHTING_GEM = this.create("fighting_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING, Item.Properties()))
|
|
@JvmField
|
|
- val POISON_GEM = noSettingsItem("poison_gem")
|
|
+ val POISON_GEM = this.create("poison_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_POISON, Item.Properties()))
|
|
@JvmField
|
|
- val GROUND_GEM = noSettingsItem("ground_gem")
|
|
+ val GROUND_GEM = this.create("ground_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND, Item.Properties()))
|
|
@JvmField
|
|
- val FLYING_GEM = noSettingsItem("flying_gem")
|
|
+ val FLYING_GEM = this.create("flying_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING, Item.Properties()))
|
|
@JvmField
|
|
- val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
|
+ val PSYCHIC_GEM = this.create("psychic_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC, Item.Properties()))
|
|
@JvmField
|
|
- val BUG_GEM = noSettingsItem("bug_gem")
|
|
+ val BUG_GEM = this.create("bug_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_BUG, Item.Properties()))
|
|
@JvmField
|
|
- val ROCK_GEM = noSettingsItem("rock_gem")
|
|
+ val ROCK_GEM = this.create("rock_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK, Item.Properties()))
|
|
@JvmField
|
|
- val GHOST_GEM = noSettingsItem("ghost_gem")
|
|
+ val GHOST_GEM = this.create("ghost_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST, Item.Properties()))
|
|
@JvmField
|
|
- val DRAGON_GEM = noSettingsItem("dragon_gem")
|
|
+ val DRAGON_GEM = this.create("dragon_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON, Item.Properties()))
|
|
@JvmField
|
|
- val DARK_GEM = noSettingsItem("dark_gem")
|
|
+ val DARK_GEM = this.create("dark_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DARK, Item.Properties()))
|
|
@JvmField
|
|
- val STEEL_GEM = noSettingsItem("steel_gem")
|
|
+ val STEEL_GEM = this.create("steel_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL, Item.Properties()))
|
|
@JvmField
|
|
- val FAIRY_GEM = noSettingsItem("fairy_gem")
|
|
+ val FAIRY_GEM = this.create("fairy_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY, Item.Properties()))
|
|
+
|
|
//@JvmField
|
|
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
|
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMenuType.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMenuType.kt
|
|
index 894732b7eb..5c0c7b9ba7 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMenuType.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMenuType.kt
|
|
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
|
|
|
|
|
|
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
import com.cobblemon.mod.common.platform.PlatformRegistry
|
|
import net.minecraft.core.Registry
|
|
import net.minecraft.core.registries.BuiltInRegistries
|
|
@@ -19,7 +20,8 @@ import net.minecraft.world.inventory.MenuType
|
|
|
|
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
|
|
|
- val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
|
+ val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
|
|
+ val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
|
|
|
|
override val registry: Registry<MenuType<*>>
|
|
get() = BuiltInRegistries.MENU
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
|
|
index 8f7fec94ee..f33b84a9a8 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
|
|
@@ -25,15 +25,15 @@ import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
|
|
import com.cobblemon.mod.common.client.net.debug.OpenRidingStatsDebugGUIHandler
|
|
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
|
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
|
+import com.cobblemon.mod.common.client.net.effect.*
|
|
+import com.cobblemon.mod.common.client.net.gui.*
|
|
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
|
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
|
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
|
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.ExpGainedDataPacketHandler
|
|
-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
|
|
+import com.cobblemon.mod.common.client.net.gui.*
|
|
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
|
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
|
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
|
|
@@ -137,9 +137,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPac
|
|
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
|
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
|
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
|
|
-import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
|
-import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
|
-import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.*
|
|
import com.cobblemon.mod.common.net.messages.server.*
|
|
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
|
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
|
@@ -150,6 +148,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
|
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
|
|
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.block.TMMachineTeachMovePacket
|
|
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
|
|
@@ -205,6 +204,7 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHan
|
|
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
|
+import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
|
|
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
|
|
@@ -228,6 +228,8 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
|
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
|
+import com.cobblemon.mod.common.net.serverhandling.pokemon.update.*
|
|
+import com.cobblemon.mod.common.net.serverhandling.pokemon.update.*
|
|
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
|
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
|
@@ -570,6 +572,7 @@ object CobblemonNetwork {
|
|
|
|
// Block entity
|
|
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
|
|
+ list.add(PacketRegisterInfo(TMMachineTeachMovePacket.ID, TMMachineTeachMovePacket::decode, TMMachineTeachMoveHandler))
|
|
|
|
// Move select packets
|
|
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
|
|
@@ -606,6 +609,10 @@ object CobblemonNetwork {
|
|
// Cooking
|
|
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
|
|
|
+ // TM Machine
|
|
+ list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
|
|
+ list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
|
|
+
|
|
// Behaviour Packets
|
|
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
|
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
|
|
index 7faf143251..eb1c789f08 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
|
|
@@ -200,6 +200,22 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
|
@JvmField
|
|
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
|
|
|
|
+ @JvmField
|
|
+ val TM_USE = this.create("item.tm.use")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_OFF = this.create("block.tm_machine.off")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_ON = this.create("block.tm_machine.on")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
|
|
+ @JvmField
|
|
+ val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
|
|
+
|
|
+
|
|
@JvmField
|
|
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
|
|
@JvmField
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/advancement/criterion/PlantTumblestoneCriterionCondition.kt b/common/src/main/kotlin/com/cobblemon/mod/common/advancement/criterion/PlantTumblestoneCriterionCondition.kt
|
|
index ee266cfd9b..af2d0dbcc6 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/advancement/criterion/PlantTumblestoneCriterionCondition.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/advancement/criterion/PlantTumblestoneCriterionCondition.kt
|
|
@@ -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)
|
|
\ No newline at end of file
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/BenchedMove.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/BenchedMove.kt
|
|
index 377fc7c55e..73a0fd25d4 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/BenchedMove.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/BenchedMove.kt
|
|
@@ -24,6 +24,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
|
|
|
class BenchedMoves : Iterable<BenchedMove> {
|
|
var changeFunction: ((BenchedMoves) -> Unit) = {}
|
|
+// var moveAddedFunction: (BenchedMove) -> Unit = {}
|
|
private var emit = true
|
|
private val benchedMoves = mutableListOf<BenchedMove>()
|
|
|
|
@@ -58,6 +59,9 @@ class BenchedMoves : Iterable<BenchedMove> {
|
|
return false
|
|
}
|
|
|
|
+ /*if (emit) {
|
|
+ moveAddedFunction(benchedMove)
|
|
+ }*/
|
|
doThenEmit { benchedMoves.add(benchedMove) }
|
|
return true
|
|
}
|
|
@@ -174,4 +178,4 @@ data class BenchedMove(val moveTemplate: MoveTemplate, val ppRaisedStages: Int)
|
|
Codec.intRange(0, 3).fieldOf(DataKeys.POKEMON_MOVESET_RAISED_PP_STAGES).forGetter(BenchedMove::ppRaisedStages)
|
|
).apply(it, ::BenchedMove) }
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/MoveSet.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/MoveSet.kt
|
|
index a3d7a9b731..1ce93ea017 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/MoveSet.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/moves/MoveSet.kt
|
|
@@ -54,6 +54,7 @@ class MoveSet : Iterable<Move> {
|
|
*/
|
|
fun getMoves() = moves.filterNotNull()
|
|
fun getMovesWithNulls() = moves.toList()
|
|
+ fun getMoveTemplates() = getMoves().map { move -> move.template }
|
|
fun hasSpace() = moves.any { it == null }
|
|
|
|
/**
|
|
@@ -222,4 +223,4 @@ class MoveSet : Iterable<Move> {
|
|
{ moveSet -> moveSet.toList() }
|
|
)
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
|
|
index 3d0bff1e6c..3022fef269 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
|
|
@@ -143,6 +143,8 @@ object CobblemonPermissions {
|
|
val CHANGE_BOX_COUNT = this.create("${COMMAND_PREFIX}boxcount", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
|
@JvmStatic
|
|
val SPECTATE_BATTLE = this.create("${COMMAND_PREFIX}spectatebattle", PermissionLevel.ALL_COMMANDS)
|
|
+ @JvmStatic
|
|
+ val TECHNICAL_MACHINE = this.create("${COMMAND_PREFIX}technicalmachine", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
|
|
|
@JvmStatic
|
|
val SEE_HIDDEN_NPCS = this.create("seehiddennpcs", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
|
|
index cc6d681722..c7b926d1c9 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
|
|
@@ -289,11 +289,11 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
|
createDefault("ancient_great_ball", MultiplierModifier(1.5F), ancient = true)
|
|
createDefault("ancient_ultra_ball", MultiplierModifier(2F), ancient = true)
|
|
createDefault("ancient_heavy_ball", throwPower = 0.75f, ancient = true)
|
|
- createDefault("ancient_leaden_ball", MultiplierModifier(1.5F), throwPower = 0.75f, ancient = true)
|
|
- createDefault("ancient_gigaton_ball", MultiplierModifier(2F), throwPower = 0.75f, ancient = true)
|
|
+ createDefault("ancient_leaden_ball", throwPower = 0.75f, ancient = true)
|
|
+ createDefault("ancient_gigaton_ball", throwPower = 0.75f, ancient = true)
|
|
createDefault("ancient_feather_ball", throwPower = 2.5f, ancient = true)
|
|
- createDefault("ancient_wing_ball", MultiplierModifier(1.5F), throwPower = 2.5f, ancient = true)
|
|
- createDefault("ancient_jet_ball", MultiplierModifier(2F), throwPower = 2.5f, ancient = true)
|
|
+ createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
|
|
+ createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
|
|
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
|
|
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
|
|
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/AbstractPokedexManager.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/AbstractPokedexManager.kt
|
|
index 0b5bfe46d7..aa55d4056b 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/AbstractPokedexManager.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/AbstractPokedexManager.kt
|
|
@@ -29,7 +29,6 @@ abstract class AbstractPokedexManager {
|
|
|
|
fun deleteSpeciesRecord(speciesId: ResourceLocation) {
|
|
speciesRecords.remove(speciesId)
|
|
- markDirty()
|
|
}
|
|
|
|
fun deleteFormRecord(speciesId: ResourceLocation, formName: String) {
|
|
@@ -40,8 +39,6 @@ abstract class AbstractPokedexManager {
|
|
deleteSpeciesRecord(speciesId)
|
|
return
|
|
}
|
|
-
|
|
- markDirty()
|
|
}
|
|
|
|
fun getSpeciesRecord(speciesId: ResourceLocation): SpeciesDexRecord? {
|
|
@@ -164,8 +161,4 @@ abstract class AbstractPokedexManager {
|
|
return newValue
|
|
}
|
|
}
|
|
-
|
|
- open fun markDirty() {
|
|
- // Save stuff
|
|
- }
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/FormDexRecord.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/FormDexRecord.kt
|
|
index 508a7f77c5..d0334c26f3 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/FormDexRecord.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/FormDexRecord.kt
|
|
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.pokedex
|
|
|
|
import com.bedrockk.molang.runtime.struct.QueryStruct
|
|
import com.bedrockk.molang.runtime.struct.VariableStruct
|
|
+import com.bedrockk.molang.runtime.value.DoubleValue
|
|
import com.bedrockk.molang.runtime.value.StringValue
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
|
import com.cobblemon.mod.common.api.events.pokemon.PokedexDataChangedEvent
|
|
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
|
@@ -25,6 +27,7 @@ import com.google.common.collect.Sets
|
|
import com.mojang.datafixers.util.Either
|
|
import com.mojang.serialization.Codec
|
|
import com.mojang.serialization.codecs.ListCodec
|
|
+import com.mojang.serialization.codecs.PrimitiveCodec
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
|
|
@@ -41,11 +44,13 @@ class FormDexRecord {
|
|
instance.group(
|
|
ListCodec(Codec.STRING, 0, 3).fieldOf("genders").forGetter { it.genders.map { it.name } },
|
|
ListCodec(Codec.STRING, 0, 2).fieldOf("seenShinyStates").forGetter { it.seenShinyStates.toList() },
|
|
+ PrimitiveCodec.INT.optionalFieldOf("highest_level", -1).forGetter { it.highestLevel },
|
|
Codec.STRING.fieldOf("knowledge").forGetter { it.knowledge.name }
|
|
- ).apply(instance) { genders, seenShinyStates, knowledge ->
|
|
+ ).apply(instance) { genders, seenShinyStates, highestLevel, knowledge ->
|
|
FormDexRecord().also {
|
|
it.genders.addAll(genders.map(Gender::valueOf))
|
|
it.seenShinyStates.addAll(seenShinyStates)
|
|
+ it.highestLevel = highestLevel
|
|
it.knowledge = PokedexEntryProgress.valueOf(knowledge)
|
|
}
|
|
}
|
|
@@ -59,6 +64,9 @@ class FormDexRecord {
|
|
private val seenShinyStates =
|
|
mutableSetOf<String>() // consider: radiants in the future (radiants should just be a resource pack tbh)
|
|
|
|
+ var highestLevel = -1
|
|
+ private set
|
|
+
|
|
/** The current awareness of the form that the dex has. */
|
|
var knowledge = PokedexEntryProgress.NONE
|
|
private set
|
|
@@ -80,11 +88,17 @@ class FormDexRecord {
|
|
struct = QueryStruct(hashMapOf())
|
|
.addFunction("data") { data }
|
|
.addFunction("knowledge") { StringValue(knowledge.name) }
|
|
- .addFunction("has_seen_gender") { params -> genders.contains(Gender.valueOf(params.getString(0).uppercase())) }
|
|
+ .addFunction("has_seen_gender") { params -> DoubleValue(genders.contains(Gender.valueOf(params.getString(0).uppercase()))) }
|
|
+ .addFunction("highest_level") { _ -> DoubleValue(highestLevel) }
|
|
+ .addFunction("set_highest_level") { params ->
|
|
+ highestLevel = maxOf(highestLevel, params.getDouble(0).toInt())
|
|
+ DoubleValue.ONE
|
|
+ }
|
|
}
|
|
|
|
fun clone() = FormDexRecord().also {
|
|
it.genders.addAll(genders)
|
|
+ it.highestLevel = highestLevel
|
|
it.seenShinyStates.addAll(seenShinyStates)
|
|
it.knowledge = knowledge
|
|
}
|
|
@@ -114,7 +128,7 @@ class FormDexRecord {
|
|
fun addAllShinyStatesAndGenders() {
|
|
val form = PokemonSpecies.getByIdentifier(speciesDexRecord.id)?.getFormByName(formName)
|
|
genders.addAll(form?.possibleGenders ?: listOf(Gender.MALE, Gender.FEMALE))
|
|
-
|
|
+ highestLevel = Cobblemon.config.maxPokemonLevel
|
|
seenShinyStates.addAll(listOf("shiny", "normal"))
|
|
speciesDexRecord.onFormRecordUpdated(this)
|
|
}
|
|
@@ -135,6 +149,9 @@ class FormDexRecord {
|
|
),
|
|
ifSucceeded = {
|
|
genders.add(pokedexEntityData.pokemon.gender)
|
|
+ if (knowledge == PokedexEntryProgress.CAUGHT) {
|
|
+ highestLevel = maxOf(highestLevel, pokedexEntityData.pokemon.level)
|
|
+ }
|
|
seenShinyStates.add(if (pokedexEntityData.pokemon.shiny) "shiny" else "normal")
|
|
if (knowledge.ordinal > this.knowledge.ordinal) {
|
|
this.knowledge = knowledge
|
|
@@ -159,6 +176,7 @@ class FormDexRecord {
|
|
|| (pokedexEntityData.pokemon.shiny && "shiny" !in seenShinyStates)
|
|
|| (!pokedexEntityData.pokemon.shiny && "normal" !in seenShinyStates)
|
|
|| knowledge.ordinal > this.knowledge.ordinal
|
|
+ || (highestLevel < pokedexEntityData.pokemon.level && knowledge == PokedexEntryProgress.CAUGHT)
|
|
|| speciesDexRecord.wouldBeDifferent(pokedexEntityData)
|
|
}
|
|
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexManager.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexManager.kt
|
|
index b2488918b1..bd0062a506 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexManager.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/PokedexManager.kt
|
|
@@ -9,6 +9,8 @@
|
|
package com.cobblemon.mod.common.api.pokedex
|
|
|
|
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
|
+import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
|
+import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
|
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
|
|
@@ -48,10 +50,44 @@ class PokedexManager(
|
|
fun obtain(pokemon: Pokemon) {
|
|
val speciesId = pokemon.species.resourceIdentifier
|
|
val formName = pokemon.form.name
|
|
- getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).caught(PokedexEntityData(pokemon = pokemon, disguise = null))
|
|
+ getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).obtained(PokedexEntityData(pokemon = pokemon, disguise = null))
|
|
}
|
|
|
|
- override fun markDirty() {
|
|
+ fun scheduleFullSyncFromStores(
|
|
+ party: Iterable<Pokemon?>,
|
|
+ pc: Iterable<Pokemon?>,
|
|
+ batchSize: Int = 50
|
|
+ ) {
|
|
+ val iterator = sequence {
|
|
+ for (pokemon in party) {
|
|
+ if (pokemon != null) yield(pokemon)
|
|
+ }
|
|
+ for (pokemon in pc) {
|
|
+ if (pokemon != null) yield(pokemon)
|
|
+ }
|
|
+ }.iterator()
|
|
+
|
|
+ if (!iterator.hasNext()) {
|
|
+ return
|
|
+ }
|
|
+
|
|
+ ScheduledTask.Builder()
|
|
+ .tracker(ServerTaskTracker)
|
|
+ .interval(0F)
|
|
+ .infiniteIterations()
|
|
+ .execute { task ->
|
|
+ var processed = 0
|
|
+ while (processed < batchSize && iterator.hasNext()) {
|
|
+ val pokemon = iterator.next()
|
|
+ obtain(pokemon)
|
|
+ processed++
|
|
+ }
|
|
+
|
|
+ if (!iterator.hasNext()) {
|
|
+ task.expire()
|
|
+ }
|
|
+ }
|
|
+ .build()
|
|
}
|
|
|
|
override fun initialize() {
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/SpeciesDexRecord.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/SpeciesDexRecord.kt
|
|
index 639c98391f..90e31aeace 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/SpeciesDexRecord.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/SpeciesDexRecord.kt
|
|
@@ -26,7 +26,8 @@ import net.minecraft.resources.ResourceLocation
|
|
/**
|
|
* Dex recorded information about a particular Pokémon species. This includes all forms and their information.
|
|
* Across all forms there is a single list of known aspects which is generally used to track cosmetic variation
|
|
- * though technically things like gender and form-motivating aspects will get collected inadvertently.
|
|
+ * though technically things like gender and form-motivating aspects will get collected inadvertently. The highest
|
|
+ * level of the species that the player has owned is also tracked.
|
|
*
|
|
* @author Hiroku
|
|
* @since August 23rd, 2024
|
|
@@ -36,10 +37,12 @@ class SpeciesDexRecord {
|
|
val CODEC: Codec<SpeciesDexRecord> = RecordCodecBuilder.create { instance ->
|
|
instance.group(
|
|
ListCodec(PrimitiveCodec.STRING, 0, 512).fieldOf("aspects").forGetter { it.aspects.toList() },
|
|
+ PrimitiveCodec.INT.optionalFieldOf("highestLevel", -1).forGetter { it.highestLevel },
|
|
Codec.unboundedMap(Codec.STRING, FormDexRecord.CODEC).fieldOf("formRecords").forGetter { it.formRecords }
|
|
- ).apply(instance) { aspects, formRecords ->
|
|
+ ).apply(instance) { aspects, highestLevel, formRecords ->
|
|
SpeciesDexRecord().also {
|
|
it.aspects.addAll(aspects)
|
|
+ it.highestLevel = highestLevel
|
|
it.formRecords.putAll(formRecords)
|
|
}
|
|
}
|
|
@@ -48,13 +51,16 @@ class SpeciesDexRecord {
|
|
|
|
@Transient
|
|
lateinit var id: ResourceLocation
|
|
+ /** The highest level of the species that the player has owned. */
|
|
+ var highestLevel = -1
|
|
+ private set
|
|
private val aspects: MutableSet<String> = mutableSetOf()
|
|
private val formRecords: MutableMap<String, FormDexRecord> = mutableMapOf()
|
|
val isFormRecordsEmpty: Boolean
|
|
get() = formRecords.isEmpty()
|
|
|
|
fun describe(): String {
|
|
- return "SpeciesDexRecord(aspects=$aspects, formRecords=$formRecords)"
|
|
+ return "SpeciesDexRecord(aspects=$aspects, highestLevel=$highestLevel, formRecords=$formRecords)"
|
|
}
|
|
|
|
@Transient
|
|
@@ -77,7 +83,14 @@ class SpeciesDexRecord {
|
|
.addFunction("is_caught") { params ->
|
|
DoubleValue(formRecords.values.any { it.knowledge.name.equals("CAUGHT", ignoreCase = true) })
|
|
}
|
|
- // more, also worth moving to something like MoLangFunctions.kt so it's easier to API in more values
|
|
+ .addFunction("highest_level") { params ->
|
|
+ DoubleValue(highestLevel)
|
|
+ }
|
|
+ .addFunction("set_highest_level") { params ->
|
|
+ highestLevel = params.getDouble(0).toInt()
|
|
+ DoubleValue.ONE
|
|
+ }
|
|
+ // more, also worth moving to something like MoLangFunctions.kt so it's easier to API in more values
|
|
|
|
@Transient
|
|
lateinit var pokedexManager: AbstractPokedexManager
|
|
@@ -94,10 +107,16 @@ class SpeciesDexRecord {
|
|
|
|
fun addInformation(pokemon: Pokemon, knowledge: PokedexEntryProgress) {
|
|
aspects.addAll(pokemon.aspects)
|
|
+ if (knowledge == PokedexEntryProgress.CAUGHT) {
|
|
+ highestLevel = maxOf(highestLevel, pokemon.level)
|
|
+ }
|
|
}
|
|
|
|
fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
|
|
aspects.addAll(pokedexEntityData.pokemon.aspects)
|
|
+ if (knowledge == PokedexEntryProgress.CAUGHT) {
|
|
+ highestLevel = maxOf(highestLevel, pokedexEntityData.pokemon.level)
|
|
+ }
|
|
}
|
|
|
|
fun addAspects(addedAspects: Set<String>) {
|
|
@@ -128,6 +147,7 @@ class SpeciesDexRecord {
|
|
|
|
fun clone() = SpeciesDexRecord().also {
|
|
it.aspects.addAll(aspects)
|
|
+ it.highestLevel = highestLevel
|
|
it.formRecords.putAll(formRecords.mapValues { it.value.clone() })
|
|
}
|
|
|
|
@@ -139,6 +159,7 @@ class SpeciesDexRecord {
|
|
|
|
fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
buffer.writeCollection(aspects) { _, it -> buffer.writeString(it) }
|
|
+ buffer.writeInt(highestLevel)
|
|
buffer.writeInt(formRecords.size)
|
|
for ((formName, formRecord) in formRecords) {
|
|
buffer.writeString(formName)
|
|
@@ -149,6 +170,7 @@ class SpeciesDexRecord {
|
|
fun decode(buffer: RegistryFriendlyByteBuf) {
|
|
aspects.clear()
|
|
aspects.addAll(buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() })
|
|
+ highestLevel = buffer.readInt()
|
|
formRecords.clear()
|
|
val numForms = buffer.readInt()
|
|
for (i in 0 until numForms) {
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchByType.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchByType.kt
|
|
index b4352d6902..c28abea392 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchByType.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchByType.kt
|
|
@@ -11,5 +11,6 @@ package com.cobblemon.mod.common.api.pokedex.filter
|
|
enum class SearchByType {
|
|
SPECIES,
|
|
ABILITIES,
|
|
+ MOVES,
|
|
DROPS;
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchFilter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchFilter.kt
|
|
index 5bcd6d2744..8f46f93f35 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchFilter.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SearchFilter.kt
|
|
@@ -9,14 +9,22 @@
|
|
package com.cobblemon.mod.common.api.pokedex.filter
|
|
|
|
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
|
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
|
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
|
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
|
+import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
|
+import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.client.CobblemonClient
|
|
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
|
+import com.cobblemon.mod.common.pokemon.FormData
|
|
+import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
|
import com.cobblemon.mod.common.util.asTranslated
|
|
import com.cobblemon.mod.common.util.itemRegistry
|
|
import net.minecraft.client.Minecraft
|
|
+import net.minecraft.resources.ResourceLocation
|
|
import net.minecraft.world.item.ItemStack
|
|
|
|
/**
|
|
@@ -47,6 +55,23 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
|
|
}
|
|
return abilityList.any { it.contains(searchString.trim().lowercase()) }
|
|
}
|
|
+ SearchByType.MOVES -> {
|
|
+ if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.CAUGHT) return false
|
|
+ val search = searchString.trim().lowercase()
|
|
+ val learnedTMs = CobblemonClient.clientTMMoveData.learnedTMs
|
|
+ val forms = pokedexManager.getCaughtForms(entry)
|
|
+ val formData = if (forms.isEmpty()) {
|
|
+ listOf(species.standardForm)
|
|
+ } else {
|
|
+ forms.map { form ->
|
|
+ species.forms.find { it.name.equals(form.displayForm, ignoreCase = true) } ?: species.standardForm
|
|
+ }
|
|
+ }
|
|
+ return formData.any { form ->
|
|
+ val highestLevel = pokedexManager.getSpeciesRecord(form.species.resourceIdentifier)?.getFormRecord(form.name)?.highestLevel ?: -1
|
|
+ hasDiscoveredMove(form, search, highestLevel, pokedexManager, learnedTMs)
|
|
+ }
|
|
+ }
|
|
SearchByType.DROPS -> {
|
|
if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.CAUGHT) return false
|
|
val dropsList = mutableListOf<String>()
|
|
@@ -68,4 +93,132 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
|
|
}
|
|
}
|
|
|
|
-}
|
|
\ No newline at end of file
|
|
+ private fun hasDiscoveredMove(
|
|
+ form: FormData,
|
|
+ search: String,
|
|
+ highestLevel: Int,
|
|
+ pokedex: AbstractPokedexManager,
|
|
+ learnedTMs: Set<ResourceLocation>
|
|
+ ): Boolean {
|
|
+ val evolutionMoveLevels = buildEvolutionMoveLevelIndex(form)
|
|
+
|
|
+ fun matches(move: MoveTemplate): Boolean {
|
|
+ return move.displayName.string.lowercase().contains(search)
|
|
+ }
|
|
+
|
|
+ fun isLevelUpDiscovered(move: MoveTemplate, level: Int): Boolean {
|
|
+ if (highestLevel >= level) return true
|
|
+
|
|
+ for ((evolutionSpeciesId, moveLevels) in evolutionMoveLevels) {
|
|
+ val evolutionLevel = moveLevels[move.name] ?: continue
|
|
+ val evolutionHighest = pokedex.getSpeciesRecord(evolutionSpeciesId)?.highestLevel ?: -1
|
|
+ if (evolutionHighest >= evolutionLevel) return true
|
|
+ }
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ form.moves.levelUpMoves.forEach { (level, moves) ->
|
|
+ for (move in moves) {
|
|
+ if (matches(move) && isLevelUpDiscovered(move, level)) return true
|
|
+ }
|
|
+ }
|
|
+
|
|
+ form.moves.tmMoves.forEach { move ->
|
|
+ val tmId = TechnicalMachines.moveToTM[move]?.id
|
|
+ val discovered = tmId == null || tmId in learnedTMs
|
|
+ if (discovered && matches(move)) return true
|
|
+ }
|
|
+
|
|
+ val alwaysDiscovered = sequenceOf(
|
|
+ form.moves.tutorMoves,
|
|
+ form.moves.eggMoves,
|
|
+ form.moves.evolutionMoves,
|
|
+ form.moves.formChangeMoves,
|
|
+ form.moves.specialMoves,
|
|
+ form.moves.legacyMoves
|
|
+ )
|
|
+
|
|
+ for (moves in alwaysDiscovered) {
|
|
+ for (move in moves) {
|
|
+ if (matches(move)) return true
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ private fun buildEvolutionMoveLevelIndex(form: FormData): Map<ResourceLocation, Map<String, Int>> {
|
|
+ val evolutionForms = collectEvolutionForms(form)
|
|
+ val levelsBySpecies = mutableMapOf<ResourceLocation, MutableMap<String, Int>>()
|
|
+
|
|
+ for (evolutionForm in evolutionForms) {
|
|
+ val speciesId = evolutionForm.species.resourceIdentifier
|
|
+ val moveLevels = levelsBySpecies.getOrPut(speciesId) { mutableMapOf() }
|
|
+ buildMoveLevelIndex(evolutionForm).forEach { (moveName, level) ->
|
|
+ val current = moveLevels[moveName]
|
|
+ if (current == null || level < current) {
|
|
+ moveLevels[moveName] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return levelsBySpecies
|
|
+ }
|
|
+
|
|
+ private fun buildMoveLevelIndex(form: FormData): Map<String, Int> {
|
|
+ val levels = mutableMapOf<String, Int>()
|
|
+ form.moves.levelUpMoves.forEach { (level, moves) ->
|
|
+ moves.forEach { move ->
|
|
+ val current = levels[move.name]
|
|
+ if (current == null || level < current) {
|
|
+ levels[move.name] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return levels
|
|
+ }
|
|
+
|
|
+ private fun collectEvolutionForms(rootForm: FormData): List<FormData> {
|
|
+ val results = mutableListOf<FormData>()
|
|
+ val visited = mutableSetOf<String>()
|
|
+
|
|
+ fun key(form: FormData): String {
|
|
+ return "${form.species.resourceIdentifier}|${form.name.lowercase()}"
|
|
+ }
|
|
+
|
|
+ fun nextEvolutions(current: FormData): Set<Evolution> {
|
|
+ return if (current.evolutions.isNotEmpty()) {
|
|
+ current.evolutions
|
|
+ } else {
|
|
+ current.species.evolutions
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun traverse(current: FormData) {
|
|
+ for (evolution in nextEvolutions(current)) {
|
|
+ val evolutionForm = resolveEvolutionForm(evolution) ?: continue
|
|
+ val evolutionKey = key(evolutionForm)
|
|
+ if (!visited.add(evolutionKey)) continue
|
|
+ results.add(evolutionForm)
|
|
+ traverse(evolutionForm)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ traverse(rootForm)
|
|
+ return results
|
|
+ }
|
|
+
|
|
+ private fun resolveEvolutionForm(evolution: Evolution): FormData? {
|
|
+ val speciesId = evolution.result.species?.asIdentifierDefaultingNamespace() ?: return null
|
|
+ val species = PokemonSpecies.getByIdentifier(speciesId) ?: return null
|
|
+ val formId = evolution.result.form
|
|
+ return if (formId == null) {
|
|
+ species.standardForm
|
|
+ } else {
|
|
+ species.forms.firstOrNull {
|
|
+ it.formOnlyShowdownId().equals(formId, ignoreCase = true) || it.name.equals(formId, ignoreCase = true)
|
|
+ } ?: species.standardForm
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SecondaryPokedexFilter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SecondaryPokedexFilter.kt
|
|
new file mode 100644
|
|
index 0000000000..2df36b0e4a
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokedex/filter/SecondaryPokedexFilter.kt
|
|
@@ -0,0 +1,166 @@
|
|
+/*
|
|
+ * 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.pokedex.filter
|
|
+
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
|
+import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
|
+import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
|
+import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
|
+import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.client.CobblemonClient
|
|
+import com.cobblemon.mod.common.pokemon.FormData
|
|
+import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+
|
|
+enum class SecondaryPokedexFilterType {
|
|
+ ALL,
|
|
+ CAUGHT,
|
|
+ SEEN,
|
|
+ UNDISCOVERED,
|
|
+ LEVEL_UP_TM_UNDISCOVERED
|
|
+}
|
|
+
|
|
+class SecondaryPokedexFilter(
|
|
+ private val pokedexManager: AbstractPokedexManager,
|
|
+ private val filterType: SecondaryPokedexFilterType
|
|
+) : EntryFilter() {
|
|
+
|
|
+ override fun test(entry: PokedexEntry): Boolean {
|
|
+ return when (filterType) {
|
|
+ SecondaryPokedexFilterType.ALL -> true
|
|
+ SecondaryPokedexFilterType.CAUGHT -> pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.CAUGHT
|
|
+ SecondaryPokedexFilterType.SEEN -> pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.ENCOUNTERED
|
|
+ SecondaryPokedexFilterType.UNDISCOVERED -> pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.NONE
|
|
+ SecondaryPokedexFilterType.LEVEL_UP_TM_UNDISCOVERED -> hasUndiscoveredLevelUpTM(entry)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun hasUndiscoveredLevelUpTM(entry: PokedexEntry): Boolean {
|
|
+ if (pokedexManager.getHighestKnowledgeFor(entry) != PokedexEntryProgress.CAUGHT) return false
|
|
+ val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
|
|
+ val forms = pokedexManager.getCaughtForms(entry)
|
|
+ val formData = if (forms.isEmpty()) {
|
|
+ listOf(species.standardForm)
|
|
+ } else {
|
|
+ forms.map { form ->
|
|
+ species.forms.find { it.name.equals(form.displayForm, ignoreCase = true) } ?: species.standardForm
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return formData.any { form ->
|
|
+ val highestLevel = pokedexManager.getSpeciesRecord(form.species.resourceIdentifier)?.getFormRecord(form.name)?.highestLevel ?: -1
|
|
+ hasUndiscoveredLevelUpTM(form, highestLevel)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun hasUndiscoveredLevelUpTM(
|
|
+ form: FormData,
|
|
+ highestLevel: Int
|
|
+ ): Boolean {
|
|
+ val evolutionMoveLevels = buildEvolutionMoveLevelIndex(form)
|
|
+
|
|
+ fun isLevelUpDiscovered(move: MoveTemplate, level: Int): Boolean {
|
|
+ if (highestLevel >= level) return true
|
|
+
|
|
+ for ((evolutionSpeciesId, moveLevels) in evolutionMoveLevels) {
|
|
+ val evolutionLevel = moveLevels[move.name] ?: continue
|
|
+ val evolutionHighest = pokedexManager.getSpeciesRecord(evolutionSpeciesId)?.highestLevel ?: -1
|
|
+ if (evolutionHighest >= evolutionLevel) return true
|
|
+ }
|
|
+
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ form.moves.levelUpMoves.forEach { (level, moves) ->
|
|
+ for (move in moves) {
|
|
+ if (TechnicalMachines.moveToTM[move] != null && !isLevelUpDiscovered(move, level)) {
|
|
+ return true
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ private fun buildEvolutionMoveLevelIndex(form: FormData): Map<ResourceLocation, Map<String, Int>> {
|
|
+ val evolutionForms = collectEvolutionForms(form)
|
|
+ val levelsBySpecies = mutableMapOf<ResourceLocation, MutableMap<String, Int>>()
|
|
+
|
|
+ for (evolutionForm in evolutionForms) {
|
|
+ val speciesId = evolutionForm.species.resourceIdentifier
|
|
+ val moveLevels = levelsBySpecies.getOrPut(speciesId) { mutableMapOf() }
|
|
+ buildMoveLevelIndex(evolutionForm).forEach { (moveName, level) ->
|
|
+ val current = moveLevels[moveName]
|
|
+ if (current == null || level < current) {
|
|
+ moveLevels[moveName] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return levelsBySpecies
|
|
+ }
|
|
+
|
|
+ private fun buildMoveLevelIndex(form: FormData): Map<String, Int> {
|
|
+ val levels = mutableMapOf<String, Int>()
|
|
+ form.moves.levelUpMoves.forEach { (level, moves) ->
|
|
+ moves.forEach { move ->
|
|
+ val current = levels[move.name]
|
|
+ if (current == null || level < current) {
|
|
+ levels[move.name] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return levels
|
|
+ }
|
|
+
|
|
+ private fun collectEvolutionForms(rootForm: FormData): List<FormData> {
|
|
+ val results = mutableListOf<FormData>()
|
|
+ val visited = mutableSetOf<String>()
|
|
+
|
|
+ fun key(form: FormData): String {
|
|
+ return "${form.species.resourceIdentifier}|${form.name.lowercase()}"
|
|
+ }
|
|
+
|
|
+ fun nextEvolutions(current: FormData): Set<Evolution> {
|
|
+ return if (current.evolutions.isNotEmpty()) {
|
|
+ current.evolutions
|
|
+ } else {
|
|
+ current.species.evolutions
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun traverse(current: FormData) {
|
|
+ for (evolution in nextEvolutions(current)) {
|
|
+ val evolutionForm = resolveEvolutionForm(evolution) ?: continue
|
|
+ val evolutionKey = key(evolutionForm)
|
|
+ if (!visited.add(evolutionKey)) continue
|
|
+ results.add(evolutionForm)
|
|
+ traverse(evolutionForm)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ traverse(rootForm)
|
|
+ return results
|
|
+ }
|
|
+
|
|
+ private fun resolveEvolutionForm(evolution: Evolution): FormData? {
|
|
+ val speciesId = evolution.result.species?.asIdentifierDefaultingNamespace() ?: return null
|
|
+ val species = PokemonSpecies.getByIdentifier(speciesId) ?: return null
|
|
+ val formId = evolution.result.form
|
|
+ return if (formId == null) {
|
|
+ species.standardForm
|
|
+ } else {
|
|
+ species.forms.firstOrNull {
|
|
+ it.formOnlyShowdownId().equals(formId, ignoreCase = true) || it.name.equals(formId, ignoreCase = true)
|
|
+ } ?: species.standardForm
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/moves/Learnset.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/moves/Learnset.kt
|
|
index bdff7f678b..870d5a2193 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/moves/Learnset.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/moves/Learnset.kt
|
|
@@ -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
|
|
@@ -19,9 +20,7 @@ import com.google.gson.JsonElement
|
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
|
|
open class Learnset : ClientDataSynchronizer<Learnset> {
|
|
- class Interpreter(
|
|
- val loadMove: (JsonElement, Learnset) -> Boolean
|
|
- ) {
|
|
+ class Interpreter(val loadMove: (JsonElement, Learnset) -> Boolean) {
|
|
companion object {
|
|
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
|
|
return Interpreter { element, learnset ->
|
|
@@ -73,8 +72,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
|
eggInterpreter,
|
|
tutorInterpreter,
|
|
legacyInterpreter,
|
|
- specialInterpreter,
|
|
- levelUpInterpreter,
|
|
+ specialInterpreter, levelUpInterpreter,
|
|
formChangeInterpreter
|
|
)
|
|
}
|
|
@@ -99,6 +97,20 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
|
.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
|
|
+ }
|
|
+
|
|
+
|
|
fun getAllLegalMoves(): Set<MoveTemplate> {
|
|
return levelUpMoves.values.flatten().toSet() +
|
|
eggMoves +
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/PokemonStore.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/PokemonStore.kt
|
|
index 5a045d28e8..8fcc964441 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/PokemonStore.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/PokemonStore.kt
|
|
@@ -77,6 +77,7 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
|
remove(pokemon)
|
|
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
|
|
set(position, pokemon)
|
|
+ Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
|
|
return true
|
|
}
|
|
|
|
@@ -170,4 +171,4 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
|
* each change, accessed by [Pokemon.getChangeObservable].
|
|
*/
|
|
abstract fun getAnyChangeObservable(): Observable<Unit>
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt
|
|
index d9e2141bab..124fe0cdd2 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/pc/PCStore.kt
|
|
@@ -332,7 +332,7 @@ open class PCStore(
|
|
val toast = Toast(
|
|
title = lang("wallpaper_unlocked"),
|
|
description = unlockableWallpaper.displayName?.let { "\"".text().add(it.asTranslated()).add("\"") } ?: lang("unknown_wallpaper") ,
|
|
- icon = ItemStack(CobblemonItems.PC)
|
|
+ icons = listOf(ItemStack(CobblemonItems.PC))
|
|
)
|
|
toast.addListeners(player)
|
|
toast.expireAfter(5F)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreManager.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreManager.kt
|
|
index 11be09b9b1..d8615494de 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreManager.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreManager.kt
|
|
@@ -8,14 +8,17 @@
|
|
|
|
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.storage.player.client.ClientInstancedPlayerData
|
|
+import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
|
+import com.cobblemon.mod.common.api.tms.TMMoveManager
|
|
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
|
import java.util.UUID
|
|
import java.util.concurrent.Executors
|
|
-import java.util.concurrent.TimeUnit
|
|
import net.minecraft.server.MinecraftServer
|
|
import net.minecraft.server.level.ServerPlayer
|
|
import net.minecraft.world.entity.player.Player
|
|
@@ -30,7 +33,6 @@ import net.minecraft.world.entity.player.Player
|
|
* @author Apion
|
|
* @since February 21, 2024
|
|
*/
|
|
-
|
|
open class PlayerInstancedDataStoreManager {
|
|
val saveExecutor = Executors.newSingleThreadExecutor(
|
|
ThreadFactoryBuilder()
|
|
@@ -53,19 +55,27 @@ open class PlayerInstancedDataStoreManager {
|
|
//Should put this somewhere else
|
|
saveTasks[PlayerInstancedDataStoreTypes.GENERAL] = ScheduledTask.Builder()
|
|
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.GENERAL) }
|
|
- .delay(30f)
|
|
- .interval(120f)
|
|
+ .delay(30F)
|
|
+ .interval(120F)
|
|
.infiniteIterations()
|
|
.tracker(ServerTaskTracker)
|
|
.build()
|
|
|
|
saveTasks[PlayerInstancedDataStoreTypes.POKEDEX] = ScheduledTask.Builder()
|
|
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.POKEDEX) }
|
|
- .delay(30f)
|
|
- .interval(120f)
|
|
+ .delay(30F)
|
|
+ .interval(120F)
|
|
.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
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+
|
|
+ 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
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreTypes.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreTypes.kt
|
|
index 2243c69f9b..6b19992c4f 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreTypes.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerInstancedDataStoreTypes.kt
|
|
@@ -8,8 +8,10 @@
|
|
|
|
package com.cobblemon.mod.common.api.storage.player
|
|
|
|
-import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
|
+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.util.cobblemonResource
|
|
import net.minecraft.resources.ResourceLocation
|
|
|
|
@@ -27,11 +29,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 {
|
|
+ Cobblemon.LOGGER.debug("Registering PlayerInstancedDataStoreType: {}", type.id)
|
|
types[type.id] = type
|
|
return type
|
|
}
|
|
|
|
- fun getTypeById(id: ResourceLocation) = types[id]
|
|
-}
|
|
\ No newline at end of file
|
|
+ fun getTypeById(id: ResourceLocation): PlayerInstancedDataStoreType? {
|
|
+ val type = types[id]
|
|
+ if (type == null) Cobblemon.LOGGER.warn("Unknown PlayerInstancedDataStoreType ID: {}", id)
|
|
+ return type
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveJsonBackend.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveJsonBackend.kt
|
|
new file mode 100644
|
|
index 0000000000..89a8f06f57
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveJsonBackend.kt
|
|
@@ -0,0 +1,35 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.api.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) }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveMongoBackend.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveMongoBackend.kt
|
|
new file mode 100644
|
|
index 0000000000..e4b7fda155
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveMongoBackend.kt
|
|
@@ -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) }
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveNbtBackend.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveNbtBackend.kt
|
|
new file mode 100644
|
|
index 0000000000..544ec919b2
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/adapter/TMMoveNbtBackend.kt
|
|
@@ -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
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/client/ClientTMMoveManager.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/client/ClientTMMoveManager.kt
|
|
new file mode 100644
|
|
index 0000000000..54e500b45d
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/client/ClientTMMoveManager.kt
|
|
@@ -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)
|
|
+ }
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/CachedPlayerDataStoreFactory.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/CachedPlayerDataStoreFactory.kt
|
|
index 24bea63adb..b31ab888f8 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/CachedPlayerDataStoreFactory.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/factory/CachedPlayerDataStoreFactory.kt
|
|
@@ -40,8 +40,6 @@ class CachedPlayerDataStoreFactory<T : InstancedPlayerData>(val backend: PlayerD
|
|
}
|
|
}
|
|
|
|
-
|
|
-
|
|
override fun saveAll() {
|
|
cache.forEach { (_, pd) -> backend.save(pd) }
|
|
cache.removeIf { (uuid, _) -> uuid.getPlayer() == null }
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
|
|
index 6079c4bf16..9afa850afe 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
|
|
@@ -91,6 +91,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")
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/ObtainMethod.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/ObtainMethod.kt
|
|
new file mode 100644
|
|
index 0000000000..3a645060c3
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/ObtainMethod.kt
|
|
@@ -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.tms
|
|
+
|
|
+import com.cobblemon.mod.common.tms.obtain.DefaultObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.UnlockableObtainMethod
|
|
+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:unlockable" -> UnlockableObtainMethod()
|
|
+ "cobblemon:default" -> DefaultObtainMethod()
|
|
+ "cobblemon:advancement" -> {
|
|
+ val advancement = buffer.readResourceLocation()
|
|
+ PlayerHasAdvancementObtainMethod(advancement)
|
|
+ }
|
|
+ "cobblemon:y_level" -> {
|
|
+ val yLevel = buffer.readVarInt()
|
|
+ val operator = buffer.readUtf()
|
|
+ PlayerYObtainMethod(yLevel, operator)
|
|
+ }
|
|
+ else -> throw IllegalArgumentException("Unknown ObtainMethod variant: $variant")
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TMMoveManager.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TMMoveManager.kt
|
|
new file mode 100644
|
|
index 0000000000..a6e250d2b9
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TMMoveManager.kt
|
|
@@ -0,0 +1,177 @@
|
|
+/*
|
|
+ * 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.CobblemonNetwork.sendPacket
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
|
+import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
|
+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.ClientTMMoveManager
|
|
+import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
|
+import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
|
+import com.cobblemon.mod.common.pokemon.Pokemon
|
|
+import com.cobblemon.mod.common.util.getPlayer
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import com.mojang.serialization.Codec
|
|
+import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
+import java.util.UUID
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+
|
|
+class TMMoveManager(
|
|
+ override val uuid: UUID,
|
|
+ val learnedTMs: MutableSet<ResourceLocation> = mutableSetOf()
|
|
+) : InstancedPlayerData {
|
|
+
|
|
+ val storeType = PlayerInstancedDataStoreTypes.TM_MOVES
|
|
+
|
|
+ override fun toClientData() = ClientTMMoveManager(learnedTMs.toMutableSet())
|
|
+ fun toClientDataFrom(set: Set<ResourceLocation>) = ClientTMMoveManager(set.toMutableSet())
|
|
+
|
|
+ fun syncTMsFromPokemon(pokemon: Pokemon) {
|
|
+ learn(getLearnableTMsFromPokemon(pokemon))
|
|
+ }
|
|
+
|
|
+ fun syncTMFromMove(moveTemplate: MoveTemplate) {
|
|
+ val tmId = TechnicalMachines.moveToTM[moveTemplate]?.id ?: return
|
|
+ learn(listOf(tmId))
|
|
+ }
|
|
+
|
|
+ fun getLearnableTMsFromPokemon(pokemon: Pokemon): Collection<ResourceLocation> {
|
|
+ return TechnicalMachines.tmMap.values
|
|
+ .filter { tm -> tm.moveName in pokemon.allAccessibleMoves }
|
|
+ .map(TechnicalMachine::id)
|
|
+ }
|
|
+
|
|
+ fun scheduleFullSyncFromStores(
|
|
+ party: Iterable<Pokemon?>,
|
|
+ pc: Iterable<Pokemon?>,
|
|
+ batchSize: Int = 25
|
|
+ ) {
|
|
+ val iterator = sequence {
|
|
+ for (pokemon in party) {
|
|
+ if (pokemon != null) yield(pokemon)
|
|
+ }
|
|
+ for (pokemon in pc) {
|
|
+ if (pokemon != null) yield(pokemon)
|
|
+ }
|
|
+ }.iterator()
|
|
+
|
|
+ if (!iterator.hasNext()) return
|
|
+
|
|
+ val tmIds = mutableSetOf<ResourceLocation>()
|
|
+
|
|
+ ScheduledTask.Builder()
|
|
+ .tracker(ServerTaskTracker)
|
|
+ .interval(0F)
|
|
+ .infiniteIterations()
|
|
+ .execute { task ->
|
|
+ var processed = 0
|
|
+ while (processed < batchSize && iterator.hasNext()) {
|
|
+ val pokemon = iterator.next()
|
|
+ for (move in pokemon.allAccessibleMoves) {
|
|
+ TechnicalMachines.moveToTM[move]?.id?.let { tmIds.add(it) }
|
|
+ }
|
|
+ processed++
|
|
+ }
|
|
+
|
|
+ if (!iterator.hasNext()) {
|
|
+ learn(tmIds)
|
|
+ task.expire()
|
|
+ }
|
|
+ }
|
|
+ .build()
|
|
+ }
|
|
+
|
|
+ fun learn(tmIds: Collection<ResourceLocation>): Boolean {
|
|
+ val newLearnedTms = mutableListOf<ResourceLocation>()
|
|
+ for (tmId in tmIds) {
|
|
+ if (learnedTMs.add(tmId)) {
|
|
+ newLearnedTms.add(tmId)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (newLearnedTms.isEmpty()) return false
|
|
+
|
|
+ syncClient(newLearnedTms.toSet())
|
|
+ sendTMToast(newLearnedTms)
|
|
+
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ fun unlearn(tmIds: Collection<ResourceLocation>): Boolean {
|
|
+ val removedTms = mutableListOf<ResourceLocation>()
|
|
+
|
|
+ for (tmId in tmIds) {
|
|
+ if (learnedTMs.remove(tmId)) {
|
|
+ removedTms.add(tmId)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (removedTms.isEmpty()) return false
|
|
+
|
|
+ syncClient(learnedTMs, isIncremental = false)
|
|
+
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ fun sendTMToast(tmIds: List<ResourceLocation>) {
|
|
+ val player = uuid.getPlayer() ?: return
|
|
+ var moveName: Component = Component.empty()
|
|
+ val icons = tmIds.mapNotNull {
|
|
+ val tm = TechnicalMachines.tmMap[it] ?: return@mapNotNull null
|
|
+ moveName = tm.translatedMoveName()
|
|
+
|
|
+ return@mapNotNull tm.createItemStack()
|
|
+ }
|
|
+
|
|
+ val description = if (icons.size == 1) moveName else lang("tms.check_tmm")
|
|
+
|
|
+ val packet = ToastPacket(
|
|
+ title = lang("tms.new_tms_learned"),
|
|
+ description = description,
|
|
+ icons = icons,
|
|
+ frameTexture = ResourceLocation.parse("minecraft:toast/advancement"),
|
|
+ progress = -1F,
|
|
+ progressColor = 0x00FF00,
|
|
+ // Keeping it a fixed UUID makes it so the ToastTracker merges different toasts on the client
|
|
+ uuid = UUID.nameUUIDFromBytes("tm_toast".toByteArray()),
|
|
+ behaviour = ToastPacket.Behaviour.SHOW_OR_UPDATE,
|
|
+ durationMs = 4000L
|
|
+ )
|
|
+
|
|
+ player.sendPacket(packet)
|
|
+ }
|
|
+
|
|
+ fun syncClient(updateSet: Set<ResourceLocation> = learnedTMs, isIncremental: Boolean = true) {
|
|
+ uuid.getPlayer()?.sendPacket(
|
|
+ SetClientPlayerDataPacket(
|
|
+ type = storeType,
|
|
+ playerData = toClientDataFrom(updateSet),
|
|
+ isIncremental = isIncremental
|
|
+ )
|
|
+ )
|
|
+ }
|
|
+
|
|
+ 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, ::TMMoveManager)
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachine.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachine.kt
|
|
new file mode 100644
|
|
index 0000000000..d462372102
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachine.kt
|
|
@@ -0,0 +1,126 @@
|
|
+/*
|
|
+ * 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.CobblemonItems
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+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.DefaultObtainMethod
|
|
+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.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
|
|
+) {
|
|
+ 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,
|
|
+ includeUnlearned: Boolean = false
|
|
+ ): 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 (tm.moveName !in pokemon.form.moves.tmLearnableMoves()) {
|
|
+ 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()
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (player != null && !includeUnlearned) {
|
|
+ val learnedTMs = CobblemonClient.clientTMMoveData.learnedTMs
|
|
+ tms.retainAll { tm -> tm.isPassivelyObtained() || tm.id in learnedTMs }
|
|
+ }
|
|
+
|
|
+ return tms
|
|
+ }
|
|
+ }
|
|
+
|
|
+ lateinit var id: ResourceLocation
|
|
+
|
|
+ /**
|
|
+ * Gets the recipe list limited to the provided amount to match the slot amount of the TM Machine.
|
|
+ *
|
|
+ * @param limit The amount of recipe item slots to take.
|
|
+ * @return The recipe list limited to 3 ingredients.
|
|
+ */
|
|
+ fun getClampedRecipe(limit: Int = 3): List<TechnicalMachineRecipe>? = recipe?.take(limit)
|
|
+
|
|
+ /**
|
|
+ * 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 {
|
|
+ if (!obtainMethods.any { it is DefaultObtainMethod }) {
|
|
+ player.sendSystemMessage(lang("tms.unlock_tm", moveName.displayName))
|
|
+ }
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ fun isPassivelyObtained() = obtainMethods.any { it is DefaultObtainMethod }
|
|
+
|
|
+ /**
|
|
+ * 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
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineDTO.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineDTO.kt
|
|
new file mode 100644
|
|
index 0000000000..e8755c1f9c
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineDTO.kt
|
|
@@ -0,0 +1,30 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.api.tms
|
|
+
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+
|
|
+/**
|
|
+ * DTO used for [TechnicalMachine] JSON loading because Gson cannot deserialize Minecraft Ingredients.
|
|
+ */
|
|
+data class TechnicalMachineDTO(
|
|
+ val moveName: MoveTemplate,
|
|
+ val recipe: List<TechnicalMachineRecipeDTO>?,
|
|
+ val obtainMethods: List<ObtainMethod> = emptyList(),
|
|
+ val type: String
|
|
+) {
|
|
+ fun toTechnicalMachine(): TechnicalMachine {
|
|
+ return TechnicalMachine(
|
|
+ moveName = moveName,
|
|
+ recipe = recipe?.map { it.toTechnicalMachineRecipe() },
|
|
+ obtainMethods = obtainMethods,
|
|
+ type = type
|
|
+ )
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipe.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipe.kt
|
|
new file mode 100644
|
|
index 0000000000..0c56977edd
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipe.kt
|
|
@@ -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.world.item.crafting.Ingredient
|
|
+
|
|
+/**
|
|
+ * Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
|
|
+ */
|
|
+data class TechnicalMachineRecipe(
|
|
+ val ingredient: Ingredient,
|
|
+ val count: Int
|
|
+)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipeDTO.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipeDTO.kt
|
|
new file mode 100644
|
|
index 0000000000..c78c62f299
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachineRecipeDTO.kt
|
|
@@ -0,0 +1,50 @@
|
|
+/*
|
|
+ * 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.annotations.SerializedName
|
|
+import net.minecraft.core.registries.BuiltInRegistries
|
|
+import net.minecraft.core.registries.Registries
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.tags.TagKey
|
|
+import net.minecraft.world.item.crafting.Ingredient
|
|
+
|
|
+/**
|
|
+ * DTO used for [TechnicalMachineRecipe] JSON loading because Gson cannot deserialize Minecraft Ingredients.
|
|
+ */
|
|
+data class TechnicalMachineRecipeDTO(
|
|
+ val item: ResourceLocation?,
|
|
+ val tag: ResourceLocation?,
|
|
+ @SerializedName("count") private val _count: Int?,
|
|
+) {
|
|
+ private val count: Int
|
|
+ get() = _count ?: 1
|
|
+
|
|
+ fun toTechnicalMachineRecipe(): TechnicalMachineRecipe {
|
|
+ val ingredient = when {
|
|
+ item != null ->
|
|
+ Ingredient.of(
|
|
+ BuiltInRegistries.ITEM.get(item)
|
|
+ )
|
|
+
|
|
+ tag != null ->
|
|
+ Ingredient.of(
|
|
+ TagKey.create(Registries.ITEM, tag)
|
|
+ )
|
|
+
|
|
+ else ->
|
|
+ error("Recipe entry must define either 'item' or 'tag'")
|
|
+ }
|
|
+
|
|
+ return TechnicalMachineRecipe(
|
|
+ ingredient = ingredient,
|
|
+ count = count
|
|
+ )
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachines.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachines.kt
|
|
new file mode 100644
|
|
index 0000000000..40f5648462
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/tms/TechnicalMachines.kt
|
|
@@ -0,0 +1,74 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.api.tms
|
|
+
|
|
+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.util.adapters.TMObtainMethodAdapter
|
|
+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 java.io.BufferedReader
|
|
+import java.util.concurrent.ExecutionException
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.server.packs.PackType
|
|
+
|
|
+object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
|
|
+ override val gson = GsonBuilder()
|
|
+ .registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
|
+ .registerTypeAdapter(ObtainMethod::class.java, TMObtainMethodAdapter)
|
|
+ .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>()
|
|
+
|
|
+ /**
|
|
+ * Overridden to deserialize a DTO first and convert it to a runtime
|
|
+ * [TechnicalMachine], since Gson cannot deserialize Minecraft Ingredients
|
|
+ * directly.
|
|
+ */
|
|
+ override fun parse(
|
|
+ reader: BufferedReader,
|
|
+ identifier: ResourceLocation
|
|
+ ): TechnicalMachine {
|
|
+ try {
|
|
+ val technicalMachineDto = gson.fromJson(reader, TechnicalMachineDTO::class.java)
|
|
+ val technicalMachine = technicalMachineDto.toTechnicalMachine()
|
|
+ return technicalMachine
|
|
+ } catch (exception: Exception) {
|
|
+ throw ExecutionException(
|
|
+ "Error loading Technical Machine JSON: $identifier",
|
|
+ exception
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun reload(data: Map<ResourceLocation, TechnicalMachine>) {
|
|
+ data.forEach { (id, tm) ->
|
|
+ tmMap[id] = tm
|
|
+ tm.id = id
|
|
+ moveToTM[tm.moveName] = tm
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun getByResourceLocation(resourceLocation: ResourceLocation) = tmMap[resourceLocation]
|
|
+ fun getAllResourceLocations() = tmMap.keys.toSet()
|
|
+ override fun sync(player: ServerPlayer) {}
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/toast/Toast.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/toast/Toast.kt
|
|
index 1fe6a25569..e9db1f320d 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/toast/Toast.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/toast/Toast.kt
|
|
@@ -28,7 +28,7 @@ import net.minecraft.world.item.ItemStack
|
|
*
|
|
* @param title The [Component] representing the toast title, this can be a [Component.empty].
|
|
* @param description The [Component] representing the toast description, this can be a [Component.empty].
|
|
- * @param icon The [ItemStack] that is rendered as the toast icon, this can be a [ItemStack.EMPTY].
|
|
+ * @param icons The [ItemStack] that is rendered as the toast icon, this can be a [ItemStack.EMPTY].
|
|
* @param frameTexture The texture of the frame, default is the [VANILLA_BACKGROUND_SPRITE].
|
|
* @param progress The value of the progress bar, this accepts a value between 0.0F and 1.0F, any other value will not render a progress bar.
|
|
* @param progressColor The progress bar color in RGB, default is the [VANILLA_PROGRESS_COLOR].
|
|
@@ -38,7 +38,7 @@ import net.minecraft.world.item.ItemStack
|
|
class Toast(
|
|
title: Component,
|
|
description: Component,
|
|
- icon: ItemStack,
|
|
+ icons: List<ItemStack>,
|
|
frameTexture: ResourceLocation = VANILLA_BACKGROUND_SPRITE,
|
|
progress: Float = -1F,
|
|
progressColor: Int = VANILLA_PROGRESS_COLOR
|
|
@@ -57,7 +57,7 @@ class Toast(
|
|
/**
|
|
* The [ItemStack] that is rendered as the toast icon, this can be a [ItemStack.EMPTY].
|
|
*/
|
|
- var icon: ItemStack by Delegates.observable(icon) { _, old, new -> if (old != new) this.launchUpdate() }
|
|
+ var icons: List<ItemStack> by Delegates.observable(icons) { _, old, new -> if (old != new) this.launchUpdate() }
|
|
|
|
/**
|
|
* The texture of the frame.
|
|
@@ -185,7 +185,7 @@ class Toast(
|
|
private fun toPacket(behaviour: ToastPacket.Behaviour): ToastPacket = ToastPacket(
|
|
this.title,
|
|
this.description,
|
|
- this.icon,
|
|
+ this.icons,
|
|
this.frameTexture,
|
|
this.progress,
|
|
this.progressColor,
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalType.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalType.kt
|
|
index 7f96d353a9..7e1d5a9b08 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalType.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalType.kt
|
|
@@ -29,9 +29,12 @@ class ElementalType(
|
|
val displayName: MutableComponent,
|
|
val hue: Int,
|
|
val textureXMultiplier: Int,
|
|
+ val primaryColor: Int,
|
|
+ val secondaryColor: Int,
|
|
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
|
|
- val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), "")
|
|
-) : ShowdownIdentifiable {
|
|
+ val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), ""),
|
|
+ val typeGem: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, showdownId + "_gem"),
|
|
+ ) : ShowdownIdentifiable {
|
|
|
|
override fun showdownId(): String {
|
|
return showdownId
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalTypes.kt b/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalTypes.kt
|
|
index d64363c98a..cf55e218f5 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalTypes.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalTypes.kt
|
|
@@ -23,7 +23,9 @@ object ElementalTypes {
|
|
name = "Normal",
|
|
displayName = Component.translatable("cobblemon.type.normal"),
|
|
hue = 0xE8E8DA,
|
|
- textureXMultiplier = 0
|
|
+ textureXMultiplier = 0,
|
|
+ primaryColor = 0x8FA689,
|
|
+ secondaryColor = 0xFFFBE2
|
|
)
|
|
|
|
@JvmField
|
|
@@ -31,7 +33,9 @@ object ElementalTypes {
|
|
name = "Fire",
|
|
displayName = Component.translatable("cobblemon.type.fire"),
|
|
hue = 0xFF6E21,
|
|
- textureXMultiplier = 1
|
|
+ textureXMultiplier = 1,
|
|
+ primaryColor = 0xF92D2D,
|
|
+ secondaryColor = 0xF99B1D
|
|
)
|
|
|
|
@JvmField
|
|
@@ -39,7 +43,9 @@ object ElementalTypes {
|
|
name = "Water",
|
|
displayName = Component.translatable("cobblemon.type.water"),
|
|
hue = 0x3FA5FF,
|
|
- textureXMultiplier = 2
|
|
+ textureXMultiplier = 2,
|
|
+ primaryColor = 0x377AF6,
|
|
+ secondaryColor = 0x50DDF9
|
|
)
|
|
|
|
@JvmField
|
|
@@ -47,7 +53,9 @@ object ElementalTypes {
|
|
name = "Grass",
|
|
displayName = Component.translatable("cobblemon.type.grass"),
|
|
hue = 0x62D14F,
|
|
- textureXMultiplier = 3
|
|
+ textureXMultiplier = 3,
|
|
+ primaryColor = 0x0EAE95,
|
|
+ secondaryColor = 0xC6F554
|
|
)
|
|
|
|
@JvmField
|
|
@@ -55,7 +63,9 @@ object ElementalTypes {
|
|
name = "Electric",
|
|
displayName = Component.translatable("cobblemon.type.electric"),
|
|
hue = 0xFFD314,
|
|
- textureXMultiplier = 4
|
|
+ textureXMultiplier = 4,
|
|
+ primaryColor = 0xF7AB19,
|
|
+ secondaryColor = 0xFFF257
|
|
)
|
|
|
|
@JvmField
|
|
@@ -63,7 +73,9 @@ object ElementalTypes {
|
|
name = "Ice",
|
|
displayName = Component.translatable("cobblemon.type.ice"),
|
|
hue = 0x54F2F2,
|
|
- textureXMultiplier = 5
|
|
+ textureXMultiplier = 5,
|
|
+ primaryColor = 0x41BAED,
|
|
+ secondaryColor = 0x91F4F9
|
|
)
|
|
|
|
@JvmField
|
|
@@ -71,7 +83,9 @@ object ElementalTypes {
|
|
name = "Fighting",
|
|
displayName = Component.translatable("cobblemon.type.fighting"),
|
|
hue = 0xEF565D,
|
|
- textureXMultiplier = 6
|
|
+ textureXMultiplier = 6,
|
|
+ primaryColor = 0xC8356B,
|
|
+ secondaryColor = 0xF36A3F
|
|
)
|
|
|
|
@JvmField
|
|
@@ -79,7 +93,9 @@ object ElementalTypes {
|
|
name = "Poison",
|
|
displayName = Component.translatable("cobblemon.type.poison"),
|
|
hue = 0xD651FF,
|
|
- textureXMultiplier = 7
|
|
+ textureXMultiplier = 7,
|
|
+ primaryColor = 0x7B41DD,
|
|
+ secondaryColor = 0xE568F4
|
|
)
|
|
|
|
@JvmField
|
|
@@ -87,7 +103,9 @@ object ElementalTypes {
|
|
name = "Ground",
|
|
displayName = Component.translatable("cobblemon.type.ground"),
|
|
hue = 0xF4A453,
|
|
- textureXMultiplier = 8
|
|
+ textureXMultiplier = 8,
|
|
+ primaryColor = 0xE08835,
|
|
+ secondaryColor = 0xFFD057
|
|
)
|
|
|
|
@JvmField
|
|
@@ -95,7 +113,9 @@ object ElementalTypes {
|
|
name = "Flying",
|
|
displayName = Component.translatable("cobblemon.type.flying"),
|
|
hue = 0xB8B2FF,
|
|
- textureXMultiplier = 9
|
|
+ textureXMultiplier = 9,
|
|
+ primaryColor = 0x6083F6,
|
|
+ secondaryColor = 0xD5CCFA
|
|
)
|
|
|
|
@JvmField
|
|
@@ -103,7 +123,9 @@ object ElementalTypes {
|
|
name = "Psychic",
|
|
displayName = Component.translatable("cobblemon.type.psychic"),
|
|
hue = 0xFF5E9E,
|
|
- textureXMultiplier = 10
|
|
+ textureXMultiplier = 10,
|
|
+ primaryColor = 0xE553CE,
|
|
+ secondaryColor = 0xF69DD5
|
|
)
|
|
|
|
@JvmField
|
|
@@ -111,7 +133,9 @@ object ElementalTypes {
|
|
name = "Bug",
|
|
displayName = Component.translatable("cobblemon.type.bug"),
|
|
hue = 0xD3D319,
|
|
- textureXMultiplier = 11
|
|
+ textureXMultiplier = 11,
|
|
+ primaryColor = 0x9FBE1B,
|
|
+ secondaryColor = 0xE9FB5B
|
|
)
|
|
|
|
@JvmField
|
|
@@ -119,7 +143,9 @@ object ElementalTypes {
|
|
name = "Rock",
|
|
displayName = Component.translatable("cobblemon.type.rock"),
|
|
hue = 0xB7A16E,
|
|
- textureXMultiplier = 12
|
|
+ textureXMultiplier = 12,
|
|
+ primaryColor = 0x9F814A,
|
|
+ secondaryColor = 0xEECB7A
|
|
)
|
|
|
|
@JvmField
|
|
@@ -127,7 +153,9 @@ object ElementalTypes {
|
|
name = "Ghost",
|
|
displayName = Component.translatable("cobblemon.type.ghost"),
|
|
hue = 0x9C80F7,
|
|
- textureXMultiplier = 13
|
|
+ textureXMultiplier = 13,
|
|
+ primaryColor = 0x4F57DD,
|
|
+ secondaryColor = 0xC182F1
|
|
)
|
|
|
|
@JvmField
|
|
@@ -135,7 +163,9 @@ object ElementalTypes {
|
|
name = "Dragon",
|
|
displayName = Component.translatable("cobblemon.type.dragon"),
|
|
hue = 0x7580FF,
|
|
- textureXMultiplier = 14
|
|
+ textureXMultiplier = 14,
|
|
+ primaryColor = 0x4E68E2,
|
|
+ secondaryColor = 0x47C7F7
|
|
)
|
|
|
|
@JvmField
|
|
@@ -143,7 +173,9 @@ object ElementalTypes {
|
|
name = "Dark",
|
|
displayName = Component.translatable("cobblemon.type.dark"),
|
|
hue = 0x587DA0,
|
|
- textureXMultiplier = 15
|
|
+ textureXMultiplier = 15,
|
|
+ primaryColor = 0x465B99,
|
|
+ secondaryColor = 0x689EBF
|
|
)
|
|
|
|
@JvmField
|
|
@@ -151,7 +183,9 @@ object ElementalTypes {
|
|
name = "Steel",
|
|
displayName = Component.translatable("cobblemon.type.steel"),
|
|
hue = 0xABD1F4,
|
|
- textureXMultiplier = 16
|
|
+ textureXMultiplier = 16,
|
|
+ primaryColor = 0x628CB9,
|
|
+ secondaryColor = 0xD6F7F3
|
|
)
|
|
|
|
@JvmField
|
|
@@ -159,16 +193,20 @@ object ElementalTypes {
|
|
name = "Fairy",
|
|
displayName = Component.translatable("cobblemon.type.fairy"),
|
|
hue = 0xFF7FE5,
|
|
- textureXMultiplier = 17
|
|
+ textureXMultiplier = 17,
|
|
+ primaryColor = 0xE2678C,
|
|
+ secondaryColor = 0xF5A7B7
|
|
)
|
|
|
|
@JvmStatic
|
|
- 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
|
|
).also {
|
|
allTypes.add(it)
|
|
}
|
|
@@ -273,4 +311,4 @@ object ElementalTypes {
|
|
@JvmName("getFAIRY")
|
|
@Deprecated("Use ElementalTypes.FAIRY, provided for backwards compatibility until Cobblemon 1.8.")
|
|
fun getFAIRY() = FAIRY
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/GrowableStoneBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/GrowableStoneBlock.kt
|
|
index 4fc762ea54..42ad44a82c 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/block/GrowableStoneBlock.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/GrowableStoneBlock.kt
|
|
@@ -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
|
|
- }
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/MonitorBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/MonitorBlock.kt
|
|
index e95592addb..e459b31519 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/block/MonitorBlock.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/MonitorBlock.kt
|
|
@@ -16,6 +16,11 @@ import com.mojang.serialization.MapCodec
|
|
import net.minecraft.core.BlockPos
|
|
import net.minecraft.core.Direction
|
|
import net.minecraft.util.StringRepresentable
|
|
+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
|
|
@@ -28,6 +33,7 @@ import net.minecraft.world.level.block.state.StateDefinition
|
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
|
import net.minecraft.world.level.block.state.properties.EnumProperty
|
|
import net.minecraft.world.level.pathfinder.PathComputationType
|
|
+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
|
|
@@ -35,7 +41,8 @@ import net.minecraft.world.phys.shapes.VoxelShape
|
|
class MonitorBlock(settings: Properties) : MultiblockBlock(settings) {
|
|
init {
|
|
registerDefaultState(stateDefinition.any()
|
|
- .setValue(HORIZONTAL_FACING, Direction.NORTH))
|
|
+ .setValue(HORIZONTAL_FACING, Direction.NORTH)
|
|
+ .setValue(SCREEN, MonitorScreen.OFF))
|
|
}
|
|
|
|
override fun createMultiBlockEntity(
|
|
@@ -55,6 +62,47 @@ class MonitorBlock(settings: Properties) : MultiblockBlock(settings) {
|
|
return defaultBlockState().setValue(HORIZONTAL_FACING, blockPlaceContext.horizontalDirection)
|
|
}
|
|
|
|
+ override fun useWithoutItem(
|
|
+ state: BlockState,
|
|
+ level: Level,
|
|
+ pos: BlockPos,
|
|
+ player: Player,
|
|
+ hit: BlockHitResult
|
|
+ ): InteractionResult {
|
|
+ val entity = level.getBlockEntity(pos) as? FossilMultiblockEntity ?: return InteractionResult.PASS
|
|
+ return entity.handleUseWithoutItem(state, level, pos, player)
|
|
+ }
|
|
+
|
|
+ override fun useItemOn(
|
|
+ stack: ItemStack,
|
|
+ state: BlockState,
|
|
+ level: Level,
|
|
+ pos: BlockPos,
|
|
+ player: Player,
|
|
+ hand: InteractionHand,
|
|
+ hit: BlockHitResult
|
|
+ ): ItemInteractionResult {
|
|
+ val entity = level.getBlockEntity(pos) as? FossilMultiblockEntity
|
|
+ ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
|
+ val result = entity.handleUseItem(stack, state, level, pos, player, hand)
|
|
+ if (result == ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) {
|
|
+ return if (entity.handleUseWithoutItem(state, level, pos, player).consumesAction()) {
|
|
+ ItemInteractionResult.sidedSuccess(level.isClientSide)
|
|
+ } else {
|
|
+ ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
|
+ }
|
|
+ }
|
|
+ return result
|
|
+ }
|
|
+
|
|
+ override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
|
|
+ if (state.block != newState.block && !level.isClientSide && !movedByPiston) {
|
|
+ val entity = level.getBlockEntity(pos) as? FossilMultiblockEntity
|
|
+ entity?.dropDisk(level, pos, state)
|
|
+ }
|
|
+ super.onRemove(state, level, pos, newState, movedByPiston)
|
|
+ }
|
|
+
|
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
|
builder.add(HORIZONTAL_FACING)
|
|
builder.add(SCREEN)
|
|
@@ -143,8 +191,27 @@ class MonitorBlock(settings: Properties) : MultiblockBlock(settings) {
|
|
BLUE_PROGRESS_7,
|
|
BLUE_PROGRESS_8,
|
|
BLUE_PROGRESS_9,
|
|
- GREEN_PROGRESS_9;
|
|
+ GREEN_PROGRESS_9,
|
|
+ TM_NORMAL,
|
|
+ TM_FIRE,
|
|
+ TM_WATER,
|
|
+ TM_GRASS,
|
|
+ TM_ELECTRIC,
|
|
+ TM_ICE,
|
|
+ TM_FIGHTING,
|
|
+ TM_POISON,
|
|
+ TM_GROUND,
|
|
+ TM_FLYING,
|
|
+ TM_PSYCHIC,
|
|
+ TM_BUG,
|
|
+ TM_ROCK,
|
|
+ TM_GHOST,
|
|
+ TM_DRAGON,
|
|
+ TM_DARK,
|
|
+ TM_STEEL,
|
|
+ TM_FAIRY,
|
|
+ MUSIC;
|
|
|
|
override fun getSerializedName(): String = this.name.lowercase()
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/TMShelfBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/TMShelfBlock.kt
|
|
new file mode 100644
|
|
index 0000000000..0d2c2d14e6
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/TMShelfBlock.kt
|
|
@@ -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.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.Containers
|
|
+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?.items?.count { !it.isEmpty }?.coerceIn(0, 14) ?: 0
|
|
+ }
|
|
+
|
|
+ override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
|
|
+ if (!state.`is`(newState.block)) {
|
|
+ if (!level.isClientSide && !movedByPiston) {
|
|
+ val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
|
|
+ if (entity != null) {
|
|
+ for (index in entity.items.indices) {
|
|
+ val stack = entity.items[index]
|
|
+ if (!stack.isEmpty) {
|
|
+ Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, stack)
|
|
+ entity.items[index] = ItemStack.EMPTY
|
|
+ }
|
|
+ }
|
|
+ level.updateNeighbourForOutputSignal(pos, this)
|
|
+ }
|
|
+ }
|
|
+ super.onRemove(state, level, pos, newState, movedByPiston)
|
|
+ } else {
|
|
+ super.onRemove(state, level, pos, newState, movedByPiston)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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)
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/TumblestoneBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/TumblestoneBlock.kt
|
|
index 2639aba852..4e2cf99559 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/block/TumblestoneBlock.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/TumblestoneBlock.kt
|
|
@@ -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
|
|
}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemBlock.kt
|
|
new file mode 100644
|
|
index 0000000000..0580048453
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemBlock.kt
|
|
@@ -0,0 +1,23 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.block
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlocks
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import net.minecraft.world.item.Item
|
|
+import net.minecraft.world.level.block.Block
|
|
+
|
|
+class TypeGemBlock(
|
|
+ settings: Properties
|
|
+) : Block(settings) {
|
|
+
|
|
+ companion object {
|
|
+
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemClusterBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemClusterBlock.kt
|
|
new file mode 100644
|
|
index 0000000000..a142ce7d1a
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemClusterBlock.kt
|
|
@@ -0,0 +1,284 @@
|
|
+/*
|
|
+ * 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.CobblemonItems
|
|
+import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
|
+import com.mojang.serialization.MapCodec
|
|
+import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.core.component.DataComponents
|
|
+import net.minecraft.core.registries.BuiltInRegistries
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.server.level.ServerLevel
|
|
+import net.minecraft.util.RandomSource
|
|
+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.item.component.BlockItemStateProperties
|
|
+import net.minecraft.world.item.component.CustomModelData
|
|
+import net.minecraft.world.item.context.BlockPlaceContext
|
|
+import net.minecraft.world.item.enchantment.Enchantments
|
|
+import net.minecraft.world.item.enchantment.ItemEnchantments
|
|
+import net.minecraft.world.level.LevelAccessor
|
|
+import net.minecraft.world.level.LevelReader
|
|
+import net.minecraft.world.level.block.AirBlock
|
|
+import net.minecraft.world.level.block.Block
|
|
+import net.minecraft.world.level.block.Blocks
|
|
+import net.minecraft.world.level.block.DirectionalBlock
|
|
+import net.minecraft.world.level.block.Mirror
|
|
+import net.minecraft.world.level.block.Rotation
|
|
+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.level.block.state.properties.IntegerProperty
|
|
+import net.minecraft.world.level.material.PushReaction
|
|
+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.shapes.Shapes
|
|
+import net.minecraft.world.phys.shapes.VoxelShape
|
|
+import javax.swing.text.html.HTML.Attribute.SHAPES
|
|
+import kotlin.io.path.Path
|
|
+
|
|
+class TypeGemClusterBlock(
|
|
+ settings: Properties,
|
|
+ val nextStage: Block,
|
|
+ val dropItemId: ResourceLocation
|
|
+) : DirectionalBlock(settings.pushReaction(PushReaction.DESTROY)) {
|
|
+
|
|
+ companion object {
|
|
+ val CODEC: MapCodec<TypeGemClusterBlock> = RecordCodecBuilder.mapCodec { instance ->
|
|
+ instance.group(
|
|
+ propertiesCodec(),
|
|
+ Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage },
|
|
+ ResourceLocation.CODEC.fieldOf("dropItem").forGetter { it.dropItemId }
|
|
+ ).apply(instance) { settings, nextStage, dropItemId ->
|
|
+ TypeGemClusterBlock(settings, nextStage, dropItemId).also {
|
|
+ gemToClusterMap[nextStage] = it
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ val gemToClusterMap: MutableMap<Block, TypeGemClusterBlock> = mutableMapOf()
|
|
+
|
|
+ val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
|
|
+ val FACING: DirectionProperty = DirectionalBlock.FACING
|
|
+ val STAGE: IntegerProperty = IntegerProperty.create("stage", 0, 3)
|
|
+ val STUNTED: BooleanProperty = BooleanProperty.create("stunted")
|
|
+
|
|
+ fun clusterFromGemBlock(gemBlock: Block): TypeGemClusterBlock? {
|
|
+ return gemToClusterMap[gemBlock]
|
|
+ }
|
|
+ }
|
|
+
|
|
+ init {
|
|
+ registerDefaultState(stateDefinition.any()
|
|
+ .setValue(FACING, Direction.UP)
|
|
+ .setValue(SHOULD_GROW, true)
|
|
+ .setValue(STAGE, 0)
|
|
+ .setValue(STUNTED, false)
|
|
+ )
|
|
+ }
|
|
+
|
|
+ override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
|
+ val facing = context.clickedFace
|
|
+ val world = context.level
|
|
+ val pos = context.clickedPos.relative(facing.opposite)
|
|
+ val blockBelow = world.getBlockState(pos).block
|
|
+ val shouldGrow = blockBelow is TypeGemCoreBlock
|
|
+
|
|
+ return defaultBlockState()
|
|
+ .setValue(FACING, facing)
|
|
+ .setValue(SHOULD_GROW, shouldGrow)
|
|
+ .setValue(STAGE, 0)
|
|
+ .setValue(STUNTED, false)
|
|
+ }
|
|
+
|
|
+ override fun isRandomlyTicking(state: BlockState): Boolean = state.getValue(SHOULD_GROW)
|
|
+
|
|
+ override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
|
+ advanceGrowth(state, level, pos, random)
|
|
+ }
|
|
+
|
|
+ fun advanceGrowth(state: BlockState, level: LevelAccessor, pos: BlockPos, random: RandomSource) {
|
|
+ if (!state.getValue(SHOULD_GROW)) return
|
|
+
|
|
+ val currentStage = state.getValue(STAGE)
|
|
+
|
|
+ if (currentStage < 3) {
|
|
+ // Progress through stages regardless of STUNTED
|
|
+ level.setBlock(pos, state.setValue(STAGE, currentStage + 1), Block.UPDATE_ALL)
|
|
+ return
|
|
+ }
|
|
+
|
|
+ // At STAGE_3
|
|
+ val isStunted = state.getValue(STUNTED)
|
|
+ val facing = state.getValue(FACING)
|
|
+
|
|
+ if (isStunted) {
|
|
+ // println("[TypeGemClusterBlock] Cluster at $pos is STUNTED at STAGE_3. Finalizing growth.")
|
|
+ level.setBlock(pos, state.setValue(SHOULD_GROW, false), Block.UPDATE_ALL)
|
|
+ return
|
|
+ }
|
|
+
|
|
+ // Log type of cluster based on nextStage
|
|
+ val clusterType = level.registryAccess()
|
|
+ .registryOrThrow(BuiltInRegistries.BLOCK.key())
|
|
+ .getKey(nextStage)
|
|
+ .toString()
|
|
+
|
|
+ // println("[TypeGemClusterBlock] Attempting to grow into TypeGemBlock at $pos")
|
|
+ // println("[TypeGemClusterBlock] Cluster type: $clusterType")
|
|
+
|
|
+ // Check neighboring gem blocks
|
|
+ val hasConflict = Direction.entries
|
|
+ .filter { it != facing.opposite }
|
|
+ .any { dir ->
|
|
+ val neighborPos = pos.relative(dir)
|
|
+ val neighborState = level.getBlockState(neighborPos)
|
|
+ val neighborBlockId = BuiltInRegistries.BLOCK.getKey(neighborState.block)
|
|
+ // println("[TypeGemClusterBlock] Neighbor at $dir -> $neighborBlockId")
|
|
+ isGem(neighborState)
|
|
+ }
|
|
+
|
|
+ if (hasConflict) {
|
|
+ // println("[TypeGemClusterBlock] Found nearby TypeGemBlock(s). Stunting cluster at $pos.")
|
|
+ level.setBlock(
|
|
+ pos,
|
|
+ state.setValue(STUNTED, true).setValue(SHOULD_GROW, false),
|
|
+ Block.UPDATE_ALL
|
|
+ )
|
|
+ } else {
|
|
+ // println("[TypeGemClusterBlock] No conflicts. Converting cluster to TypeGemBlock.")
|
|
+ var nextState = nextStage.defaultBlockState()
|
|
+ if (nextState.hasProperty(FACING)) {
|
|
+ nextState = nextState.setValue(FACING, facing)
|
|
+ }
|
|
+ level.setBlock(pos, nextState, Block.UPDATE_ALL)
|
|
+ attachDecorativeClusters(level, pos, random)
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ private fun isGem(state: BlockState): Boolean {
|
|
+ return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
|
|
+ }
|
|
+
|
|
+ private fun attachDecorativeClusters(level: LevelAccessor, gemPos: BlockPos, random: RandomSource) {
|
|
+ val openDirections = Direction.entries.filter { direction ->
|
|
+ level.getBlockState(gemPos.relative(direction)).isAir
|
|
+ }.toMutableList()
|
|
+
|
|
+ if (openDirections.isEmpty()) {
|
|
+ return
|
|
+ }
|
|
+
|
|
+ val minClusters = maxOf(1, (openDirections.size * 0.75f).toInt())
|
|
+ val clusterCount = random.nextInt(minClusters, openDirections.size + 1)
|
|
+
|
|
+ repeat(clusterCount) {
|
|
+ if (openDirections.isEmpty()) return@repeat
|
|
+
|
|
+ val index = random.nextInt(openDirections.size)
|
|
+ val direction = openDirections.removeAt(index)
|
|
+ val clusterPos = gemPos.relative(direction)
|
|
+ val stage = random.nextInt(0, 4)
|
|
+
|
|
+ val clusterState = defaultBlockState()
|
|
+ .setValue(FACING, direction)
|
|
+ .setValue(STAGE, stage)
|
|
+ .setValue(SHOULD_GROW, false) // so we can reduce lag... hopefully
|
|
+ .setValue(STUNTED, false)
|
|
+
|
|
+ level.setBlock(clusterPos, clusterState, Block.UPDATE_ALL)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun getDrops(state: BlockState, params: LootParams.Builder): List<ItemStack> {
|
|
+ val drops = super.getDrops(state, params)
|
|
+
|
|
+ for (stack in drops) {
|
|
+ val blockItem = stack.item as? BlockItem ?: continue
|
|
+ if (blockItem.block !is TypeGemClusterBlock) continue
|
|
+
|
|
+ stack.set(
|
|
+ DataComponents.BLOCK_STATE,
|
|
+ BlockItemStateProperties.EMPTY
|
|
+ .with(STAGE, state.getValue(STAGE))
|
|
+ .with(SHOULD_GROW, false)
|
|
+ .with(STUNTED, state.getValue(STUNTED))
|
|
+ )
|
|
+ stack.set(
|
|
+ DataComponents.CUSTOM_MODEL_DATA,
|
|
+ CustomModelData(state.getValue(STAGE))
|
|
+ )
|
|
+ }
|
|
+
|
|
+ return drops
|
|
+ }
|
|
+
|
|
+ override fun getShape(
|
|
+ state: BlockState,
|
|
+ level: net.minecraft.world.level.BlockGetter,
|
|
+ pos: BlockPos,
|
|
+ context: net.minecraft.world.phys.shapes.CollisionContext
|
|
+ ): VoxelShape {
|
|
+ val stage = state.getValue(STAGE)
|
|
+ val facing = state.getValue(FACING)
|
|
+
|
|
+ val (min, max) = when (stage) {
|
|
+ 0 -> 5.0 to 11.0
|
|
+ 1 -> 4.0 to 12.0
|
|
+ 2 -> 3.0 to 13.0
|
|
+ 3 -> 2.0 to 14.0
|
|
+ else -> return Shapes.empty()
|
|
+ }
|
|
+
|
|
+ return when (facing) {
|
|
+ Direction.UP -> Shapes.box(min / 16, 0.0, min / 16, max / 16, (min + 1) / 16, max / 16)
|
|
+ Direction.DOWN -> Shapes.box(min / 16, (16 - (min + 1)) / 16, min / 16, max / 16, 1.0, max / 16)
|
|
+ Direction.NORTH -> Shapes.box(min / 16, min / 16, (16 - (min + 1)) / 16, max / 16, max / 16, 1.0)
|
|
+ Direction.SOUTH -> Shapes.box((16 - max) / 16, min / 16, 0.0, (16 - min) / 16, max / 16, (min + 1) / 16)
|
|
+ Direction.WEST -> Shapes.box((16 - (min + 1)) / 16, min / 16, min / 16, 1.0, max / 16, max / 16)
|
|
+ Direction.EAST -> Shapes.box(0.0, min / 16, min / 16, (min + 1) / 16, max / 16, max / 16)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
|
+ val facing = state.getValue(FACING)
|
|
+ if (direction == facing.opposite && !canSurvive(state, level, pos)) {
|
|
+ return Blocks.AIR.defaultBlockState()
|
|
+ }
|
|
+ return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
|
+ }
|
|
+
|
|
+ override fun rotate(state: BlockState, rotation: Rotation): BlockState {
|
|
+ return state.setValue(FACING, rotation.rotate(state.getValue(FACING)))
|
|
+ }
|
|
+
|
|
+ override fun mirror(state: BlockState, mirror: Mirror): BlockState {
|
|
+ return state.rotate(mirror.getRotation(state.getValue(FACING)))
|
|
+ }
|
|
+
|
|
+ override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
|
+ val supportPos = pos.relative(state.getValue(FACING).opposite)
|
|
+ val supportBlock = level.getBlockState(supportPos).block
|
|
+ return supportBlock !is AirBlock
|
|
+ }
|
|
+
|
|
+ override fun codec(): MapCodec<out DirectionalBlock> = CODEC
|
|
+
|
|
+ override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
|
+ super.createBlockStateDefinition(builder)
|
|
+ builder.add(FACING, SHOULD_GROW, STAGE, STUNTED)
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemCoreBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemCoreBlock.kt
|
|
new file mode 100644
|
|
index 0000000000..c791841741
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/TypeGemCoreBlock.kt
|
|
@@ -0,0 +1,215 @@
|
|
+/*
|
|
+ * 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.CobblemonBlocks
|
|
+import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
|
+import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
|
|
+import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.STUNTED
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.core.registries.BuiltInRegistries
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.server.level.ServerLevel
|
|
+import net.minecraft.util.RandomSource
|
|
+import net.minecraft.world.level.BlockGetter
|
|
+import net.minecraft.world.level.LevelAccessor
|
|
+import net.minecraft.world.level.WorldGenLevel
|
|
+import net.minecraft.world.level.block.Block
|
|
+import net.minecraft.world.level.block.DirectionalBlock
|
|
+import net.minecraft.world.level.block.state.BlockState
|
|
+import java.util.*
|
|
+
|
|
+class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
|
+
|
|
+ companion object {
|
|
+ const val MAX_CONNECTED_GEMS = 7
|
|
+ const val MIN_DISTANCE_BETWEEN_GEMS = 1
|
|
+ const val CONTINUATION_CHANCE = 0.8f
|
|
+
|
|
+ val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
|
|
+ mapOf(
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_normal") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_fire") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_water") to CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_electric") to CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_grass") to CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_ice") to CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_fighting") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_poison") to CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_ground") to CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_flying") to CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_psychic") to CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_bug") to CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_rock") to CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_ghost") to CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_dragon") to CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_dark") to CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_steel") to CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
|
|
+ ResourceLocation.parse("cobblemon:type_gem_block_fairy") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY
|
|
+ )
|
|
+ }
|
|
+
|
|
+ fun setStuntState(level: LevelAccessor, gemBlocks: List<Pair<BlockState, BlockPos>>, stunted: Boolean) {
|
|
+ for ((_, gemPos) in gemBlocks) {
|
|
+ for (dir in Direction.entries) {
|
|
+ val neighborPos = gemPos.relative(dir)
|
|
+ val neighborState = level.getBlockState(neighborPos)
|
|
+
|
|
+ if (neighborState.block is TypeGemClusterBlock) {
|
|
+ if (neighborState.hasProperty(STUNTED) && neighborState.getValue(STUNTED) != stunted) {
|
|
+
|
|
+ var updatedState = neighborState.setValue(STUNTED, stunted)
|
|
+
|
|
+ if (updatedState.hasProperty(SHOULD_GROW)) {
|
|
+ updatedState = updatedState.setValue(SHOULD_GROW, !stunted)
|
|
+ }
|
|
+
|
|
+ level.setBlock(neighborPos, updatedState, 3)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun getConnectedGemBlocks(level: BlockGetter, pos: BlockPos): List<Pair<BlockState, BlockPos>> {
|
|
+ val connectedGems = mutableListOf<Pair<BlockState, BlockPos>>()
|
|
+ val visited = mutableSetOf<BlockPos>()
|
|
+ val queue: Queue<BlockPos> = LinkedList()
|
|
+
|
|
+ visited.add(pos)
|
|
+ queue.add(pos)
|
|
+ connectedGems.add(level.getBlockState(pos) to pos)
|
|
+
|
|
+ while (queue.isNotEmpty()) {
|
|
+ val current = queue.poll()
|
|
+
|
|
+ for (direction in Direction.entries) {
|
|
+ val neighbor = current.relative(direction)
|
|
+ if (visited.add(neighbor)) {
|
|
+ val state = level.getBlockState(neighbor)
|
|
+ if (state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)) {
|
|
+ connectedGems.add(state to neighbor)
|
|
+ queue.add(neighbor)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return connectedGems
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
|
+ grow(level, pos, random)
|
|
+ }
|
|
+
|
|
+ fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
|
|
+ val desiredLength = MAX_CONNECTED_GEMS * percentage
|
|
+
|
|
+ while (true) {
|
|
+ val growInfo = grow(level, pos, random, true)
|
|
+ val grown = growInfo.first
|
|
+ val clusterSize = growInfo.second
|
|
+
|
|
+ if (!grown || clusterSize >= desiredLength) break
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun grow(
|
|
+ level: WorldGenLevel,
|
|
+ pos: BlockPos,
|
|
+ random: RandomSource,
|
|
+ forced: Boolean = false
|
|
+ ): Pair<Boolean, Int> {
|
|
+ val connectedGems = getConnectedGemBlocks(level, pos) // maybe we can store this and access it for better performance? check for changes in list rather than checking each time grow is called? idk
|
|
+ val gemCount = connectedGems.size
|
|
+ val overLimit = gemCount >= MAX_CONNECTED_GEMS
|
|
+
|
|
+ if (overLimit) {
|
|
+ // if there is no open air pockets to grow any new clusters then exit early
|
|
+ if (!hasBreathingRoom(level, connectedGems)) {
|
|
+ return Pair(false, gemCount)
|
|
+ }
|
|
+ // max type gem block count met, tell clusters to grow but not turn into blocks
|
|
+ setStuntState(level, connectedGems, true)
|
|
+ //return Pair(false, gemCount)
|
|
+ } else {
|
|
+ // make sure connected clusters know they can grow and turn into blocks
|
|
+ setStuntState(level, connectedGems, false)
|
|
+ }
|
|
+
|
|
+ val javaRandom = java.util.Random(random.nextLong())
|
|
+
|
|
+ for ((gemState, gemPos) in connectedGems.shuffled(javaRandom)) {
|
|
+ val registryKey = BuiltInRegistries.BLOCK.getKey(gemState.block)
|
|
+ val clusterBlock = BLOCK_TO_CLUSTER[registryKey]
|
|
+
|
|
+ for (dir in Direction.entries.shuffled(javaRandom)) {
|
|
+ val targetPos = gemPos.relative(dir)
|
|
+ val targetState = level.getBlockState(targetPos)
|
|
+
|
|
+ if (!targetState.isAir) continue
|
|
+ if (!isPositionValidForGrowth(level, targetPos)) continue
|
|
+
|
|
+ // Place gem cluster
|
|
+ if (clusterBlock != null) {
|
|
+ var placeState = clusterBlock.defaultBlockState()
|
|
+ .setValue(DirectionalBlock.FACING, dir)
|
|
+ .setValue(TypeGemClusterBlock.STAGE, 0)
|
|
+ .setValue(SHOULD_GROW, true)
|
|
+
|
|
+ level.setBlock(targetPos, placeState, UPDATE_ALL)
|
|
+ }
|
|
+
|
|
+ if (forced && clusterBlock != null) {
|
|
+ forceAdvanceClusterGrowth(level, targetPos, random)
|
|
+ }
|
|
+
|
|
+ val updatedGemCount = if (forced) getConnectedGemBlocks(level, pos).size else connectedGems.size
|
|
+ return Pair(true, updatedGemCount)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // println("[TypeGemCoreBlock] No valid spot found for gem placement.")
|
|
+ return Pair(false, connectedGems.size)
|
|
+ }
|
|
+
|
|
+ private fun forceAdvanceClusterGrowth(level: WorldGenLevel, clusterPos: BlockPos, random: RandomSource) {
|
|
+ repeat(5) {
|
|
+ val clusterState = level.getBlockState(clusterPos)
|
|
+ val clusterBlock = clusterState.block as? TypeGemClusterBlock ?: return
|
|
+ clusterBlock.advanceGrowth(clusterState, level, clusterPos, random)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun hasBreathingRoom(level: WorldGenLevel, gems: List<Pair<BlockState, BlockPos>>): Boolean {
|
|
+ for ((_, gemPos) in gems) {
|
|
+ for (dir in Direction.entries) {
|
|
+ if (level.getBlockState(gemPos.relative(dir)).isAir) {
|
|
+ return true
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ private fun isPositionValidForGrowth(level: WorldGenLevel, pos: BlockPos): Boolean {
|
|
+ return Direction.entries.none { dir ->
|
|
+ val nearby = pos.relative(dir)
|
|
+ isGem(level.getBlockState(nearby)) &&
|
|
+ pos.distManhattan(nearby) < MIN_DISTANCE_BETWEEN_GEMS
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun isGem(state: BlockState): Boolean {
|
|
+ return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
|
|
+ }
|
|
+
|
|
+ override fun isRandomlyTicking(state: BlockState): Boolean = true
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/FossilMultiblockEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/FossilMultiblockEntity.kt
|
|
index c34a7e78b8..307393c80a 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/FossilMultiblockEntity.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/FossilMultiblockEntity.kt
|
|
@@ -12,15 +12,35 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
|
|
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
|
import com.cobblemon.mod.common.api.multiblock.MultiblockStructure
|
|
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.api.types.ElementalTypes
|
|
+import com.cobblemon.mod.common.block.MonitorBlock
|
|
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
|
import com.cobblemon.mod.common.util.DataKeys
|
|
+import com.cobblemon.mod.common.util.tmList
|
|
import net.minecraft.core.BlockPos
|
|
import net.minecraft.core.HolderLookup
|
|
import net.minecraft.nbt.CompoundTag
|
|
+import net.minecraft.nbt.NbtOps
|
|
import net.minecraft.nbt.NbtUtils
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
import net.minecraft.world.level.ChunkPos
|
|
+import net.minecraft.world.Containers
|
|
+import net.minecraft.world.InteractionResult
|
|
+import net.minecraft.world.ItemInteractionResult
|
|
+import net.minecraft.world.InteractionHand
|
|
+import net.minecraft.world.entity.item.ItemEntity
|
|
+import net.minecraft.world.entity.player.Player
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import net.minecraft.world.item.Items
|
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
|
import net.minecraft.world.level.block.state.BlockState
|
|
+import net.minecraft.world.level.Level
|
|
+import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
|
+import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
|
+import net.minecraft.world.level.gameevent.GameEvent
|
|
|
|
open class FossilMultiblockEntity(
|
|
pos: BlockPos,
|
|
@@ -28,8 +48,30 @@ open class FossilMultiblockEntity(
|
|
multiblockBuilder: MultiblockStructureBuilder,
|
|
type: BlockEntityType<*> = CobblemonBlockEntities.FOSSIL_MULTIBLOCK
|
|
) : MultiblockEntity(type, pos, state, multiblockBuilder) {
|
|
+ private val musicDiscs = setOf(
|
|
+ Items.MUSIC_DISC_13,
|
|
+ Items.MUSIC_DISC_CAT,
|
|
+ Items.MUSIC_DISC_BLOCKS,
|
|
+ Items.MUSIC_DISC_CHIRP,
|
|
+ Items.MUSIC_DISC_FAR,
|
|
+ Items.MUSIC_DISC_MALL,
|
|
+ Items.MUSIC_DISC_MELLOHI,
|
|
+ Items.MUSIC_DISC_STAL,
|
|
+ Items.MUSIC_DISC_STRAD,
|
|
+ Items.MUSIC_DISC_WARD,
|
|
+ Items.MUSIC_DISC_11,
|
|
+ Items.MUSIC_DISC_WAIT,
|
|
+ Items.MUSIC_DISC_OTHERSIDE,
|
|
+ Items.MUSIC_DISC_5,
|
|
+ Items.MUSIC_DISC_RELIC,
|
|
+ Items.MUSIC_DISC_PRECIPICE,
|
|
+ Items.MUSIC_DISC_CREATOR,
|
|
+ Items.MUSIC_DISC_CREATOR_MUSIC_BOX,
|
|
+ Items.MUSIC_DISC_PIGSTEP
|
|
+ )
|
|
|
|
override var masterBlockPos: BlockPos? = null
|
|
+ private var diskStack: ItemStack = ItemStack.EMPTY
|
|
|
|
override var multiblockStructure: MultiblockStructure? = null
|
|
set(structure) {
|
|
@@ -76,6 +118,164 @@ open class FossilMultiblockEntity(
|
|
} else {
|
|
null
|
|
}
|
|
+ diskStack = if (nbt.contains(DataKeys.MONITOR_DISK)) {
|
|
+ ItemStack.parse(registryLookup, nbt.get(DataKeys.MONITOR_DISK)).orElse(ItemStack.EMPTY)
|
|
+ } else {
|
|
+ ItemStack.EMPTY
|
|
+ }
|
|
+ updateMonitorScreen()
|
|
+ }
|
|
+
|
|
+ override fun saveAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider) {
|
|
+ super.saveAdditional(nbt, registryLookup)
|
|
+ if (!diskStack.isEmpty) {
|
|
+ nbt.put(DataKeys.MONITOR_DISK, ItemStack.CODEC.encodeStart(NbtOps.INSTANCE, diskStack).orThrow)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun handleUseItem(
|
|
+ stack: ItemStack,
|
|
+ state: BlockState,
|
|
+ level: Level,
|
|
+ pos: BlockPos,
|
|
+ player: Player,
|
|
+ hand: InteractionHand
|
|
+ ): ItemInteractionResult {
|
|
+ val handStack = player.getItemInHand(hand)
|
|
+ if (!isValidDisk(handStack)) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
|
+ if (multiblockStructure != null) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
|
+
|
|
+ if (level.isClientSide) return ItemInteractionResult.SUCCESS
|
|
+
|
|
+ val newDisk = handStack.copyWithCount(1)
|
|
+ // In creative, prevent duplicate ejection spam when repeatedly inserting the same disk.
|
|
+ if (player.isCreative && !diskStack.isEmpty && ItemStack.isSameItemSameComponents(diskStack, newDisk)) {
|
|
+ return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ val oldStack = diskStack
|
|
+ if (!oldStack.isEmpty) {
|
|
+ if (player is ServerPlayer) {
|
|
+ unlockTmForPlayer(player, oldStack)
|
|
+ }
|
|
+ ejectDiskStack(level, pos, state, oldStack)
|
|
+ }
|
|
+
|
|
+ diskStack = newDisk
|
|
+ if (!player.isCreative) {
|
|
+ handStack.shrink(1)
|
|
+ }
|
|
+
|
|
+ if (player is ServerPlayer) {
|
|
+ unlockTmForPlayer(player, diskStack)
|
|
+ }
|
|
+
|
|
+ updateMonitorScreen()
|
|
+ markUpdated(level, pos, state)
|
|
+
|
|
+ return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ fun handleUseWithoutItem(
|
|
+ state: BlockState,
|
|
+ level: Level,
|
|
+ pos: BlockPos,
|
|
+ player: Player
|
|
+ ): InteractionResult {
|
|
+ if (diskStack.isEmpty) return InteractionResult.PASS
|
|
+ if (multiblockStructure != null) return InteractionResult.PASS
|
|
+
|
|
+ if (player is ServerPlayer) {
|
|
+ unlockTmForPlayer(player, diskStack)
|
|
+ }
|
|
+ ejectDiskStack(level, pos, state, diskStack)
|
|
+ diskStack = ItemStack.EMPTY
|
|
+ updateMonitorScreen()
|
|
+ markUpdated(level, pos, state)
|
|
+
|
|
+ return InteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ fun dropDisk(level: Level, pos: BlockPos, state: BlockState) {
|
|
+ if (diskStack.isEmpty) return
|
|
+ ejectDiskStack(level, pos, state, diskStack)
|
|
+ diskStack = ItemStack.EMPTY
|
|
+ }
|
|
+
|
|
+ private fun ejectDiskStack(level: Level, pos: BlockPos, state: BlockState, stack: ItemStack) {
|
|
+ val facing = if (state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) {
|
|
+ state.getValue(HorizontalDirectionalBlock.FACING)
|
|
+ } else {
|
|
+ null
|
|
+ }
|
|
+ if (facing == null) {
|
|
+ Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, stack)
|
|
+ return
|
|
+ }
|
|
+
|
|
+ val ejectFacing = facing.opposite
|
|
+ val x = pos.x + 0.5 + ejectFacing.stepX * 0.6
|
|
+ val y = pos.y + 0.5
|
|
+ val z = pos.z + 0.5 + ejectFacing.stepZ * 0.6
|
|
+ val itemEntity = ItemEntity(level, x, y, z, stack)
|
|
+ itemEntity.setDeltaMovement(ejectFacing.stepX * 0.15, 0.05, ejectFacing.stepZ * 0.15)
|
|
+ level.addFreshEntity(itemEntity)
|
|
+ }
|
|
+
|
|
+ private fun isValidDisk(stack: ItemStack): Boolean {
|
|
+ return stack.item is TechnicalMachineItem || musicDiscs.contains(stack.item)
|
|
+ }
|
|
+
|
|
+ private fun unlockTmForPlayer(player: ServerPlayer, stack: ItemStack) {
|
|
+ if (stack.item !is TechnicalMachineItem) return
|
|
+ val move = TMMoveComponent.getTMMove(stack) ?: return
|
|
+ val tmId = TechnicalMachines.moveToTM[move]?.id ?: return
|
|
+ player.tmList()?.learn(listOf(tmId))
|
|
+ }
|
|
+
|
|
+ private fun updateMonitorScreen() {
|
|
+ val level = level ?: return
|
|
+ if (level.isClientSide) return
|
|
+ if (multiblockStructure != null) return
|
|
+
|
|
+ val state = blockState
|
|
+ if (!state.hasProperty(MonitorBlock.SCREEN)) return
|
|
+ val targetScreen = getDiskScreen()
|
|
+ if (state.getValue(MonitorBlock.SCREEN) != targetScreen) {
|
|
+ level.setBlockAndUpdate(blockPos, state.setValue(MonitorBlock.SCREEN, targetScreen))
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun getDiskScreen(): MonitorBlock.MonitorScreen {
|
|
+ if (diskStack.isEmpty) return MonitorBlock.MonitorScreen.OFF
|
|
+ if (musicDiscs.contains(diskStack.item)) return MonitorBlock.MonitorScreen.MUSIC
|
|
+ val move = TMMoveComponent.getTMMove(diskStack) ?: return MonitorBlock.MonitorScreen.TM_NORMAL
|
|
+ return when (move.elementalType) {
|
|
+ ElementalTypes.FIRE -> MonitorBlock.MonitorScreen.TM_FIRE
|
|
+ ElementalTypes.WATER -> MonitorBlock.MonitorScreen.TM_WATER
|
|
+ ElementalTypes.GRASS -> MonitorBlock.MonitorScreen.TM_GRASS
|
|
+ ElementalTypes.ELECTRIC -> MonitorBlock.MonitorScreen.TM_ELECTRIC
|
|
+ ElementalTypes.ICE -> MonitorBlock.MonitorScreen.TM_ICE
|
|
+ ElementalTypes.FIGHTING -> MonitorBlock.MonitorScreen.TM_FIGHTING
|
|
+ ElementalTypes.POISON -> MonitorBlock.MonitorScreen.TM_POISON
|
|
+ ElementalTypes.GROUND -> MonitorBlock.MonitorScreen.TM_GROUND
|
|
+ ElementalTypes.FLYING -> MonitorBlock.MonitorScreen.TM_FLYING
|
|
+ ElementalTypes.PSYCHIC -> MonitorBlock.MonitorScreen.TM_PSYCHIC
|
|
+ ElementalTypes.BUG -> MonitorBlock.MonitorScreen.TM_BUG
|
|
+ ElementalTypes.ROCK -> MonitorBlock.MonitorScreen.TM_ROCK
|
|
+ ElementalTypes.GHOST -> MonitorBlock.MonitorScreen.TM_GHOST
|
|
+ ElementalTypes.DRAGON -> MonitorBlock.MonitorScreen.TM_DRAGON
|
|
+ ElementalTypes.DARK -> MonitorBlock.MonitorScreen.TM_DARK
|
|
+ ElementalTypes.STEEL -> MonitorBlock.MonitorScreen.TM_STEEL
|
|
+ ElementalTypes.FAIRY -> MonitorBlock.MonitorScreen.TM_FAIRY
|
|
+ else -> MonitorBlock.MonitorScreen.TM_NORMAL
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun markUpdated(level: Level, pos: BlockPos, state: BlockState) {
|
|
+ level.sendBlockUpdated(pos, state, state, 3)
|
|
+ level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state))
|
|
+ setChanged()
|
|
}
|
|
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMMachineBlockEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMMachineBlockEntity.kt
|
|
new file mode 100644
|
|
index 0000000000..09bffea5db
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMMachineBlockEntity.kt
|
|
@@ -0,0 +1,383 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.block.entity
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlockEntities
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.moves.Moves
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
+import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.util.itemRegistry
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.HolderLookup
|
|
+import net.minecraft.core.NonNullList
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.network.protocol.Packet
|
|
+import net.minecraft.network.protocol.game.ClientGamePacketListener
|
|
+import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
|
+import net.minecraft.world.ContainerHelper
|
|
+import net.minecraft.world.SimpleContainer
|
|
+import net.minecraft.world.entity.player.Inventory
|
|
+import net.minecraft.world.entity.player.Player
|
|
+import net.minecraft.world.inventory.AbstractContainerMenu
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
|
+import net.minecraft.world.level.block.state.BlockState
|
|
+import net.minecraft.nbt.CompoundTag
|
|
+import net.minecraft.sounds.SoundEvent
|
|
+import net.minecraft.sounds.SoundSource
|
|
+import net.minecraft.world.Container
|
|
+import net.minecraft.world.inventory.ContainerData
|
|
+import net.minecraft.world.level.Level
|
|
+
|
|
+class TMMachineBlockEntity(pos: BlockPos, state: BlockState) :
|
|
+ BaseContainerBlockEntity(CobblemonBlockEntities.TM_MACHINE, pos, state) {
|
|
+ companion object {
|
|
+ const val BURN_ACTIVE_TAG = "burnActive"
|
|
+ const val BURN_PROGRESS_TAG = "burnProgress"
|
|
+ const val REPEAT_PROCESS_TAG = "repeatProcess"
|
|
+ const val ACTIVE_MOVE_TAG = "activeMove"
|
|
+
|
|
+ const val BURN_PROGRESS_PER_TICK = 2
|
|
+ const val BURN_TOTAL_TIME = 200
|
|
+ const val TOTAL_PROCESS_TIME =
|
|
+ BURN_TOTAL_TIME + ((TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS) * BURN_PROGRESS_PER_TICK)
|
|
+
|
|
+ // Container data IDs
|
|
+ const val BURN_PROGRESS_INDEX = 0
|
|
+ const val BURN_ACTIVE_INDEX = 1
|
|
+ const val REPEAT_PROCESS_INDEX = 2
|
|
+ const val POSITION_X_INDEX = 3
|
|
+ const val POSITION_Y_INDEX = 4
|
|
+ const val POSITION_Z_INDEX = 5
|
|
+
|
|
+ fun serverTick(level: Level, pos: BlockPos, state: BlockState, blockEntity: TMMachineBlockEntity) {
|
|
+ if (level.isClientSide) return
|
|
+
|
|
+ // check if the TMM is powered by redstone. For toggling automation
|
|
+ val powered = level.hasNeighborSignal(pos)
|
|
+ val data = blockEntity.containerData
|
|
+
|
|
+ // Progress while active
|
|
+ if (data.get(BURN_ACTIVE_INDEX) == 1) {
|
|
+ val currentProgress = data.get(BURN_PROGRESS_INDEX)
|
|
+ if (currentProgress < TOTAL_PROCESS_TIME) {
|
|
+ val progressPerTick = if (currentProgress >= BURN_TOTAL_TIME) 1 else BURN_PROGRESS_PER_TICK
|
|
+ data.set(BURN_PROGRESS_INDEX, currentProgress + progressPerTick)
|
|
+
|
|
+ // comparator progress bar updates
|
|
+ level.updateNeighbourForOutputSignal(pos, state.block)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // auto start with batching when powered
|
|
+ if (powered && data.get(BURN_ACTIVE_INDEX) == 0) {
|
|
+ if (blockEntity.canCraftSelectedTM()) {
|
|
+ data.set(BURN_ACTIVE_INDEX, 1)
|
|
+ data.set(BURN_PROGRESS_INDEX, 0)
|
|
+ blockEntity.setChanged()
|
|
+ level.updateNeighbourForOutputSignal(pos, state.block)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ val burnProgressValue = data.get(BURN_PROGRESS_INDEX)
|
|
+ val postCraftTicks = if (burnProgressValue >= BURN_TOTAL_TIME) (burnProgressValue - BURN_TOTAL_TIME) else 0
|
|
+
|
|
+ if (postCraftTicks > 0) {
|
|
+ if (postCraftTicks == TMMachineScreen.CRAFT_TICKS) {
|
|
+ blockEntity.craftTM()
|
|
+ level.updateNeighbourForOutputSignal(pos, state.block)
|
|
+ }
|
|
+
|
|
+ if (postCraftTicks >= (TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS)) {
|
|
+ data.set(BURN_PROGRESS_INDEX, 0)
|
|
+
|
|
+ // use batch processing while powered with redstone
|
|
+ val keepRunning = powered && blockEntity.canCraftSelectedTM()
|
|
+ data.set(BURN_ACTIVE_INDEX, if (keepRunning) 1 else 0)
|
|
+
|
|
+ blockEntity.setChanged()
|
|
+ level.updateNeighbourForOutputSignal(pos, state.block)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ var tmMachineInventory = TMMachineBlockInventory(this)
|
|
+ var partialTicks: Float = 0F
|
|
+
|
|
+ var burnProgress: Int = 0
|
|
+ var burnActive: Boolean = false
|
|
+ var repeatProcess: Boolean = false
|
|
+ var activeMove: String = ""
|
|
+
|
|
+ var containerData: ContainerData = object : ContainerData {
|
|
+ override fun getCount() = 6
|
|
+ override fun get(index: Int): Int {
|
|
+ return when (index) {
|
|
+ BURN_PROGRESS_INDEX -> burnProgress
|
|
+ BURN_ACTIVE_INDEX -> if (burnActive) 1 else 0
|
|
+ REPEAT_PROCESS_INDEX -> if (repeatProcess) 1 else 0
|
|
+ POSITION_X_INDEX -> blockPos.x
|
|
+ POSITION_Y_INDEX -> blockPos.y
|
|
+ POSITION_Z_INDEX -> blockPos.z
|
|
+ else -> 0
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun set(index: Int, value: Int) {
|
|
+ when (index) {
|
|
+ BURN_PROGRESS_INDEX -> burnProgress = value
|
|
+ BURN_ACTIVE_INDEX -> burnActive = value == 1
|
|
+ REPEAT_PROCESS_INDEX -> repeatProcess = value == 1
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ private fun canCraftSelectedTM(): Boolean {
|
|
+ val move = Moves.getByName(activeMove) ?: return false
|
|
+ val tm = TechnicalMachines.moveToTM[move] ?: return false
|
|
+
|
|
+ // We must have the Output be empty or stackable and not full
|
|
+ val resultStack = getItem(TMMachineMenu.RESULT_SLOT)
|
|
+ val crafted = ItemStack(CobblemonItems.TECHNICAL_MACHINE).also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
|
+ val outputOk = resultStack.isEmpty || (
|
|
+ resultStack.count < resultStack.maxStackSize &&
|
|
+ ItemStack.isSameItemSameComponents(resultStack, crafted)
|
|
+ )
|
|
+ if (!outputOk) return false
|
|
+
|
|
+ // Blank disc slot
|
|
+ if (getItem(TMMachineMenu.BLANK_TM_SLOT).item != CobblemonItems.BLANK_TM) return false
|
|
+
|
|
+ // Ingredient slots
|
|
+ val recipes = tm.getClampedRecipe() ?: emptyList()
|
|
+ for ((index, recipe) in recipes.withIndex()) {
|
|
+ val slot = TMMachineMenu.INGREDIENT_SLOTS.first + index
|
|
+ val provided = getItem(slot)
|
|
+ if (!recipe.ingredient.test(provided) || provided.count < recipe.count) return false
|
|
+ }
|
|
+
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ private fun craftTM() {
|
|
+ if (!(level?.isClientSide ?: true)) {
|
|
+ Moves.getByName(activeMove)?.let { move ->
|
|
+ TechnicalMachines.moveToTM[move]?.let { tm ->
|
|
+ val resultStack = getItem(TMMachineMenu.RESULT_SLOT)
|
|
+ if (!resultStack.isEmpty && resultStack.count >= resultStack.maxStackSize) return
|
|
+
|
|
+ // Craft the TM
|
|
+ val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
|
+ .also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
|
+
|
|
+ if (!resultStack.isEmpty && !ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)) return
|
|
+
|
|
+ val blankTmStack = getItem(TMMachineMenu.BLANK_TM_SLOT)
|
|
+ if (blankTmStack.item != CobblemonItems.BLANK_TM) return
|
|
+
|
|
+ val recipes = tm.getClampedRecipe() ?: emptyList()
|
|
+ for ((index, recipe) in recipes.withIndex()) {
|
|
+ val slot = TMMachineMenu.INGREDIENT_SLOTS.first + index
|
|
+ val provided = getItem(slot)
|
|
+ if (!recipe.ingredient.test(provided) || provided.count < recipe.count) return
|
|
+ }
|
|
+
|
|
+ // Consume ingredients
|
|
+ blankTmStack.shrink(1)
|
|
+ for ((index, ingredient) in recipes.withIndex()) {
|
|
+ getItem(TMMachineMenu.INGREDIENT_SLOTS.first + index).shrink(ingredient.count)
|
|
+ }
|
|
+
|
|
+ // Add crafted TM to result slot
|
|
+ if (resultStack.isEmpty) {
|
|
+ setItem(TMMachineMenu.RESULT_SLOT, craftedTmStack)
|
|
+ } else if (resultStack.count < resultStack.maxStackSize) {
|
|
+ resultStack.grow(1)
|
|
+ }
|
|
+
|
|
+ tmMachineInventory.setChanged()
|
|
+ setChanged()
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun createMenu(containerId: Int, inventory: Inventory): AbstractContainerMenu {
|
|
+ return TMMachineMenu(containerId, inventory, tmMachineInventory, containerData)
|
|
+ }
|
|
+
|
|
+ override fun saveAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
|
|
+ super.saveAdditional(compound, registries)
|
|
+ compound.putBoolean(BURN_ACTIVE_TAG, burnActive)
|
|
+ compound.putBoolean(REPEAT_PROCESS_TAG, repeatProcess)
|
|
+ compound.putInt(BURN_PROGRESS_TAG, burnProgress)
|
|
+ compound.putString(ACTIVE_MOVE_TAG, activeMove)
|
|
+ ContainerHelper.saveAllItems(compound, tmMachineInventory.items, registries)
|
|
+ }
|
|
+
|
|
+ override fun loadAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
|
|
+ super.loadAdditional(compound, registries)
|
|
+ burnActive = compound.getBoolean(BURN_ACTIVE_TAG)
|
|
+ repeatProcess = compound.getBoolean(REPEAT_PROCESS_TAG)
|
|
+ burnProgress = compound.getInt(BURN_PROGRESS_TAG)
|
|
+ activeMove = compound.getString(ACTIVE_MOVE_TAG)
|
|
+ ContainerHelper.loadAllItems(compound, tmMachineInventory.items, registries)
|
|
+ }
|
|
+
|
|
+ override fun getDisplayName(): Component = Component.translatable("block.cobblemon.tm_machine")
|
|
+
|
|
+ override fun getDefaultName(): Component = Component.translatable("cobblemon.container.tm_machine")
|
|
+
|
|
+ override fun getItems(): NonNullList<ItemStack> = tmMachineInventory.items
|
|
+
|
|
+ override fun setItems(items: NonNullList<ItemStack>) {
|
|
+ for (i in items.indices) {
|
|
+ tmMachineInventory.setItem(i, items[i])
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
|
+
|
|
+ override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag = saveWithoutMetadata(registryLookup)
|
|
+
|
|
+ override fun getContainerSize(): Int = tmMachineInventory.containerSize
|
|
+
|
|
+ override fun isEmpty(): Boolean = tmMachineInventory.isEmpty
|
|
+
|
|
+ override fun getItem(slot: Int): ItemStack = tmMachineInventory.getItem(slot)
|
|
+
|
|
+ override fun removeItem(slot: Int, amount: Int): ItemStack = tmMachineInventory.removeItem(slot, amount)
|
|
+
|
|
+ override fun removeItemNoUpdate(slot: Int): ItemStack = tmMachineInventory.removeItemNoUpdate(slot)
|
|
+
|
|
+ override fun setItem(slot: Int, stack: ItemStack) = tmMachineInventory.setItem(slot, stack)
|
|
+
|
|
+ override fun setChanged() {
|
|
+ var currentState = blockState
|
|
+ level?.getBlockState(worldPosition)?.let { state ->
|
|
+ currentState = state
|
|
+ var updated = false
|
|
+
|
|
+ if (state.hasProperty(TMMachineBlock.EMPTY)) {
|
|
+ val isEmpty = getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty
|
|
+ if (currentState.getValue(TMMachineBlock.EMPTY) != isEmpty) {
|
|
+ if (!isEmpty) playSound(CobblemonSounds.TM_MACHINE_PLACE_DISC, 0.5F)
|
|
+ currentState = currentState.setValue(TMMachineBlock.EMPTY, isEmpty)
|
|
+ updated = true
|
|
+ }
|
|
+ }
|
|
+ if (state.hasProperty(TMMachineBlock.ACTIVE)) {
|
|
+ val isActive = containerData.get(BURN_ACTIVE_INDEX) == 1
|
|
+ if (currentState.getValue(TMMachineBlock.ACTIVE) != isActive) {
|
|
+ currentState = currentState.setValue(TMMachineBlock.ACTIVE, isActive)
|
|
+ updated = true
|
|
+ }
|
|
+ }
|
|
+ if (updated) level?.setBlockAndUpdate(worldPosition, currentState)
|
|
+ }
|
|
+
|
|
+ // Notify the block entity's level that this block entity has changed
|
|
+ level?.blockEntityChanged(worldPosition)
|
|
+
|
|
+ // Mark the chunk containing this block entity as dirty, ensuring it is saved
|
|
+ level?.getChunkAt(worldPosition)?.isUnsaved = true
|
|
+
|
|
+ // Update Neighbours
|
|
+ level?.updateNeighborsAt(blockPos, currentState.block)
|
|
+ }
|
|
+
|
|
+ override fun stillValid(player: Player): Boolean = tmMachineInventory.stillValid(player)
|
|
+
|
|
+ override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean = tmMachineInventory.canPlaceItem(slot, stack)
|
|
+
|
|
+ override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean =
|
|
+ tmMachineInventory.canTakeItem(target, slot, stack)
|
|
+
|
|
+ fun playSound(soundEvent: SoundEvent, volume: Float = 1F, pitch: Float = 1F) {
|
|
+ level?.playSound(null, worldPosition, soundEvent, SoundSource.BLOCKS, volume, pitch)
|
|
+ }
|
|
+
|
|
+ fun isReadyToCraft(): Boolean = canCraftSelectedTM()
|
|
+
|
|
+ fun isOutputBlocked(): Boolean {
|
|
+ val level = level ?: return false
|
|
+ val move = Moves.getByName(activeMove) ?: return false
|
|
+ val tm = TechnicalMachines.moveToTM[move] ?: return false
|
|
+
|
|
+ val resultStack = getItem(TMMachineMenu.RESULT_SLOT)
|
|
+ if (resultStack.isEmpty) return false
|
|
+
|
|
+ val crafted = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
|
+ .also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
|
+
|
|
+ // If wrong TM in output OR output is full
|
|
+ if (!ItemStack.isSameItemSameComponents(resultStack, crafted)) return true
|
|
+ return resultStack.count >= resultStack.maxStackSize
|
|
+ }
|
|
+
|
|
+ class TMMachineBlockInventory(val blockEntity: TMMachineBlockEntity) : SimpleContainer(6) {
|
|
+ override fun canTakeItem(target: Container, slot: Int, stack: ItemStack) = slot == 0
|
|
+ override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean {
|
|
+ val powered = blockEntity.level?.hasNeighborSignal(blockEntity.blockPos) == true
|
|
+ val allowAutomation = blockEntity.containerData.get(BURN_ACTIVE_INDEX) == 1 || powered
|
|
+
|
|
+ if (!allowAutomation) return false
|
|
+
|
|
+ Moves.getByName(blockEntity.activeMove)?.let {
|
|
+ val tm = TechnicalMachines.moveToTM[it] ?: return false
|
|
+ val item = stack.item
|
|
+
|
|
+ return when (slot) {
|
|
+ 1 -> item == CobblemonItems.BLANK_TM
|
|
+ 2, 3, 4 -> {
|
|
+ val recipe = tm.getClampedRecipe()
|
|
+ val recipeIndex = slot - 2
|
|
+ if (recipe == null || recipeIndex >= recipe.size) {
|
|
+ return false
|
|
+ }
|
|
+ recipe[recipeIndex].ingredient.test(stack)
|
|
+ }
|
|
+ else -> false
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ override fun setChanged() {
|
|
+ super.setChanged()
|
|
+ blockEntity.setChanged()
|
|
+
|
|
+ val allowAutomation =
|
|
+ blockEntity.containerData.get(BURN_ACTIVE_INDEX) == 1 || blockEntity.containerData.get(
|
|
+ REPEAT_PROCESS_INDEX
|
|
+ ) == 1
|
|
+
|
|
+ // force comparators to refresh if inventory changes and batch mode is on
|
|
+ if (allowAutomation) {
|
|
+ blockEntity.level?.updateNeighbourForOutputSignal(blockEntity.blockPos, blockEntity.blockState.block)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun startOpen(player: Player) {
|
|
+ blockEntity.startOpen(player)
|
|
+ }
|
|
+
|
|
+ override fun stopOpen(player: Player) {
|
|
+ blockEntity.stopOpen(player)
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMShelfBlockEntity.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMShelfBlockEntity.kt
|
|
new file mode 100644
|
|
index 0000000000..ae2e034638
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/entity/TMShelfBlockEntity.kt
|
|
@@ -0,0 +1,302 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.block.entity
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlockEntities
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.block.TMShelfBlock
|
|
+import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.core.HolderLookup
|
|
+import net.minecraft.core.NonNullList
|
|
+import net.minecraft.nbt.CompoundTag
|
|
+import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
|
+import net.minecraft.sounds.SoundEvent
|
|
+import net.minecraft.sounds.SoundSource
|
|
+import net.minecraft.world.ContainerHelper
|
|
+import net.minecraft.world.Containers
|
|
+import net.minecraft.world.InteractionResult
|
|
+import net.minecraft.world.ItemInteractionResult
|
|
+import net.minecraft.world.WorldlyContainer
|
|
+import net.minecraft.world.entity.player.Player
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import net.minecraft.world.item.Items
|
|
+import net.minecraft.world.level.Level
|
|
+import net.minecraft.world.level.block.entity.BlockEntity
|
|
+import net.minecraft.world.level.block.state.BlockState
|
|
+import net.minecraft.world.phys.BlockHitResult
|
|
+import net.minecraft.world.level.gameevent.GameEvent
|
|
+import java.util.OptionalInt
|
|
+import kotlin.math.pow
|
|
+
|
|
+class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.TM_SHELF, pos, state), WorldlyContainer {
|
|
+ val items: NonNullList<ItemStack> = NonNullList.withSize(14, ItemStack.EMPTY)
|
|
+ var lastInteractedSlot: Int = -1
|
|
+ private val accessibleSlots = IntArray(14) { it }
|
|
+ private var lastNotePulseTick: Long = Long.MIN_VALUE
|
|
+
|
|
+ fun handleUseItem(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): ItemInteractionResult {
|
|
+ if (stack.isEmpty) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
|
+ val slot = getHitSlot(hit, state).orElse(-1)
|
|
+ if (slot !in 0 until 14) return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
|
+ if (!isValidItem(stack)) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
|
+
|
|
+ val oldStack = items[slot]
|
|
+ if (!oldStack.isEmpty) {
|
|
+ if (!player.addItem(oldStack)) {
|
|
+ Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, oldStack)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ items[slot] = stack.copyWithCount(1)
|
|
+ stack.shrink(1)
|
|
+ lastInteractedSlot = slot
|
|
+ updateBlockState(level, pos)
|
|
+ markUpdated()
|
|
+
|
|
+ return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ fun handleUseWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
|
|
+ val slot = getHitSlot(hit, state).orElse(-1)
|
|
+ if (slot !in 0 until 14 || items[slot].isEmpty) return InteractionResult.PASS
|
|
+
|
|
+ val removed = items[slot]
|
|
+ items[slot] = ItemStack.EMPTY
|
|
+ if (!player.addItem(removed)) {
|
|
+ Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, removed)
|
|
+ }
|
|
+
|
|
+ lastInteractedSlot = slot
|
|
+ updateBlockState(level, pos)
|
|
+
|
|
+ return InteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ private fun updateBlockState(level: Level, pos: BlockPos) {
|
|
+ val newState = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.fold(blockState) { acc, prop ->
|
|
+ val index = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.indexOf(prop)
|
|
+ acc.setValue(prop, !items[index].isEmpty)
|
|
+ }
|
|
+ level.setBlock(pos, newState, 3)
|
|
+ level.updateNeighbourForOutputSignal(pos, newState.block)
|
|
+ level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState))
|
|
+ }
|
|
+
|
|
+ private fun getHitSlot(hit: BlockHitResult, state: BlockState): OptionalInt {
|
|
+ val facing = state.getValue(TMShelfBlock.FACING)
|
|
+ if (hit.direction != facing) {
|
|
+ return OptionalInt.empty()
|
|
+ }
|
|
+
|
|
+ val relative = hit.location.subtract(hit.blockPos.x.toDouble(), hit.blockPos.y.toDouble(), hit.blockPos.z.toDouble())
|
|
+
|
|
+ val (x, y) = when (facing) {
|
|
+ // Face-local X where 0 = visual left and 1 = visual right.
|
|
+ Direction.NORTH -> 1.0 - relative.x to relative.y
|
|
+ Direction.SOUTH -> relative.x to relative.y
|
|
+ Direction.WEST -> 1.0 - relative.z to relative.y
|
|
+ Direction.EAST -> relative.z to relative.y
|
|
+ else -> return OptionalInt.empty()
|
|
+ }
|
|
+
|
|
+ // Have interaction segments match the renderer areas visually
|
|
+ val leftSlotMinX = 1.0 / 16.0
|
|
+ val leftSlotMaxX = 7.0 / 16.0
|
|
+ val rightSlotMinX = 9.0 / 16.0
|
|
+ val rightSlotMaxX = 15.0 / 16.0
|
|
+ val slotMinY = 1.0 / 16.0
|
|
+ val slotMaxY = 15.0 / 16.0
|
|
+ val slotHeight = 2.0 / 16.0
|
|
+
|
|
+ val col = when {
|
|
+ x in leftSlotMinX..leftSlotMaxX -> 0
|
|
+ x in rightSlotMinX..rightSlotMaxX -> 1
|
|
+ else -> return OptionalInt.empty()
|
|
+ }
|
|
+
|
|
+ if (y < slotMinY || y > slotMaxY) {
|
|
+ return OptionalInt.empty()
|
|
+ }
|
|
+
|
|
+ val rowFromBottom = ((y - slotMinY) / slotHeight).toInt().coerceIn(0, 6)
|
|
+ val row = 6 - rowFromBottom
|
|
+
|
|
+ return OptionalInt.of(row * 2 + col)
|
|
+ }
|
|
+
|
|
+ fun isValidItem(stack: ItemStack): Boolean {
|
|
+ return stack.item is TechnicalMachineItem
|
|
+ || stack.item == CobblemonItems.UPGRADE
|
|
+ || stack.item == CobblemonItems.DUBIOUS_DISC
|
|
+ || stack.item == Items.MUSIC_DISC_13
|
|
+ || stack.item == Items.MUSIC_DISC_CAT
|
|
+ || stack.item == Items.MUSIC_DISC_BLOCKS
|
|
+ || stack.item == Items.MUSIC_DISC_CHIRP
|
|
+ || stack.item == Items.MUSIC_DISC_FAR
|
|
+ || stack.item == Items.MUSIC_DISC_MALL
|
|
+ || stack.item == Items.MUSIC_DISC_MELLOHI
|
|
+ || stack.item == Items.MUSIC_DISC_STAL
|
|
+ || stack.item == Items.MUSIC_DISC_STRAD
|
|
+ || stack.item == Items.MUSIC_DISC_WARD
|
|
+ || stack.item == Items.MUSIC_DISC_11
|
|
+ || stack.item == Items.MUSIC_DISC_WAIT
|
|
+ || stack.item == Items.MUSIC_DISC_OTHERSIDE
|
|
+ || stack.item == Items.MUSIC_DISC_5
|
|
+ || stack.item == Items.MUSIC_DISC_RELIC
|
|
+ || stack.item == Items.MUSIC_DISC_PRECIPICE
|
|
+ || stack.item == Items.MUSIC_DISC_CREATOR
|
|
+ || stack.item == Items.MUSIC_DISC_CREATOR_MUSIC_BOX
|
|
+ || stack.item == Items.MUSIC_DISC_PIGSTEP
|
|
+ }
|
|
+
|
|
+ fun onNoteBlockPulse(level: Level, noteBlockPos: BlockPos, soundEvent: SoundEvent) {
|
|
+ val gameTime = level.gameTime
|
|
+ if (lastNotePulseTick == gameTime) {
|
|
+ return
|
|
+ }
|
|
+ lastNotePulseTick = gameTime
|
|
+
|
|
+ val octaveUp = !items[1].isEmpty
|
|
+ val octaveDown = !items[12].isEmpty
|
|
+ val octaveShift = when {
|
|
+ octaveUp && !octaveDown -> 12
|
|
+ octaveDown && !octaveUp -> -12
|
|
+ else -> 0
|
|
+ }
|
|
+
|
|
+ NOTE_SLOT_TO_SEMITONE.forEach { (slot, semitone) ->
|
|
+ if (items[slot].isEmpty) return@forEach
|
|
+ val pitch = 2.0.pow((semitone + octaveShift) / 12.0).toFloat()
|
|
+ level.playSound(null, noteBlockPos, soundEvent, SoundSource.BLOCKS, 3.0f, pitch)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun markUpdated() {
|
|
+ level?.setBlock(blockPos, blockState, 3)
|
|
+ level?.sendBlockUpdated(blockPos, blockState, blockState, 3)
|
|
+ setChanged()
|
|
+ }
|
|
+
|
|
+ override fun clearContent() {
|
|
+ var changed = false
|
|
+ for (index in items.indices) {
|
|
+ if (!items[index].isEmpty) {
|
|
+ items[index] = ItemStack.EMPTY
|
|
+ changed = true
|
|
+ }
|
|
+ }
|
|
+ if (changed) {
|
|
+ level?.let { updateBlockState(it, blockPos) }
|
|
+ markUpdated()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun getContainerSize(): Int = items.size
|
|
+
|
|
+ override fun isEmpty(): Boolean = items.all { it.isEmpty }
|
|
+
|
|
+ override fun getItem(slot: Int): ItemStack {
|
|
+ if (slot !in items.indices) return ItemStack.EMPTY
|
|
+ return items[slot]
|
|
+ }
|
|
+
|
|
+ override fun removeItem(slot: Int, amount: Int): ItemStack {
|
|
+ val removed = ContainerHelper.removeItem(items, slot, amount)
|
|
+ if (!removed.isEmpty) {
|
|
+ if (items[slot].isEmpty) {
|
|
+ lastInteractedSlot = slot
|
|
+ }
|
|
+ level?.let { updateBlockState(it, blockPos) }
|
|
+ markUpdated()
|
|
+ }
|
|
+ return removed
|
|
+ }
|
|
+
|
|
+ override fun removeItemNoUpdate(slot: Int): ItemStack {
|
|
+ val removed = ContainerHelper.takeItem(items, slot)
|
|
+ if (!removed.isEmpty) {
|
|
+ level?.let { updateBlockState(it, blockPos) }
|
|
+ markUpdated()
|
|
+ }
|
|
+ return removed
|
|
+ }
|
|
+
|
|
+ override fun setItem(slot: Int, stack: ItemStack) {
|
|
+ if (slot !in items.indices) return
|
|
+ if (!stack.isEmpty && !isValidItem(stack)) return
|
|
+
|
|
+ items[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack.copyWithCount(1)
|
|
+ lastInteractedSlot = slot
|
|
+ level?.let { updateBlockState(it, blockPos) }
|
|
+ markUpdated()
|
|
+ }
|
|
+
|
|
+ override fun stillValid(player: Player): Boolean {
|
|
+ return level?.getBlockEntity(blockPos) === this &&
|
|
+ player.distanceToSqr(blockPos.x + 0.5, blockPos.y + 0.5, blockPos.z + 0.5) <= 64.0
|
|
+ }
|
|
+
|
|
+ override fun getMaxStackSize(): Int = 1
|
|
+
|
|
+ override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean {
|
|
+ return slot in items.indices && items[slot].isEmpty && isValidItem(stack)
|
|
+ }
|
|
+
|
|
+ override fun getSlotsForFace(side: Direction): IntArray = accessibleSlots
|
|
+
|
|
+ override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, direction: Direction?): Boolean {
|
|
+ return canPlaceItem(slot, stack)
|
|
+ }
|
|
+
|
|
+ override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean {
|
|
+ return slot in items.indices
|
|
+ }
|
|
+
|
|
+ override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
|
|
+ return saveWithoutMetadata(registries)
|
|
+ }
|
|
+
|
|
+ override fun getUpdatePacket(): ClientboundBlockEntityDataPacket {
|
|
+ return ClientboundBlockEntityDataPacket.create(this)
|
|
+ }
|
|
+
|
|
+ override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
|
+ super.saveAdditional(tag, registries)
|
|
+ ContainerHelper.saveAllItems(tag, items, true, registries)
|
|
+ tag.putInt("LastInteractedSlot", lastInteractedSlot)
|
|
+ }
|
|
+
|
|
+ override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
|
+ super.loadAdditional(tag, registries)
|
|
+ items.clear()
|
|
+ ContainerHelper.loadAllItems(tag, items, registries)
|
|
+ lastInteractedSlot = tag.getInt("LastInteractedSlot")
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ // C=0 chromatic semitone mapping.
|
|
+ private val NOTE_SLOT_TO_SEMITONE = linkedMapOf(
|
|
+ 0 to 5, // F
|
|
+ 2 to 4, // E
|
|
+ 4 to 3, // D#
|
|
+ 6 to 2, // D
|
|
+ 8 to 1, // C#
|
|
+ 10 to 0, // C
|
|
+ 3 to 11, // B
|
|
+ 5 to 10, // A#
|
|
+ 7 to 9, // A
|
|
+ 9 to 8, // G#
|
|
+ 11 to 7, // G
|
|
+ 13 to 6 // F#
|
|
+ )
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/multiblock/FossilMultiblockBuilder.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/multiblock/FossilMultiblockBuilder.kt
|
|
index bd26810891..fb96744e30 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/block/multiblock/FossilMultiblockBuilder.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/multiblock/FossilMultiblockBuilder.kt
|
|
@@ -14,7 +14,9 @@ import com.cobblemon.mod.common.CobblemonSounds
|
|
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
|
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
|
import com.cobblemon.mod.common.api.multiblock.condition.BlockRelativeCondition
|
|
+import com.cobblemon.mod.common.block.MonitorBlock
|
|
import com.cobblemon.mod.common.block.RestorationTankBlock
|
|
+import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
|
import com.cobblemon.mod.common.util.DataKeys
|
|
import com.cobblemon.mod.common.util.blockPositionsAsList
|
|
import net.minecraft.advancements.critereon.BlockPredicate
|
|
@@ -121,6 +123,13 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
|
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as? MultiblockEntity
|
|
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as? MultiblockEntity
|
|
val tankTopEntity = world.getBlockEntity(restorationTankPos.above()) as? MultiblockEntity
|
|
+ if (monitorEntity is FossilMultiblockEntity) {
|
|
+ val monitorState = world.getBlockState(monitorPos)
|
|
+ monitorEntity.dropDisk(world, monitorPos, monitorState)
|
|
+ if (monitorState.hasProperty(MonitorBlock.SCREEN)) {
|
|
+ world.setBlockAndUpdate(monitorPos, monitorState.setValue(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
|
|
+ }
|
|
+ }
|
|
val structure = FossilMultiblockStructure(monitorPos, fossilAnalyzerPos, restorationTankPos)
|
|
|
|
structure.tankConnectorDirection = dirsToCheck.filter {
|
|
@@ -168,4 +177,4 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
|
//lol thanks mojang for not allowing nbt puts to be chained
|
|
}
|
|
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineBlock.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineBlock.kt
|
|
new file mode 100644
|
|
index 0000000000..ebfa1ed951
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineBlock.kt
|
|
@@ -0,0 +1,255 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlockEntities
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.playSoundServer
|
|
+import com.cobblemon.mod.common.util.toVec3d
|
|
+import com.mojang.serialization.MapCodec
|
|
+import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.world.Containers
|
|
+import net.minecraft.world.InteractionResult
|
|
+import net.minecraft.world.entity.Entity
|
|
+import net.minecraft.world.entity.LivingEntity
|
|
+import net.minecraft.world.entity.player.Player
|
|
+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.BaseEntityBlock
|
|
+import net.minecraft.world.level.block.Block
|
|
+import net.minecraft.world.level.block.RenderShape
|
|
+import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
|
+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.BlockStateProperties
|
|
+import net.minecraft.world.level.block.state.properties.BooleanProperty
|
|
+import net.minecraft.world.level.gameevent.GameEvent
|
|
+import net.minecraft.world.level.material.FluidState
|
|
+import net.minecraft.world.level.material.Fluids
|
|
+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 org.jetbrains.annotations.Nullable
|
|
+
|
|
+class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
|
|
+
|
|
+ companion object {
|
|
+ val EMPTY: BooleanProperty = BooleanProperty.create("empty")
|
|
+ val FACING = BlockStateProperties.HORIZONTAL_FACING
|
|
+ val WATERLOGGED = BlockStateProperties.WATERLOGGED
|
|
+ val ACTIVE: BooleanProperty = BooleanProperty.create("active")
|
|
+ val OPEN: BooleanProperty = BooleanProperty.create("open")
|
|
+
|
|
+ private val SHAPE: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.375, 1.0)
|
|
+
|
|
+ private val SHAPE_OPEN: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0)
|
|
+
|
|
+ private val SHAPE_OPEN_NORTH: VoxelShape = Shapes.or(
|
|
+ Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
|
+ Shapes.box(0.0, 0.3125, 0.8125, 1.0, 0.4375, 0.9375),
|
|
+ Shapes.box(0.0, 0.4375, 0.6875, 1.0, 0.5625, 0.8125),
|
|
+ Shapes.box(0.0, 0.5625, 0.5625, 1.0, 0.6875, 0.6875),
|
|
+ Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
|
|
+ Shapes.box(0.0, 0.8125, 0.3125, 1.0, 0.9375, 0.4375),
|
|
+ Shapes.box(0.0, 0.9375, 0.3125, 1.0, 1.0, 0.375)
|
|
+ )
|
|
+
|
|
+ private val SHAPE_OPEN_SOUTH: VoxelShape = Shapes.or(
|
|
+ Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
|
+ Shapes.box(0.0, 0.3125, 0.0625, 1.0, 0.4375, 0.1875),
|
|
+ Shapes.box(0.0, 0.4375, 0.1875, 1.0, 0.5625, 0.3125),
|
|
+ Shapes.box(0.0, 0.5625, 0.3125, 1.0, 0.6875, 0.4375),
|
|
+ Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
|
|
+ Shapes.box(0.0, 0.8125, 0.5625, 1.0, 0.9375, 0.6875),
|
|
+ Shapes.box(0.0, 0.9375, 0.625, 1.0, 1.0, 0.6875)
|
|
+ )
|
|
+
|
|
+ private val SHAPE_OPEN_EAST: VoxelShape = Shapes.or(
|
|
+ Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
|
+ Shapes.box(0.0625, 0.3125, 0.0, 0.1875, 0.4375, 1.0),
|
|
+ Shapes.box(0.1875, 0.4375, 0.0, 0.3125, 0.5625, 1.0),
|
|
+ Shapes.box(0.3125, 0.5625, 0.0, 0.4375, 0.6875, 1.0),
|
|
+ Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
|
|
+ Shapes.box(0.5625, 0.8125, 0.0, 0.6875, 0.9375, 1.0),
|
|
+ Shapes.box(0.625, 0.9375, 0.0, 0.6875, 1.0, 1.0)
|
|
+ )
|
|
+
|
|
+ private val SHAPE_OPEN_WEST: VoxelShape = Shapes.or(
|
|
+ Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
|
+ Shapes.box(0.8125, 0.3125, 0.0, 0.9375, 0.4375, 1.0),
|
|
+ Shapes.box(0.6875, 0.4375, 0.0, 0.8125, 0.5625, 1.0),
|
|
+ Shapes.box(0.5625, 0.5625, 0.0, 0.6875, 0.6875, 1.0),
|
|
+ Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
|
|
+ Shapes.box(0.3125, 0.8125, 0.0, 0.4375, 0.9375, 1.0),
|
|
+ Shapes.box(0.3125, 0.9375, 0.0, 0.375, 1.0, 1.0)
|
|
+ )
|
|
+
|
|
+ val CODEC: MapCodec<TMMachineBlock> = RecordCodecBuilder.mapCodec { instance ->
|
|
+ instance.group(
|
|
+ Properties.CODEC.fieldOf("properties").forGetter { it.properties }
|
|
+ ).apply(instance, ::TMMachineBlock)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ init {
|
|
+ registerDefaultState(stateDefinition.any()
|
|
+ .setValue(FACING, Direction.NORTH)
|
|
+ .setValue(WATERLOGGED, false)
|
|
+ .setValue(ACTIVE, false)
|
|
+ .setValue(EMPTY, true))
|
|
+ }
|
|
+
|
|
+ override fun <T : BlockEntity> getTicker(level: Level, state: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
|
+ return createTicker(level, blockEntityType, CobblemonBlockEntities.TM_MACHINE)
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ protected fun <T : BlockEntity> createTicker(level: Level, serverType: BlockEntityType<T>, clientType: BlockEntityType<out TMMachineBlockEntity>): BlockEntityTicker<T>? {
|
|
+ return if (!level.isClientSide) createTickerHelper(serverType, clientType, TMMachineBlockEntity::serverTick)
|
|
+ else null
|
|
+ }
|
|
+
|
|
+ override fun fallOn(level: Level, state: BlockState, pos: BlockPos, entity: Entity, fallDistance: Float) {
|
|
+ if (!level.isClientSide && (entity is LivingEntity) &&
|
|
+ (entity.bbWidth * entity.bbWidth * entity.bbHeight > 0.512F) &&
|
|
+ (entity.y >= (pos.y + 0.55)) &&
|
|
+ state.hasProperty(OPEN) && state.getValue(OPEN)
|
|
+ ) {
|
|
+ level.setBlockAndUpdate(pos, state.setValue(OPEN, false))
|
|
+ level.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.TM_MACHINE_CLOSE, volume = 1F, pitch = 1F)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = TMMachineBlockEntity(pos, state)
|
|
+
|
|
+ override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
|
|
+ return defaultBlockState()
|
|
+ .setValue(FACING, context.horizontalDirection.opposite)
|
|
+ .setValue(WATERLOGGED, context.level.getFluidState(context.clickedPos).type == Fluids.WATER)
|
|
+ .setValue(EMPTY, true)
|
|
+ .setValue(ACTIVE, false)
|
|
+ .setValue(OPEN, false)
|
|
+ }
|
|
+
|
|
+ override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
|
|
+ if (!level.isClientSide) {
|
|
+ if (player.isCrouching) {
|
|
+ if (state.hasProperty(OPEN)) {
|
|
+ val isOpen = state.getValue(OPEN)
|
|
+ level.setBlockAndUpdate(pos, state.setValue(OPEN, !isOpen))
|
|
+ level.playSoundServer(
|
|
+ position = pos.toVec3d(),
|
|
+ sound = if (!isOpen) CobblemonSounds.TM_MACHINE_CLOSE else CobblemonSounds.TM_MACHINE_OPEN,
|
|
+ volume = 1F,
|
|
+ pitch = 1F
|
|
+ )
|
|
+ }
|
|
+ } else {
|
|
+ val serverPlayer = player as? ServerPlayer
|
|
+ if (serverPlayer != null) {
|
|
+ val recipeId = cobblemonResource("tm_machine")
|
|
+ val recipeOpt = level.server?.recipeManager?.byKey(recipeId)
|
|
+ recipeOpt?.ifPresent { recipe ->
|
|
+ if (!serverPlayer.recipeBook.contains(recipe)) {
|
|
+ serverPlayer.awardRecipes(listOf(recipe))
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ val blockEntity = level.getBlockEntity(pos)
|
|
+ if (blockEntity is TMMachineBlockEntity) {
|
|
+ player.openMenu(blockEntity)
|
|
+ level.playSoundServer(
|
|
+ position = pos.toVec3d(),
|
|
+ sound = CobblemonSounds.TM_MACHINE_ON,
|
|
+ volume = 0.5F,
|
|
+ pitch = 1F
|
|
+ )
|
|
+ level.gameEvent(player, GameEvent.BLOCK_OPEN, pos)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return InteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+
|
|
+ override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
|
+ builder.add(FACING, WATERLOGGED, EMPTY, ACTIVE, OPEN)
|
|
+ }
|
|
+
|
|
+ override fun codec(): MapCodec<TMMachineBlock> = CODEC
|
|
+
|
|
+ override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
|
+
|
|
+ override fun getFluidState(state: BlockState): FluidState =
|
|
+ if (state.getValue(WATERLOGGED)) Fluids.WATER.defaultFluidState() else super.getFluidState(state)
|
|
+
|
|
+ override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
|
+ return if (state.getValue(OPEN)) {
|
|
+ when (state.getValue(FACING)) {
|
|
+ Direction.NORTH -> SHAPE_OPEN_NORTH
|
|
+ Direction.SOUTH -> SHAPE_OPEN_SOUTH
|
|
+ Direction.WEST -> SHAPE_OPEN_WEST
|
|
+ Direction.EAST -> SHAPE_OPEN_EAST
|
|
+ else -> Shapes.empty()
|
|
+ }
|
|
+ } else SHAPE
|
|
+ }
|
|
+
|
|
+ override fun getShape(state: BlockState, getter: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = if (state.getValue(OPEN)) SHAPE_OPEN else SHAPE
|
|
+
|
|
+ override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
|
|
+ Containers.dropContentsOnDestroy(state, newState, level, pos)
|
|
+ super.onRemove(state, level, pos, newState, movedByPiston)
|
|
+ }
|
|
+
|
|
+ override fun isSignalSource(state: BlockState): Boolean = false
|
|
+
|
|
+ override fun getSignal(state: BlockState, level: BlockGetter, pos: BlockPos, direction: Direction): Int = 0
|
|
+
|
|
+ override fun hasAnalogOutputSignal(state: BlockState): Boolean = true
|
|
+
|
|
+ /*override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
|
+ val be = level.getBlockEntity(pos) as? TMMachineBlockEntity ?: return 0
|
|
+
|
|
+ // Have comparator output act like a progress bar when burning because that might be super dope to see without going into the menu
|
|
+ val scaled = (be.burnProgress.toFloat() / TMMachineBlockEntity.TOTAL_PROCESS_TIME.toFloat() * 15f).toInt()
|
|
+ return scaled.coerceIn(0, 15)
|
|
+ }*/
|
|
+
|
|
+ override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
|
+ val be = level.getBlockEntity(pos) as? TMMachineBlockEntity ?: return 0
|
|
+
|
|
+ // Have comparator output act like a progress bar when burning because that might be super dope to see without going into the menu
|
|
+ if (be.burnActive) {
|
|
+ val scaled = (be.burnProgress.toFloat() / TMMachineBlockEntity.TOTAL_PROCESS_TIME.toFloat() * 15F).toInt()
|
|
+ return scaled.coerceIn(0, 15)
|
|
+ }
|
|
+
|
|
+ // if batch mode is off but it is ready to craft then show signal of 15
|
|
+ if (!be.repeatProcess) {
|
|
+ if (be.isReadyToCraft()) return 15
|
|
+ else return 0
|
|
+ }
|
|
+
|
|
+ // When not burning but idling in batch mode maybe show some cool states? Could be nice
|
|
+ return when {
|
|
+ be.isOutputBlocked() -> 8
|
|
+ else -> 4
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineMenu.kt b/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineMenu.kt
|
|
new file mode 100644
|
|
index 0000000000..ee86608259
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/block/tmmachine/TMMachineMenu.kt
|
|
@@ -0,0 +1,210 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.CobblemonMenuType
|
|
+import com.cobblemon.mod.common.api.moves.Moves
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
|
+import com.cobblemon.mod.common.util.itemRegistry
|
|
+import net.minecraft.server.level.ServerLevel
|
|
+import net.minecraft.util.Mth
|
|
+import net.minecraft.world.Container
|
|
+import net.minecraft.world.SimpleContainer
|
|
+import net.minecraft.world.entity.player.Inventory
|
|
+import net.minecraft.world.entity.player.Player
|
|
+import net.minecraft.world.inventory.*
|
|
+import net.minecraft.world.item.ItemStack
|
|
+
|
|
+class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(menuType, syncId), ContainerListener {
|
|
+ companion object {
|
|
+ const val RESULT_SLOT = 0
|
|
+ const val BLANK_TM_SLOT = 1
|
|
+ val INGREDIENT_SLOTS = 2..4
|
|
+ }
|
|
+
|
|
+ var playerInventory: Inventory? = null
|
|
+ var tmMachineEntity: TMMachineBlockEntity? = null
|
|
+ var inventory: Container? = null
|
|
+ var containerData: ContainerData? = null
|
|
+
|
|
+ constructor(syncId: Int, playerInventory: Inventory) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
|
+ this.playerInventory = playerInventory
|
|
+ this.inventory = SimpleContainer(5)
|
|
+ this.containerData = SimpleContainerData(6)
|
|
+ this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
|
|
+
|
|
+ this.addDataSlots(containerData!!)
|
|
+ inventory?.startOpen(playerInventory.player)
|
|
+
|
|
+ val startX = 15
|
|
+ val startY = 144
|
|
+ val slotWidth = 18
|
|
+ val slotHeight = 18
|
|
+
|
|
+ for (row in 0..2) {
|
|
+ for (col in 0..8) {
|
|
+ this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
|
|
+ }
|
|
+ }
|
|
+ for (col in 0..8) {
|
|
+ this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
|
+ }
|
|
+
|
|
+ val container = inventory
|
|
+ container?.let {
|
|
+ this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
|
+ this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
|
+ for (index in INGREDIENT_SLOTS) {
|
|
+ this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
|
+ }
|
|
+ }
|
|
+ this.addSlotListener(this)
|
|
+ }
|
|
+
|
|
+ constructor(syncId: Int, playerInventory: Inventory, inventory: Container, containerData: ContainerData) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
|
+ this.playerInventory = playerInventory
|
|
+ this.inventory = inventory
|
|
+ this.containerData = containerData
|
|
+ this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
|
|
+
|
|
+ this.addDataSlots(containerData)
|
|
+ inventory.startOpen(playerInventory.player)
|
|
+
|
|
+ val startX = 15
|
|
+ val startY = 144
|
|
+ val slotWidth = 18
|
|
+ val slotHeight = 18
|
|
+
|
|
+ for (row in 0..2) {
|
|
+ for (col in 0..8) {
|
|
+ this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
|
|
+ }
|
|
+ }
|
|
+ for (col in 0..8) {
|
|
+ this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
|
+ }
|
|
+
|
|
+ val container = inventory
|
|
+ container.let {
|
|
+ this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
|
+ this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
|
+ for (index in INGREDIENT_SLOTS) {
|
|
+ this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
|
+ }
|
|
+ }
|
|
+ this.addSlotListener(this)
|
|
+ }
|
|
+
|
|
+ override fun quickMoveStack(player: Player, index: Int): ItemStack {
|
|
+ var itemStack = ItemStack.EMPTY
|
|
+ val slot = this.slots[index]
|
|
+ val maxPlayerInventoryIndex = Inventory.INVENTORY_SIZE - 1
|
|
+
|
|
+ if (slot.hasItem()) {
|
|
+ val movedStack = slot.item
|
|
+ itemStack = movedStack.copy()
|
|
+
|
|
+ // If in container slots, move to player inventory
|
|
+ // 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
|
|
+ if (index in 36..40) {
|
|
+ if (!this.moveItemStackTo(movedStack, 0, maxPlayerInventoryIndex, false)) return ItemStack.EMPTY
|
|
+ }
|
|
+ // If in player inventory, move to container slots. 'endIndex' is non-inclusive
|
|
+ else if (index <= maxPlayerInventoryIndex) {
|
|
+ if (!this.moveItemStackTo(movedStack, 37, 40 + 1, false)) return ItemStack.EMPTY
|
|
+ }
|
|
+
|
|
+ if (movedStack.isEmpty) {
|
|
+ slot.setByPlayer(ItemStack.EMPTY)
|
|
+ } else {
|
|
+ slot.setChanged()
|
|
+ }
|
|
+ }
|
|
+ return itemStack
|
|
+ }
|
|
+
|
|
+ override fun stillValid(player: Player): Boolean {
|
|
+ return player.let { inventory?.stillValid(it) } ?: false
|
|
+ }
|
|
+
|
|
+ fun getBurnProgressValue(): Int = this.containerData?.get(TMMachineBlockEntity.BURN_PROGRESS_INDEX) ?: 0
|
|
+
|
|
+ fun getBurnProgressRatio(): Float {
|
|
+ val amount = getBurnProgressValue()
|
|
+ val max = TMMachineBlockEntity.BURN_TOTAL_TIME
|
|
+ return if (amount != 0) {
|
|
+ Mth.clamp((amount.toFloat() / max.toFloat()), 0.0F, 1.0F)
|
|
+ } else {
|
|
+ 0.0F
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun getPostCraftTicks(): Int {
|
|
+ return if (getBurnProgressValue() >= TMMachineBlockEntity.BURN_TOTAL_TIME) {
|
|
+ getBurnProgressValue() - TMMachineBlockEntity.BURN_TOTAL_TIME
|
|
+ } else 0
|
|
+ }
|
|
+
|
|
+ override fun removed(player: Player) {
|
|
+ super.removed(player)
|
|
+ tmMachineEntity?.setChanged()
|
|
+ }
|
|
+
|
|
+ class TMResultSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
|
|
+ override fun mayPlace(stack: ItemStack): Boolean = false
|
|
+ }
|
|
+
|
|
+ class BlankTMSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
|
|
+ override fun mayPlace(stack: ItemStack): Boolean = super.mayPlace(stack) && (stack.item == CobblemonItems.BLANK_TM)
|
|
+ }
|
|
+
|
|
+ override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
|
|
+ val level = playerInventory?.player?.level()
|
|
+
|
|
+ // If ingredient slots have been modified and level is server side and process is active
|
|
+ // 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
|
|
+ if ((dataSlotIndex in 37 .. 40) && (level?.isClientSide == false) && (containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) == 1)) {
|
|
+ // Stop process if there are not enough ingredients after slots have been modified
|
|
+ var validCost = !(inventory?.getItem(BLANK_TM_SLOT)?.isEmpty ?: true)
|
|
+
|
|
+ if (validCost) {
|
|
+ Moves.getByName(tmMachineEntity?.activeMove ?: "")?.let { moveTemplate ->
|
|
+ TechnicalMachines.moveToTM[moveTemplate]?.let { tm ->
|
|
+ val recipes = tm.getClampedRecipe() ?: emptyList()
|
|
+ for ((index, recipe) in recipes.withIndex()) {
|
|
+ val slot = INGREDIENT_SLOTS.first + index
|
|
+ val provided = inventory?.getItem(slot) ?: ItemStack.EMPTY
|
|
+
|
|
+ if (!recipe.ingredient.test(provided) || provided.count < recipe.count) {
|
|
+ validCost = false
|
|
+ break
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!validCost) {
|
|
+ containerData?.set(TMMachineBlockEntity.BURN_ACTIVE_INDEX, 0)
|
|
+ containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tmMachineEntity?.setChanged()
|
|
+ inventory?.setChanged()
|
|
+ playerInventory?.setChanged()
|
|
+ broadcastChanges()
|
|
+ }
|
|
+
|
|
+ override fun dataChanged(containerMenu: AbstractContainerMenu, dataSlotIndex: Int, value: Int) {
|
|
+ broadcastChanges()
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
|
|
index c995c97728..36fadaab40 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
|
|
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.berry.Berries
|
|
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
|
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
|
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
|
+import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
|
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
|
import com.cobblemon.mod.common.block.entity.TintBlockEntity
|
|
import com.cobblemon.mod.common.client.battle.ClientBattle
|
|
@@ -27,6 +28,7 @@ import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
|
|
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
|
|
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
|
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
|
+import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
|
|
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
|
|
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
|
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
|
@@ -40,7 +42,9 @@ import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
|
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
|
|
import com.cobblemon.mod.common.client.render.block.PokeSnackBlockEntityRenderer
|
|
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
|
+import com.cobblemon.mod.common.client.render.block.TMShelfBlockEntityRenderer
|
|
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
|
+import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
|
|
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
|
|
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
|
import com.cobblemon.mod.common.client.render.color.PokeSnackItemColorProvider
|
|
@@ -69,6 +73,7 @@ import com.cobblemon.mod.common.client.tooltips.FishingRodTooltipGenerator
|
|
import com.cobblemon.mod.common.client.tooltips.PokePuffTooltipGenerator
|
|
import com.cobblemon.mod.common.client.tooltips.RecipeSeasoningAbsorptionTooltipGenerator
|
|
import com.cobblemon.mod.common.client.tooltips.SeasoningTooltipGenerator
|
|
+import com.cobblemon.mod.common.client.tooltips.TechnicalMachineTooltipGenerator
|
|
import com.cobblemon.mod.common.client.tooltips.TooltipManager
|
|
import com.cobblemon.mod.common.client.trade.ClientTrade
|
|
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
|
@@ -89,6 +94,7 @@ import net.minecraft.client.renderer.blockentity.HangingSignRenderer
|
|
import net.minecraft.client.renderer.blockentity.SignRenderer
|
|
import net.minecraft.client.renderer.entity.EntityRenderer
|
|
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
|
+import net.minecraft.client.renderer.entity.layers.RenderLayer
|
|
import net.minecraft.client.resources.PlayerSkin
|
|
import net.minecraft.server.packs.resources.ResourceManager
|
|
import net.minecraft.world.InteractionHand
|
|
@@ -103,6 +109,7 @@ object CobblemonClient {
|
|
var battle: ClientBattle? = null
|
|
var clientPlayerData = ClientGeneralPlayerData()
|
|
var clientPokedexData = ClientPokedexManager(mutableMapOf())
|
|
+ var clientTMMoveData = ClientTMMoveManager(mutableSetOf())
|
|
|
|
/** If true then we won't bother them anymore about choosing a starter even if it's a thing they can do. */
|
|
var checkedStarterScreen = false
|
|
@@ -119,6 +126,7 @@ object CobblemonClient {
|
|
requests = ClientPlayerActionRequests()
|
|
teamData = ClientPlayerTeamData()
|
|
clientPokedexData = ClientPokedexManager(mutableMapOf())
|
|
+ clientTMMoveData = ClientTMMoveManager(mutableSetOf())
|
|
storage.onLogin()
|
|
// CobblemonDataProvider.canReload = false
|
|
}
|
|
@@ -206,6 +214,7 @@ object CobblemonClient {
|
|
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
|
|
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
|
|
TooltipManager.registerTooltipGenerator(AprijuiceTooltipGenerator)
|
|
+ TooltipManager.registerTooltipGenerator(TechnicalMachineTooltipGenerator)
|
|
TooltipManager.registerTooltipGenerator(PokePuffTooltipGenerator)
|
|
}
|
|
|
|
@@ -287,6 +296,7 @@ object CobblemonClient {
|
|
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
|
CobblemonBlocks.REVIVAL_HERB,
|
|
*CobblemonBlocks.berries().values.toTypedArray(),
|
|
+ CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
|
CobblemonBlocks.GALARICA_NUT_BUSH,
|
|
CobblemonBlocks.RESTORATION_TANK,
|
|
CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE,
|
|
@@ -297,6 +307,24 @@ object CobblemonClient {
|
|
CobblemonBlocks.MEDIUM_BUDDING_BLACK_TUMBLESTONE,
|
|
CobblemonBlocks.LARGE_BUDDING_BLACK_TUMBLESTONE,
|
|
CobblemonBlocks.BLACK_TUMBLESTONE_CLUSTER,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY,
|
|
CobblemonBlocks.SMALL_BUDDING_SKY_TUMBLESTONE,
|
|
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
|
|
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
|
@@ -362,7 +390,8 @@ object CobblemonClient {
|
|
CobblemonBlocks.X_DEFENSE,
|
|
CobblemonBlocks.X_SP_ATK,
|
|
CobblemonBlocks.X_SP_DEF,
|
|
- CobblemonBlocks.X_SPEED
|
|
+ CobblemonBlocks.X_SPEED,
|
|
+ CobblemonBlocks.TM_MACHINE
|
|
)
|
|
|
|
this.createBoatModelLayers()
|
|
@@ -390,6 +419,7 @@ object CobblemonClient {
|
|
|
|
private fun registerMenuScreens() {
|
|
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
|
|
+ MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
|
|
}
|
|
|
|
private fun registerBlockEntityRenderers() {
|
|
@@ -413,6 +443,7 @@ object CobblemonClient {
|
|
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
|
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
|
|
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
|
|
+ this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.TM_SHELF, ::TMShelfBlockEntityRenderer)
|
|
}
|
|
|
|
private fun registerEntityRenderers() {
|
|
@@ -421,14 +452,14 @@ object CobblemonClient {
|
|
LOGGER.info("Registering PokéBall renderer")
|
|
this.implementation.registerEntityRenderer(CobblemonEntities.EMPTY_POKEBALL, ::PokeBallRenderer)
|
|
LOGGER.info("Registering Boat renderer")
|
|
- this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
|
|
- LOGGER.info("Registering Boat with Chest renderer")
|
|
- this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx ->
|
|
+ this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx ->
|
|
CobblemonBoatRenderer(
|
|
ctx,
|
|
true
|
|
)
|
|
}
|
|
+ LOGGER.info("Registering Boat with Chest renderer")
|
|
+ this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
|
|
LOGGER.info("Registering Generic Bedrock renderer")
|
|
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
|
|
LOGGER.info("Registering Generic Bedrock Entity renderer")
|
|
@@ -445,6 +476,7 @@ object CobblemonClient {
|
|
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
|
|
implementation.registerItemColors(PonigiriItemColorProvider, CobblemonItems.PONIGIRI)
|
|
implementation.registerItemColors(SinisterTeaItemColorProvider, CobblemonItems.SINISTER_TEA)
|
|
+ implementation.registerItemColors(TechnicalMachineItemColorProvider, CobblemonItems.TECHNICAL_MACHINE)
|
|
}
|
|
|
|
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
|
@@ -481,4 +513,4 @@ object CobblemonClient {
|
|
)
|
|
}
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt
|
|
index a2dd8bbef0..15f7e9ef0f 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PartyOverlay.kt
|
|
@@ -93,7 +93,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
|
|
|
val starterToast = CobblemonToast(
|
|
Mth.createInsecureUUID(),
|
|
- CobblemonItems.POKE_BALL.defaultInstance,
|
|
+ listOf(CobblemonItems.POKE_BALL.defaultInstance),
|
|
lang("ui.starter.choose_starter_title", SummaryBinding.boundKey().displayName).red(),
|
|
lang("ui.starter.choose_starter_description", SummaryBinding.boundKey().displayName).darkGray(),
|
|
AdvancementToast.BACKGROUND_SPRITE,
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/interact/moveselect/MoveSlotButton.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/interact/moveselect/MoveSlotButton.kt
|
|
index b91d59265e..cbead029d8 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/interact/moveselect/MoveSlotButton.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/interact/moveselect/MoveSlotButton.kt
|
|
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.interact.moveselect
|
|
|
|
import com.cobblemon.mod.common.api.gui.blitk
|
|
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
-import com.cobblemon.mod.common.api.moves.Moves
|
|
import com.cobblemon.mod.common.api.text.bold
|
|
import com.cobblemon.mod.common.api.text.gold
|
|
import com.cobblemon.mod.common.api.text.red
|
|
@@ -21,6 +20,7 @@ import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
|
import com.cobblemon.mod.common.client.gui.TypeIcon
|
|
import com.cobblemon.mod.common.client.render.drawScaledText
|
|
import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.lang
|
|
import com.cobblemon.mod.common.util.math.toRGB
|
|
import net.minecraft.client.gui.GuiGraphics
|
|
import net.minecraft.client.gui.components.Button
|
|
@@ -31,96 +31,92 @@ import net.minecraft.util.Mth
|
|
|
|
class MoveSlotButton(
|
|
x: Int, y: Int,
|
|
- val move: MoveTemplate,
|
|
- val pp: Int,
|
|
- val ppMax: Int,
|
|
- val enabled: Boolean = true,
|
|
+ var move: MoveTemplate? = null,
|
|
+ var pp: Int = 0,
|
|
+ var ppMax: Int = 0,
|
|
+ var ppAsFraction: Boolean = true,
|
|
+ var enabled: Boolean = true,
|
|
onPress: OnPress
|
|
-) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
|
|
+) : Button(x, y, WIDTH, HEIGHT, move?.name?.text() ?: lang("ui.moves"), onPress, CreateNarration { move?.name?.text() ?: lang("ui.moves") }), CobblemonRenderable {
|
|
|
|
companion object {
|
|
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
|
+ private val moveDisabledResource = cobblemonResource("textures/gui/summary/summary_move_disabled.png")
|
|
private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
|
|
|
const val WIDTH = 108
|
|
const val HEIGHT = 22
|
|
}
|
|
|
|
- override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
|
- isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height && enabled
|
|
-
|
|
- val moveTemplate = Moves.getByNameOrDummy(move.name)
|
|
- val rgb = moveTemplate.elementalType.hue.toRGB()
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, pPartialTicks: Float) {
|
|
+ move?.let {
|
|
+ isHovered = isMouseOver(mouseX.toDouble(), mouseY.toDouble()) && enabled
|
|
+ val rgb = it.elementalType.hue.toRGB()
|
|
+ val alpha = if (enabled) 1.0F else 0.7F
|
|
+ val matrices = context.pose()
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = if (enabled) moveResource else moveDisabledResource,
|
|
+ x = x,
|
|
+ y = y,
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT,
|
|
+ vOffset = if (isHovered) HEIGHT else 0,
|
|
+ textureHeight = HEIGHT * 2,
|
|
+ red = rgb.first,
|
|
+ green = rgb.second,
|
|
+ blue = rgb.third
|
|
+ )
|
|
|
|
- val alpha = if (enabled) 1.0 else 0.5
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = moveOverlayResource,
|
|
+ x = x,
|
|
+ y = y,
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT
|
|
+ )
|
|
|
|
- val matrices = context.pose()
|
|
- blitk(
|
|
- matrixStack = matrices,
|
|
- texture = moveResource,
|
|
- x = x,
|
|
- y = y,
|
|
- width = WIDTH,
|
|
- height = HEIGHT,
|
|
- vOffset = if (isHovered) HEIGHT else 0,
|
|
- textureHeight = HEIGHT * 2,
|
|
- red = rgb.first,
|
|
- green = rgb.second,
|
|
- blue = rgb.third,
|
|
- alpha = alpha
|
|
- )
|
|
+ if (pp != -1 && ppMax != -1) {
|
|
+ var movePPText = lang("ui.moves.pp", pp).bold()
|
|
+ if (ppAsFraction) {
|
|
+ movePPText = Component.literal("$pp/$ppMax").bold()
|
|
+
|
|
+ if (pp <= Mth.floor(ppMax / 2F)) {
|
|
+ movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = movePPText,
|
|
+ x = x + 93,
|
|
+ y = y + 13,
|
|
+ centered = true,
|
|
+ opacity = alpha
|
|
+ )
|
|
+ }
|
|
|
|
- blitk(
|
|
- matrixStack = matrices,
|
|
- texture = moveOverlayResource,
|
|
- x = x,
|
|
- y = y,
|
|
- width = WIDTH,
|
|
- height = HEIGHT,
|
|
- alpha = alpha
|
|
- )
|
|
+ // Type Icon
|
|
+ TypeIcon(x = x + 2, y = y + 2, type = it.elementalType, opacity = alpha).render(context)
|
|
|
|
- if (pp != -1 && ppMax != -1) {
|
|
- var movePPText = Component.literal("$pp/$ppMax").bold()
|
|
-
|
|
- if (pp <= Mth.floor(ppMax / 2F)) {
|
|
- movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
|
|
- }
|
|
+ // Move Category
|
|
+ MoveCategoryIcon(x = x + 66, y = y + 13.5, category = it.damageCategory, opacity = alpha).render(context)
|
|
|
|
+ // Move Name
|
|
drawScaledText(
|
|
context = context,
|
|
font = CobblemonResources.DEFAULT_LARGE,
|
|
- text = movePPText,
|
|
- x = x + 93,
|
|
- y = y + 13,
|
|
- centered = true
|
|
+ text = it.displayName.bold(),
|
|
+ x = x + 28,
|
|
+ y = y + 2,
|
|
+ shadow = true,
|
|
+ opacity = alpha
|
|
)
|
|
}
|
|
-
|
|
- // Type Icon
|
|
- TypeIcon(
|
|
- x = x + 2,
|
|
- y = y + 2,
|
|
- type = moveTemplate.elementalType
|
|
- ).render(context)
|
|
-
|
|
- // Move Category
|
|
- MoveCategoryIcon(
|
|
- x = x + 66,
|
|
- y = y + 13.5,
|
|
- category = move.damageCategory
|
|
- ).render(context)
|
|
-
|
|
- // Move Name
|
|
- drawScaledText(
|
|
- context = context,
|
|
- font = CobblemonResources.DEFAULT_LARGE,
|
|
- text = move.displayName.bold(),
|
|
- x = x + 28,
|
|
- y = y + 2,
|
|
- shadow = true
|
|
- )
|
|
}
|
|
|
|
override fun playDownSound(soundManager: SoundManager) {}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/party/PartyTutorialToasts.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/party/PartyTutorialToasts.kt
|
|
index 7a70fb5f2a..01ef4c4ee3 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/party/PartyTutorialToasts.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/party/PartyTutorialToasts.kt
|
|
@@ -45,7 +45,7 @@ object PartyTutorialToasts {
|
|
|
|
private val toast = CobblemonToast(
|
|
Mth.createInsecureUUID(),
|
|
- CobblemonItems.POKEDEX_RED.defaultInstance,
|
|
+ listOf(CobblemonItems.POKEDEX_RED.defaultInstance),
|
|
lang("ui.party.party_select_title").red(),
|
|
lang(
|
|
"ui.party.party_select_description",
|
|
@@ -96,4 +96,4 @@ object PartyTutorialToasts {
|
|
private fun hideToast() {
|
|
if (toastAttached) toast.nextVisibility = Toast.Visibility.HIDE
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
|
|
index edd6162449..31da2fae81 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUI.kt
|
|
@@ -22,6 +22,8 @@ import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
|
import com.cobblemon.mod.common.api.pokedex.filter.EntryFilter
|
|
import com.cobblemon.mod.common.api.pokedex.filter.SearchByType
|
|
import com.cobblemon.mod.common.api.pokedex.filter.SearchFilter
|
|
+import com.cobblemon.mod.common.api.pokedex.filter.SecondaryPokedexFilter
|
|
+import com.cobblemon.mod.common.api.pokedex.filter.SecondaryPokedexFilterType
|
|
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
|
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
|
import com.cobblemon.mod.common.api.text.bold
|
|
@@ -39,6 +41,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ABILI
|
|
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_DESCRIPTION
|
|
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_DROPS
|
|
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ICON_SIZE
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_MOVES
|
|
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
|
|
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
|
|
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
|
|
@@ -83,12 +86,14 @@ class PokedexGUI private constructor(
|
|
private val arrowDownIcon = cobblemonResource("textures/gui/pokedex/arrow_down.png")
|
|
|
|
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
|
|
+ private val secondaryFilterIcon = cobblemonResource("textures/gui/pokedex/filter_icon_button.png")
|
|
private val tabIcons = arrayOf(
|
|
cobblemonResource("textures/gui/pokedex/tab_info.png"),
|
|
cobblemonResource("textures/gui/pokedex/tab_abilities.png"),
|
|
cobblemonResource("textures/gui/pokedex/tab_size.png"),
|
|
cobblemonResource("textures/gui/pokedex/tab_stats.png"),
|
|
- cobblemonResource("textures/gui/pokedex/tab_drops.png")
|
|
+ cobblemonResource("textures/gui/pokedex/tab_drops.png"),
|
|
+ cobblemonResource("textures/gui/pokedex/tab_moves.png")
|
|
)
|
|
|
|
/**
|
|
@@ -127,11 +132,13 @@ class PokedexGUI private constructor(
|
|
private lateinit var regionSelectWidgetUp: ScaledButton
|
|
private lateinit var regionSelectWidgetDown: ScaledButton
|
|
private lateinit var searchByTypeButton: ScaledButton
|
|
+ private lateinit var secondaryFilterButton: ScaledButton
|
|
private lateinit var scrollScreen: EntriesScrollingWidget
|
|
private lateinit var pokemonInfoWidget: PokemonInfoWidget
|
|
private lateinit var searchWidget: SearchWidget
|
|
|
|
private var selectedSearchByType: SearchByType = SearchByType.SPECIES
|
|
+ private var selectedSecondaryFilter: SecondaryPokedexFilterType = SecondaryPokedexFilterType.ALL
|
|
private val tabButtons: MutableList<ScaledButton> = mutableListOf()
|
|
|
|
lateinit var tabInfoElement: GuiEventListener
|
|
@@ -163,7 +170,7 @@ class PokedexGUI private constructor(
|
|
displaytabInfoElement(tabInfoIndex, false)
|
|
|
|
if (::searchWidget.isInitialized) removeWidget(searchWidget)
|
|
- searchWidget = SearchWidget(x + 26, y + 28, 128, HEADER_BAR_HEIGHT, update = ::updateFilters)
|
|
+ searchWidget = SearchWidget(x + 26, y + 28, 120, HEADER_BAR_HEIGHT, update = ::updateFilters)
|
|
addRenderableWidget(searchWidget)
|
|
|
|
if (::regionSelectWidgetUp.isInitialized) removeWidget(regionSelectWidgetUp)
|
|
@@ -208,6 +215,23 @@ class PokedexGUI private constructor(
|
|
)
|
|
addRenderableWidget(searchByTypeButton)
|
|
|
|
+ if (::secondaryFilterButton.isInitialized) removeWidget(secondaryFilterButton)
|
|
+ secondaryFilterButton = ScaledButton(
|
|
+ buttonX = (x + 146.5).toFloat(),
|
|
+ buttonY = (y + 29.5).toFloat(),
|
|
+ buttonWidth = 16,
|
|
+ buttonHeight = 16,
|
|
+ scale = SCALE,
|
|
+ resource = secondaryFilterIcon,
|
|
+ clickAction = {
|
|
+ val filters = SecondaryPokedexFilterType.entries.toList()
|
|
+ val selectedIndex = filters.indexOf(selectedSecondaryFilter)
|
|
+ selectedSecondaryFilter = filters[if (selectedIndex == filters.lastIndex) 0 else (selectedIndex + 1)]
|
|
+ updateFilters()
|
|
+ }
|
|
+ )
|
|
+ addRenderableWidget(secondaryFilterButton)
|
|
+
|
|
updateFilters(true)
|
|
}
|
|
|
|
@@ -309,8 +333,7 @@ class PokedexGUI private constructor(
|
|
blitk(
|
|
matrixStack = matrices,
|
|
texture = tabSelectArrow,
|
|
- x = (x + 198 + (25 * tabInfoIndex)) / SCALE,
|
|
- // (x + 191.5 + (22 * tabInfoIndex)) / SCALE for 6 tabs
|
|
+ x = (x + 191.5F + (22F * tabInfoIndex)) / SCALE,
|
|
y = (y + 177) / SCALE,
|
|
width = 12,
|
|
height = 6,
|
|
@@ -332,6 +355,18 @@ class PokedexGUI private constructor(
|
|
-14
|
|
)
|
|
}
|
|
+
|
|
+ if (secondaryFilterButton.isButtonHovered(mouseX, mouseY)) {
|
|
+ val filterText = lang("ui.pokedex.filter.${selectedSecondaryFilter.name.lowercase()}").bold()
|
|
+ renderTooltip(
|
|
+ context,
|
|
+ filterText,
|
|
+ mouseX,
|
|
+ mouseY,
|
|
+ delta,
|
|
+ -14
|
|
+ )
|
|
+ }
|
|
}
|
|
|
|
override fun onClose() {
|
|
@@ -344,6 +379,7 @@ class PokedexGUI private constructor(
|
|
val canDisplayEntry = true //selectedForm?.unlockForms
|
|
|
|
if (::pokemonInfoWidget.isInitialized
|
|
+ && !pokemonInfoWidget.isSuppressed
|
|
&& pokemonInfoWidget.isWithinPortraitSpace(mouseX, mouseY)
|
|
&& canDisplayEntry == true
|
|
) {
|
|
@@ -375,7 +411,7 @@ class PokedexGUI private constructor(
|
|
}
|
|
|
|
override fun tick() {
|
|
- if (::pokemonInfoWidget.isInitialized) pokemonInfoWidget.tick()
|
|
+ if (::pokemonInfoWidget.isInitialized && !pokemonInfoWidget.isSuppressed) pokemonInfoWidget.tick()
|
|
}
|
|
|
|
fun updatePokedexRegion(nextIndex: Boolean) {
|
|
@@ -441,6 +477,7 @@ class PokedexGUI private constructor(
|
|
fun getFilters(): Collection<EntryFilter> {
|
|
val filters: MutableList<EntryFilter> = mutableListOf()
|
|
|
|
+ filters.add(SecondaryPokedexFilter(CobblemonClient.clientPokedexData, selectedSecondaryFilter))
|
|
filters.add(SearchFilter(CobblemonClient.clientPokedexData, searchWidget.value, selectedSearchByType))
|
|
|
|
return filters
|
|
@@ -462,7 +499,7 @@ class PokedexGUI private constructor(
|
|
|
|
for (i in tabIcons.indices) {
|
|
tabButtons.add(ScaledButton(
|
|
- x + 197F + (i * 25F), // x + 190.5F + (i * 22F) for 6 tabs
|
|
+ x + 190.5F + (i * 22F),
|
|
y + 181.5F,
|
|
TAB_ICON_SIZE,
|
|
TAB_ICON_SIZE,
|
|
@@ -497,6 +534,9 @@ class PokedexGUI private constructor(
|
|
}
|
|
|
|
tabInfoIndex = tabIndex
|
|
+ if (::pokemonInfoWidget.isInitialized) {
|
|
+ pokemonInfoWidget.isSuppressed = tabInfoIndex == TAB_MOVES
|
|
+ }
|
|
if (::tabInfoElement.isInitialized) removeWidget(tabInfoElement)
|
|
|
|
val x = (width - BASE_WIDTH) / 2
|
|
@@ -520,11 +560,19 @@ class PokedexGUI private constructor(
|
|
TAB_DROPS -> {
|
|
tabInfoElement = DropsScrollingWidget(x + 189, y + 135)
|
|
}
|
|
+ TAB_MOVES -> {
|
|
+ tabInfoElement = MovesLearnsetWidget(x + 180, y + 28)
|
|
+ }
|
|
}
|
|
val element = tabInfoElement
|
|
if (element is Renderable && element is NarratableEntry) {
|
|
addRenderableWidget(element)
|
|
}
|
|
+ // Keep tab icons above the moves overlay.
|
|
+ if (tabButtons.isNotEmpty()) {
|
|
+ tabButtons.forEach { removeWidget(it) }
|
|
+ tabButtons.forEach { addRenderableWidget(it) }
|
|
+ }
|
|
if (update) updateTabInfoElement()
|
|
}
|
|
|
|
@@ -578,6 +626,17 @@ class PokedexGUI private constructor(
|
|
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
|
|
(tabInfoElement as DropsScrollingWidget).setEntries()
|
|
}
|
|
+ TAB_MOVES -> {
|
|
+ val availableForms = selectedEntry?.let { CobblemonClient.clientPokedexData.getEncounteredForms(it) } ?: emptyList()
|
|
+ val activeForm = this.selectedForm ?: availableForms.firstOrNull()
|
|
+ val fallbackForm = activeForm ?: PokedexForm().apply { displayForm = form.name }
|
|
+ (tabInfoElement as MovesLearnsetWidget).setLearnset(
|
|
+ species = species,
|
|
+ form = form,
|
|
+ availableForms = availableForms,
|
|
+ selectedForm = fallbackForm
|
|
+ ) { next -> cycleSelectedForm(next) }
|
|
+ }
|
|
// TAB_MOVES -> {
|
|
// form.moves.getLevelUpMovesUpTo(100)
|
|
// }
|
|
@@ -600,6 +659,29 @@ class PokedexGUI private constructor(
|
|
displaytabInfoElement(tabInfoIndex)
|
|
}
|
|
|
|
+ fun cycleSelectedForm(next: Boolean) {
|
|
+ val entry = selectedEntry ?: return
|
|
+ val forms = CobblemonClient.clientPokedexData.getEncounteredForms(entry)
|
|
+ if (forms.isEmpty()) return
|
|
+
|
|
+ val current = selectedForm ?: forms.first()
|
|
+ val currentIndex = forms.indexOfFirst { it.displayForm.equals(current.displayForm, ignoreCase = true) }
|
|
+ .let { if (it == -1) 0 else it }
|
|
+
|
|
+ val nextIndex = if (next) {
|
|
+ (currentIndex + 1) % forms.size
|
|
+ } else {
|
|
+ (currentIndex - 1 + forms.size) % forms.size
|
|
+ }
|
|
+
|
|
+ val newForm = forms[nextIndex]
|
|
+ selectedForm = newForm
|
|
+ if (::pokemonInfoWidget.isInitialized) {
|
|
+ pokemonInfoWidget.setSelectedForm(newForm)
|
|
+ }
|
|
+ updateTabInfoElement()
|
|
+ }
|
|
+
|
|
fun canSelectTab(tabIndex: Int): Boolean {
|
|
val selectedForm = this.selectedForm ?: return false
|
|
val selectedEntry = this.selectedEntry ?: return false
|
|
@@ -621,4 +703,4 @@ class PokedexGUI private constructor(
|
|
fun playSound(soundEvent: SoundEvent) {
|
|
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUIConstants.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUIConstants.kt
|
|
index 7260cef409..f4b479c7c9 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUIConstants.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/PokedexGUIConstants.kt
|
|
@@ -35,9 +35,10 @@ object PokedexGUIConstants {
|
|
const val TAB_SIZE = 2
|
|
const val TAB_STATS = 3
|
|
const val TAB_DROPS = 4
|
|
+ const val TAB_MOVES = 5
|
|
|
|
// RowScrollingWidget
|
|
const val SCROLL_BASE_HEIGHT = 141
|
|
const val SCROLL_SLOT_SIZE = 25
|
|
const val SCROLL_SLOT_SPACING = 2
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/MovesLearnsetWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/MovesLearnsetWidget.kt
|
|
new file mode 100644
|
|
index 0000000000..87854a1067
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/MovesLearnsetWidget.kt
|
|
@@ -0,0 +1,1050 @@
|
|
+/*
|
|
+ * 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.pokedex.widgets
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
|
+import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
|
+import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
|
+import com.cobblemon.mod.common.api.text.bold
|
|
+import com.cobblemon.mod.common.api.text.font
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.api.types.ElementalType
|
|
+import com.cobblemon.mod.common.client.CobblemonClient
|
|
+import com.cobblemon.mod.common.client.CobblemonResources
|
|
+import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
|
+import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
|
+import com.cobblemon.mod.common.client.gui.TypeIcon
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_PADDING
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_BAR_WIDTH
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
|
|
+import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
|
|
+import com.cobblemon.mod.common.client.settings.ServerSettings
|
|
+import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
|
+import com.cobblemon.mod.common.client.render.drawScaledText
|
|
+import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
|
+import com.cobblemon.mod.common.pokemon.FormData
|
|
+import com.cobblemon.mod.common.pokemon.Species
|
|
+import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import java.math.RoundingMode
|
|
+import java.text.DecimalFormat
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.network.chat.MutableComponent
|
|
+import net.minecraft.network.chat.Style
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.util.FastColor
|
|
+import net.minecraft.util.Mth
|
|
+
|
|
+class MovesLearnsetWidget(val pX: Int, val pY: Int) : SoundlessWidget(
|
|
+ pX,
|
|
+ pY,
|
|
+ HALF_OVERLAY_WIDTH,
|
|
+ HALF_OVERLAY_HEIGHT,
|
|
+ Component.literal("MovesLearnset")
|
|
+) {
|
|
+ companion object {
|
|
+ private const val LIST_TOP_OFFSET = -15
|
|
+ private const val LIST_HEIGHT = 54
|
|
+ private const val LIST_SLOT_HEIGHT = 10
|
|
+ private const val LIST_SIDE_PADDING = 4
|
|
+ private const val LIST_TM_ICON_RENDER_SIZE = 8
|
|
+ private const val LIST_TM_ICON_OFFSET = LIST_TM_ICON_RENDER_SIZE + 1
|
|
+ private const val LIST_TM_ICON_TEXTURE_SIZE = 16
|
|
+ private const val LIST_TYPE_ICON_SIZE = 18
|
|
+ private val LIST_BACKGROUND_COLOR = FastColor.ARGB32.color(255, 239, 253, 255)
|
|
+
|
|
+ private const val DATA_TOP_OFFSET = 108
|
|
+ private const val DATA_ROW_HEIGHT = 10
|
|
+ private const val DATA_LABEL_OFFSET_X = 14
|
|
+ private const val DATA_ICON_SIZE = 10
|
|
+ private const val DATA_INFO_TOP_OFFSET = 7
|
|
+
|
|
+ private const val DESCRIPTION_TOP_OFFSET = 71
|
|
+ private const val DESCRIPTION_HEIGHT = 38
|
|
+ private const val DESCRIPTION_SCROLLBAR_WIDTH = 3
|
|
+ private const val DESCRIPTION_SCROLLBAR_OFFSET = 13
|
|
+ private const val DESCRIPTION_LEFT_OFFSET = 62
|
|
+ private const val DESCRIPTION_DIVIDER_OFFSET = 6
|
|
+
|
|
+ private val overlayResource = cobblemonResource("textures/gui/pokedex/pokedex_screen_info_overlay.png")
|
|
+ private val arrowFormLeft = cobblemonResource("textures/gui/pokedex/forms_arrow_left.png")
|
|
+ private val arrowFormRight = cobblemonResource("textures/gui/pokedex/forms_arrow_right.png")
|
|
+ private val typeBar = cobblemonResource("textures/gui/pokedex/type_bar.png")
|
|
+ private val typeBarDouble = cobblemonResource("textures/gui/pokedex/type_bar_double.png")
|
|
+ private val filterArrowLeft = cobblemonResource("textures/gui/pokedex/info_arrow_left.png")
|
|
+ private val filterArrowRight = cobblemonResource("textures/gui/pokedex/info_arrow_right.png")
|
|
+ private val sortAlphaIcon = cobblemonResource("textures/gui/pokedex/moves_sort_alpha.png")
|
|
+ private val sortTypeIcon = cobblemonResource("textures/gui/pokedex/moves_sort_type.png")
|
|
+ private val sortSourceIcon = cobblemonResource("textures/gui/pokedex/moves_sort_source.png")
|
|
+ private val sortDiscoveredIcon = cobblemonResource("textures/gui/pokedex/moves_sort_discovered.png")
|
|
+ private val moveDexTypeIcons = cobblemonResource("textures/gui/pokedex/types_small_dex.png")
|
|
+ private val tmDiscIcon = cobblemonResource("textures/item/tms/tm.png")
|
|
+ private val movesPowerIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_power.png")
|
|
+ private val movesAccuracyIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_accuracy.png")
|
|
+ private val movesCategoryIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_category.png")
|
|
+
|
|
+ // Match PokemonInfoWidget placement
|
|
+ private const val TYPE_BAR_Y = 14
|
|
+ private const val TYPE_BAR_HEIGHT = 25
|
|
+ private const val TYPE_ICON_X = 3
|
|
+ private const val TYPE_ICON_Y = 17
|
|
+ private const val FORM_LABEL_X = 85
|
|
+ private const val FORM_LABEL_Y = 14
|
|
+ private const val FORM_ARROW_LEFT_X = 56F
|
|
+ private const val FORM_ARROW_RIGHT_X = 107F
|
|
+ private const val FORM_ARROW_Y = 15F
|
|
+ private const val FORM_ARROW_WIDTH = 10
|
|
+ private const val FORM_ARROW_HEIGHT = 16
|
|
+
|
|
+ // Adjustable filter arrow placement
|
|
+ private const val FILTER_ARROW_LEFT_X = 56F
|
|
+ private const val FILTER_ARROW_RIGHT_X = 107F
|
|
+ private const val FILTER_ARROW_Y = 28F
|
|
+ private const val FILTER_ARROW_WIDTH = 7
|
|
+ private const val FILTER_ARROW_HEIGHT = 10
|
|
+ private const val FILTER_LABEL_X = 84
|
|
+ private const val FILTER_LABEL_Y = 26
|
|
+
|
|
+ // Sort button placement
|
|
+ private const val SORT_BUTTON_X = 125F
|
|
+ private const val SORT_BUTTON_Y = 27.5F
|
|
+ private const val SORT_BUTTON_WIDTH = 20
|
|
+ private const val SORT_BUTTON_HEIGHT = 20
|
|
+ }
|
|
+
|
|
+ private val listWidget = LearnsetMovesScrollingWidget(
|
|
+ pX + LIST_SIDE_PADDING,
|
|
+ pY + LIST_TOP_OFFSET,
|
|
+ HALF_OVERLAY_WIDTH - (LIST_SIDE_PADDING * 2),
|
|
+ LIST_HEIGHT
|
|
+ ) { entry -> selectMove(entry) }
|
|
+
|
|
+ private val descriptionWidget = MoveDescriptionWidget(
|
|
+ pX + LIST_SIDE_PADDING + DESCRIPTION_LEFT_OFFSET,
|
|
+ pY + DESCRIPTION_TOP_OFFSET,
|
|
+ HALF_OVERLAY_WIDTH - (LIST_SIDE_PADDING * 2) - 50,
|
|
+ DESCRIPTION_HEIGHT
|
|
+ )
|
|
+
|
|
+ private var moveEntries: List<LearnsetMoveEntry> = emptyList()
|
|
+ private var filteredEntries: List<LearnsetMoveEntry> = emptyList()
|
|
+ private var selectedEntry: LearnsetMoveEntry? = null
|
|
+ private var speciesName: MutableComponent = Component.literal("")
|
|
+ private var speciesNumber: MutableComponent = "0000".text()
|
|
+ private var formLabel: MutableComponent = Component.literal("")
|
|
+ private var primaryType: ElementalType? = null
|
|
+ private var secondaryType: ElementalType? = null
|
|
+ private var availableForms: List<PokedexForm> = emptyList()
|
|
+ private var onFormChange: ((Boolean) -> Unit)? = null
|
|
+ private var filterIndex = 0
|
|
+ private var sortMode = LearnsetSort.SOURCE
|
|
+
|
|
+ private val decimalFormat = DecimalFormat("#.##").also { it.roundingMode = RoundingMode.CEILING }
|
|
+
|
|
+ private val formLeftButton: ScaledButton = ScaledButton(
|
|
+ pX + FORM_ARROW_LEFT_X,
|
|
+ pY + FORM_ARROW_Y,
|
|
+ FORM_ARROW_WIDTH,
|
|
+ FORM_ARROW_HEIGHT,
|
|
+ arrowFormLeft,
|
|
+ clickAction = { onFormChange?.invoke(false) }
|
|
+ ).apply { addWidget(this) }
|
|
+
|
|
+ private val formRightButton: ScaledButton = ScaledButton(
|
|
+ pX + FORM_ARROW_RIGHT_X,
|
|
+ pY + FORM_ARROW_Y,
|
|
+ FORM_ARROW_WIDTH,
|
|
+ FORM_ARROW_HEIGHT,
|
|
+ arrowFormRight,
|
|
+ clickAction = { onFormChange?.invoke(true) }
|
|
+ ).apply { addWidget(this) }
|
|
+
|
|
+ private val filterLeftButton: ScaledButton = ScaledButton(
|
|
+ pX + FILTER_ARROW_LEFT_X,
|
|
+ pY + FILTER_ARROW_Y,
|
|
+ FILTER_ARROW_WIDTH,
|
|
+ FILTER_ARROW_HEIGHT,
|
|
+ filterArrowLeft,
|
|
+ clickAction = { cycleFilter(false) }
|
|
+ ).apply { addWidget(this) }
|
|
+
|
|
+ private val filterRightButton: ScaledButton = ScaledButton(
|
|
+ pX + FILTER_ARROW_RIGHT_X,
|
|
+ pY + FILTER_ARROW_Y,
|
|
+ FILTER_ARROW_WIDTH,
|
|
+ FILTER_ARROW_HEIGHT,
|
|
+ filterArrowRight,
|
|
+ clickAction = { cycleFilter(true) }
|
|
+ ).apply { addWidget(this) }
|
|
+
|
|
+ private val sortButton: ScaledButton = ScaledButton(
|
|
+ pX + SORT_BUTTON_X,
|
|
+ pY + SORT_BUTTON_Y,
|
|
+ SORT_BUTTON_WIDTH,
|
|
+ SORT_BUTTON_HEIGHT,
|
|
+ sortSourceIcon,
|
|
+ clickAction = { toggleSort() }
|
|
+ ).apply { addWidget(this) }
|
|
+
|
|
+ init {
|
|
+ addWidget(listWidget)
|
|
+ addWidget(descriptionWidget)
|
|
+ }
|
|
+
|
|
+ fun setLearnset(
|
|
+ species: Species,
|
|
+ form: FormData,
|
|
+ availableForms: List<PokedexForm>,
|
|
+ selectedForm: PokedexForm,
|
|
+ onFormChange: (Boolean) -> Unit
|
|
+ ) {
|
|
+ speciesName = species.translatedName
|
|
+ speciesNumber = species.nationalPokedexNumber.toString().padStart(4, '0').text()
|
|
+ primaryType = form.primaryType
|
|
+ secondaryType = form.secondaryType
|
|
+ this.availableForms = availableForms
|
|
+ this.onFormChange = onFormChange
|
|
+ updateFormLabel(species, selectedForm)
|
|
+ updateFormButtons()
|
|
+
|
|
+ moveEntries = buildLearnsetEntries(form)
|
|
+ applyFilter()
|
|
+ selectMove(null)
|
|
+ }
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val matrices = context.pose()
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = overlayResource,
|
|
+ x = pX,
|
|
+ y = pY,
|
|
+ width = HALF_OVERLAY_WIDTH,
|
|
+ height = HALF_OVERLAY_HEIGHT
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = speciesNumber.bold(),
|
|
+ x = pX + 3,
|
|
+ y = pY + 1,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ if (!speciesName.string.isBlank()) {
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = speciesName.bold(),
|
|
+ x = pX + 26,
|
|
+ y = pY + 1,
|
|
+ colour = 0x606B6E
|
|
+ )
|
|
+ }
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = if (secondaryType != null) typeBarDouble else typeBar,
|
|
+ x = pX,
|
|
+ y = pY + TYPE_BAR_Y,
|
|
+ width = HALF_OVERLAY_WIDTH,
|
|
+ height = TYPE_BAR_HEIGHT
|
|
+ )
|
|
+
|
|
+ if (primaryType != null) {
|
|
+ TypeIcon(
|
|
+ x = pX + TYPE_ICON_X,
|
|
+ y = pY + TYPE_ICON_Y,
|
|
+ type = primaryType!!,
|
|
+ secondaryType = secondaryType
|
|
+ ).render(context)
|
|
+ }
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = formLabel.bold(),
|
|
+ x = pX + FORM_LABEL_X,
|
|
+ y = pY + FORM_LABEL_Y,
|
|
+ shadow = true,
|
|
+ centered = true
|
|
+ )
|
|
+
|
|
+ formLeftButton.render(context, mouseX, mouseY, delta)
|
|
+ formRightButton.render(context, mouseX, mouseY, delta)
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = currentFilter().label.bold(),
|
|
+ x = pX + FILTER_LABEL_X,
|
|
+ y = pY + FILTER_LABEL_Y,
|
|
+ shadow = true,
|
|
+ centered = true
|
|
+ )
|
|
+
|
|
+ filterLeftButton.render(context, mouseX, mouseY, delta)
|
|
+ filterRightButton.render(context, mouseX, mouseY, delta)
|
|
+ sortButton.render(context, mouseX, mouseY, delta)
|
|
+
|
|
+ if (sortButton.isButtonHovered(mouseX, mouseY)) {
|
|
+ val sortKey = "ui.moves.learnset.sort.${sortMode.name.lowercase()}"
|
|
+ renderTooltip(context, lang(sortKey).bold(), mouseX, mouseY, delta, -14)
|
|
+ }
|
|
+
|
|
+ /*drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = lang("ui.moves").bold(),
|
|
+ x = pX + 4,
|
|
+ y = pY + 11,
|
|
+ shadow = true
|
|
+ )*/
|
|
+
|
|
+ renderListBackground(context)
|
|
+ if (filteredEntries.isEmpty()) {
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = Component.literal("No moves available."),
|
|
+ x = pX + (HALF_OVERLAY_WIDTH / 2) - 20,
|
|
+ y = pY + LIST_TOP_OFFSET + (LIST_HEIGHT / 2) + 30,
|
|
+ shadow = false,
|
|
+ colour = 0x606B6E,
|
|
+ scale = SCALE,
|
|
+ centered = true
|
|
+ )
|
|
+ } else {
|
|
+ listWidget.renderWidget(context, mouseX, mouseY, delta)
|
|
+ }
|
|
+
|
|
+ renderDescriptionDivider(context)
|
|
+ renderDataSection(context)
|
|
+
|
|
+ descriptionWidget.renderWidget(context, mouseX, mouseY, delta)
|
|
+ }
|
|
+
|
|
+ private fun renderListBackground(context: GuiGraphics) {
|
|
+ val left = pX + LIST_SIDE_PADDING - 3
|
|
+ val top = pY + LIST_TOP_OFFSET + 54
|
|
+ val right = left + listWidget.width + 6
|
|
+ val bottom = top + LIST_HEIGHT
|
|
+ context.fill(left, top, right, bottom, LIST_BACKGROUND_COLOR)
|
|
+ }
|
|
+
|
|
+ private fun renderDescriptionDivider(context: GuiGraphics) {
|
|
+ val dividerX = pX + LIST_SIDE_PADDING + DESCRIPTION_LEFT_OFFSET + DESCRIPTION_DIVIDER_OFFSET
|
|
+ val dividerTop = pY + DESCRIPTION_TOP_OFFSET + 39
|
|
+ val dividerBottom = pY + DESCRIPTION_TOP_OFFSET + DESCRIPTION_HEIGHT + 37
|
|
+ context.fill(dividerX, dividerTop, dividerX + 1, dividerBottom, FastColor.ARGB32.color(255, 126, 231, 229))
|
|
+ }
|
|
+
|
|
+ private fun renderDataSection(context: GuiGraphics) {
|
|
+ val dataTop = pY + DATA_TOP_OFFSET
|
|
+ val dataRight = pX + HALF_OVERLAY_WIDTH - 70
|
|
+ val entry = selectedEntry
|
|
+ val move = entry?.move
|
|
+ val showMoveInfo = entry?.isDiscovered == true
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = Component.literal("Data").bold(),
|
|
+ x = pX + 4,
|
|
+ y = dataTop - 10,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = movesPowerIconResource,
|
|
+ x = (pX + 6) / SCALE,
|
|
+ y = (dataTop + 1 + DATA_INFO_TOP_OFFSET) / SCALE,
|
|
+ width = DATA_ICON_SIZE,
|
|
+ height = DATA_ICON_SIZE,
|
|
+ scale = SCALE
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = movesAccuracyIconResource,
|
|
+ x = (pX + 6) / SCALE,
|
|
+ y = (dataTop + 1 + DATA_ROW_HEIGHT + DATA_INFO_TOP_OFFSET) / SCALE,
|
|
+ width = DATA_ICON_SIZE,
|
|
+ height = DATA_ICON_SIZE,
|
|
+ scale = SCALE
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = movesCategoryIconResource,
|
|
+ x = (pX + 6) / SCALE,
|
|
+ y = (dataTop + 1 + (DATA_ROW_HEIGHT * 2) + DATA_INFO_TOP_OFFSET) / SCALE,
|
|
+ width = DATA_ICON_SIZE,
|
|
+ height = DATA_ICON_SIZE,
|
|
+ scale = SCALE
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = lang("ui.power"),
|
|
+ x = pX + DATA_LABEL_OFFSET_X,
|
|
+ y = dataTop + 1 + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = lang("ui.accuracy"),
|
|
+ x = pX + DATA_LABEL_OFFSET_X,
|
|
+ y = dataTop + 1 + DATA_ROW_HEIGHT + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = Component.literal("ui.category"),
|
|
+ x = pX + DATA_LABEL_OFFSET_X,
|
|
+ y = dataTop + 1 + (DATA_ROW_HEIGHT * 2) + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ val powerText = if (showMoveInfo && move != null && move.power.toInt() > 0) move.power.toInt().toString() else "-"
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = powerText.text(),
|
|
+ x = dataRight,
|
|
+ y = dataTop + 1 + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ val accuracyText = if (showMoveInfo && move != null) formatAccuracy(move.accuracy) else "-"
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = accuracyText.text(),
|
|
+ x = dataRight,
|
|
+ y = dataTop + 1 + DATA_ROW_HEIGHT + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ val categoryText = if (showMoveInfo && move != null) move.damageCategory.displayName else "-".text()
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = categoryText as MutableComponent,
|
|
+ x = dataRight,
|
|
+ y = dataTop + 1 + (DATA_ROW_HEIGHT * 2) + DATA_INFO_TOP_OFFSET,
|
|
+ scale = SCALE,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ /*if (showMoveInfo && move != null) {
|
|
+ MoveCategoryIcon(
|
|
+ x = pX + 6,
|
|
+ y = dataTop + 2 + (DATA_ROW_HEIGHT * 2) + DATA_INFO_TOP_OFFSET,
|
|
+ category = move.damageCategory
|
|
+ ).render(context)
|
|
+ }*/
|
|
+ }
|
|
+
|
|
+ private fun formatAccuracy(input: Double): String {
|
|
+ if (input <= 0) return "-"
|
|
+ return "${decimalFormat.format(input)}%"
|
|
+ }
|
|
+
|
|
+ private fun applyFilter() {
|
|
+ val filtered = when (currentFilter()) {
|
|
+ LearnsetFilter.ALL -> moveEntries
|
|
+ LearnsetFilter.LEVEL_UP -> moveEntries.filter { it.source == LearnsetSource.LEVEL_UP }
|
|
+ LearnsetFilter.TM -> moveEntries.filter { it.source == LearnsetSource.TM }
|
|
+ LearnsetFilter.EGG -> moveEntries.filter { it.source == LearnsetSource.EGG }
|
|
+ }
|
|
+ val sorted = sortEntries(filtered)
|
|
+ filteredEntries = sorted
|
|
+ listWidget.setEntries(sorted)
|
|
+ if (selectedEntry !in sorted) {
|
|
+ selectMove(null)
|
|
+ } else {
|
|
+ listWidget.setSelectedEntry(selectedEntry)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun sortEntries(entries: List<LearnsetMoveEntry>): List<LearnsetMoveEntry> {
|
|
+ val baseEntries = if (sortMode == LearnsetSort.DISCOVERED) {
|
|
+ entries.filter { it.isDiscovered }
|
|
+ } else {
|
|
+ entries
|
|
+ }
|
|
+ return when (sortMode) {
|
|
+ LearnsetSort.ALPHABETICAL -> baseEntries.sortedWith(
|
|
+ compareBy(
|
|
+ { it.move.displayName.string.lowercase() },
|
|
+ { it.level ?: Int.MAX_VALUE }
|
|
+ )
|
|
+ )
|
|
+ LearnsetSort.TYPE -> baseEntries.sortedWith(
|
|
+ compareBy(
|
|
+ { it.move.elementalType.displayName.string.lowercase() },
|
|
+ { it.move.displayName.string.lowercase() },
|
|
+ { it.level ?: Int.MAX_VALUE }
|
|
+ )
|
|
+ )
|
|
+ LearnsetSort.SOURCE,
|
|
+ LearnsetSort.DISCOVERED -> baseEntries.sortedWith(
|
|
+ compareBy(
|
|
+ { sourceOrder(it.source) },
|
|
+ { it.level ?: Int.MAX_VALUE },
|
|
+ { it.move.displayName.string.lowercase() }
|
|
+ )
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun cycleFilter(next: Boolean) {
|
|
+ val total = LearnsetFilter.entries.size
|
|
+ filterIndex = if (next) {
|
|
+ (filterIndex + 1) % total
|
|
+ } else {
|
|
+ (filterIndex - 1 + total) % total
|
|
+ }
|
|
+ applyFilter()
|
|
+ }
|
|
+
|
|
+ private fun toggleSort() {
|
|
+ sortMode = when (sortMode) {
|
|
+ LearnsetSort.ALPHABETICAL -> LearnsetSort.TYPE
|
|
+ LearnsetSort.TYPE -> LearnsetSort.SOURCE
|
|
+ LearnsetSort.SOURCE -> LearnsetSort.DISCOVERED
|
|
+ LearnsetSort.DISCOVERED -> LearnsetSort.ALPHABETICAL
|
|
+ }
|
|
+ sortButton.resource = when (sortMode) {
|
|
+ LearnsetSort.ALPHABETICAL -> sortAlphaIcon
|
|
+ LearnsetSort.TYPE -> sortTypeIcon
|
|
+ LearnsetSort.SOURCE -> sortSourceIcon
|
|
+ LearnsetSort.DISCOVERED -> sortDiscoveredIcon
|
|
+ }
|
|
+ applyFilter()
|
|
+ }
|
|
+
|
|
+ private fun sourceOrder(source: LearnsetSource): Int {
|
|
+ return when (source) {
|
|
+ LearnsetSource.LEVEL_UP -> 0
|
|
+ LearnsetSource.TM -> 1
|
|
+ LearnsetSource.EGG -> 2
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun currentFilter(): LearnsetFilter {
|
|
+ return LearnsetFilter.entries[filterIndex.coerceIn(0, LearnsetFilter.entries.lastIndex)]
|
|
+ }
|
|
+
|
|
+ private fun updateFormButtons() {
|
|
+ val hasForms = availableForms.size > 1
|
|
+ formLeftButton.isVisible = hasForms
|
|
+ formRightButton.isVisible = hasForms
|
|
+ formLeftButton.active = hasForms
|
|
+ formRightButton.active = hasForms
|
|
+ }
|
|
+
|
|
+ private fun updateFormLabel(species: Species, form: PokedexForm) {
|
|
+ val formName = if (form.displayForm.equals("normal", ignoreCase = true)) {
|
|
+ ""
|
|
+ } else {
|
|
+ "-${form.displayForm.lowercase().replace("-", "")}"
|
|
+ }
|
|
+ val label = lang("ui.pokedex.info.form.${species}${formName}")
|
|
+ formLabel = if (label.string.isBlank() || label.string.startsWith("cobblemon.ui.pokedex.info.form.")) {
|
|
+ Component.literal(form.displayForm)
|
|
+ } else {
|
|
+ label
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun selectMove(entry: LearnsetMoveEntry?) {
|
|
+ selectedEntry = if (selectedEntry == entry || entry == null) null else entry
|
|
+ listWidget.setSelectedEntry(selectedEntry)
|
|
+
|
|
+ if (selectedEntry?.isDiscovered == true) {
|
|
+ val description = selectedEntry!!.move.description.string
|
|
+ descriptionWidget.showPlaceholder = false
|
|
+ descriptionWidget.setText(listOf(description))
|
|
+ } else {
|
|
+ descriptionWidget.showPlaceholder = true
|
|
+ descriptionWidget.setText(emptyList())
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun buildEvolutionMoveLevelIndex(form: FormData): Map<ResourceLocation, Map<String, Int>> {
|
|
+ val evolutionForms = collectEvolutionForms(form)
|
|
+ val levelsBySpecies = mutableMapOf<ResourceLocation, MutableMap<String, Int>>()
|
|
+
|
|
+ for (evolutionForm in evolutionForms) {
|
|
+ val speciesId = evolutionForm.species.resourceIdentifier
|
|
+ val moveLevels = levelsBySpecies.getOrPut(speciesId) { mutableMapOf() }
|
|
+ buildMoveLevelIndex(evolutionForm).forEach { (moveName, level) ->
|
|
+ val current = moveLevels[moveName]
|
|
+ if (current == null || level < current) {
|
|
+ moveLevels[moveName] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return levelsBySpecies
|
|
+ }
|
|
+
|
|
+ private fun buildMoveLevelIndex(form: FormData): Map<String, Int> {
|
|
+ val levels = mutableMapOf<String, Int>()
|
|
+ form.moves.levelUpMoves.forEach { (level, moves) ->
|
|
+ moves.forEach { move ->
|
|
+ val current = levels[move.name]
|
|
+ if (current == null || level < current) {
|
|
+ levels[move.name] = level
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return levels
|
|
+ }
|
|
+
|
|
+ private fun collectEvolutionForms(rootForm: FormData): List<FormData> {
|
|
+ val results = mutableListOf<FormData>()
|
|
+ val visited = mutableSetOf<String>()
|
|
+
|
|
+ fun key(form: FormData): String {
|
|
+ return "${form.species.resourceIdentifier}|${form.name.lowercase()}"
|
|
+ }
|
|
+
|
|
+ fun nextEvolutions(current: FormData): Set<Evolution> {
|
|
+ return if (current.evolutions.isNotEmpty()) {
|
|
+ current.evolutions
|
|
+ } else {
|
|
+ current.species.evolutions
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun traverse(current: FormData) {
|
|
+ for (evolution in nextEvolutions(current)) {
|
|
+ val evolutionForm = resolveEvolutionForm(evolution) ?: continue
|
|
+ val evolutionKey = key(evolutionForm)
|
|
+ if (!visited.add(evolutionKey)) continue
|
|
+ results.add(evolutionForm)
|
|
+ traverse(evolutionForm)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ traverse(rootForm)
|
|
+ return results
|
|
+ }
|
|
+
|
|
+ private fun resolveEvolutionForm(evolution: Evolution): FormData? {
|
|
+ val speciesId = evolution.result.species?.asIdentifierDefaultingNamespace() ?: return null
|
|
+ val species = PokemonSpecies.getByIdentifier(speciesId) ?: return null
|
|
+ val formId = evolution.result.form
|
|
+ return if (formId == null) {
|
|
+ species.standardForm
|
|
+ } else {
|
|
+ species.forms.firstOrNull {
|
|
+ it.formOnlyShowdownId().equals(formId, ignoreCase = true) || it.name.equals(formId, ignoreCase = true)
|
|
+ } ?: species.standardForm
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun buildLearnsetEntries(form: FormData): List<LearnsetMoveEntry> {
|
|
+ val entries = linkedMapOf<String, LearnsetMoveEntry>()
|
|
+ val speciesId = form.species.resourceIdentifier
|
|
+ val pokedexManager = CobblemonClient.clientPokedexData
|
|
+ val highestLevel = pokedexManager.getSpeciesRecord(speciesId)?.getFormRecord(form.name)?.highestLevel ?: 0
|
|
+ val evolutionMoveLevels = buildEvolutionMoveLevelIndex(form)
|
|
+ val learnedTMs = CobblemonClient.clientTMMoveData.learnedTMs
|
|
+ val unlockAllMoveDexMovesByDefault = ServerSettings.unlockAllMoveDexMovesByDefault
|
|
+
|
|
+ fun isLevelUpDiscovered(move: MoveTemplate, level: Int): Boolean {
|
|
+ if (unlockAllMoveDexMovesByDefault) return true
|
|
+ if (highestLevel >= level) return true
|
|
+
|
|
+ for ((evolutionSpeciesId, moveLevels) in evolutionMoveLevels) {
|
|
+ val evolutionLevel = moveLevels[move.name] ?: continue
|
|
+ val evolutionHighest = pokedexManager.getSpeciesRecord(evolutionSpeciesId)?.highestLevel ?: 0
|
|
+ if (evolutionHighest >= evolutionLevel) return true
|
|
+ }
|
|
+
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ fun addEntry(
|
|
+ move: MoveTemplate,
|
|
+ source: LearnsetSource,
|
|
+ level: Int? = null,
|
|
+ tmLocked: Boolean = false,
|
|
+ isDiscovered: Boolean = true
|
|
+ ) {
|
|
+ val key = move.name
|
|
+ if (!entries.containsKey(key)) {
|
|
+ val tmId = TechnicalMachines.moveToTM[move]?.id
|
|
+ val tmUnlocked = source == LearnsetSource.TM && tmId != null && tmId in learnedTMs
|
|
+ val resolvedTmLocked = if (source == LearnsetSource.TM) tmLocked else false
|
|
+ entries[key] = LearnsetMoveEntry(move, source, level, resolvedTmLocked, isDiscovered, tmId, tmUnlocked)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ form.moves.levelUpMoves.toSortedMap().forEach { (level, moves) ->
|
|
+ moves.forEach { move ->
|
|
+ val discovered = isLevelUpDiscovered(move, level)
|
|
+ addEntry(move, LearnsetSource.LEVEL_UP, level = level, isDiscovered = discovered)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ form.moves.tmMoves.sortedBy { it.displayName.string }.forEach { move ->
|
|
+ val tmLocked = if (unlockAllMoveDexMovesByDefault) false else {
|
|
+ val tmId = TechnicalMachines.moveToTM[move]?.id
|
|
+ tmId != null && tmId !in learnedTMs
|
|
+ }
|
|
+ addEntry(move, LearnsetSource.TM, tmLocked = tmLocked, isDiscovered = !tmLocked)
|
|
+ }
|
|
+
|
|
+ form.moves.eggMoves.sortedBy { it.displayName.string }.forEach { move ->
|
|
+ addEntry(move, LearnsetSource.EGG)
|
|
+ }
|
|
+
|
|
+ return entries.values.toList()
|
|
+ }
|
|
+
|
|
+ private enum class LearnsetSource(val label: String) {
|
|
+ LEVEL_UP("Lv"),
|
|
+ TM("TM"),
|
|
+ EGG("Egg")
|
|
+ }
|
|
+
|
|
+ private enum class LearnsetFilter(val label: MutableComponent) {
|
|
+ ALL(Component.literal("All")),
|
|
+ LEVEL_UP(Component.literal("Level-Up")),
|
|
+ TM(Component.literal("TM")),
|
|
+ EGG(Component.literal("Egg"))
|
|
+ }
|
|
+
|
|
+ private enum class LearnsetSort {
|
|
+ ALPHABETICAL,
|
|
+ TYPE,
|
|
+ SOURCE,
|
|
+ DISCOVERED
|
|
+ }
|
|
+
|
|
+ private data class LearnsetMoveEntry(
|
|
+ val move: MoveTemplate,
|
|
+ val source: LearnsetSource,
|
|
+ val level: Int? = null,
|
|
+ val tmLocked: Boolean = false,
|
|
+ val isDiscovered: Boolean = true,
|
|
+ val tmId: ResourceLocation? = null,
|
|
+ val tmUnlocked: Boolean = false
|
|
+ )
|
|
+
|
|
+ // todo fix the scrolling area height
|
|
+ private class LearnsetMovesScrollingWidget(
|
|
+ val pX: Int,
|
|
+ val pY: Int,
|
|
+ listWidth: Int,
|
|
+ listHeight: Int,
|
|
+ val onSelect: (LearnsetMoveEntry?) -> Unit
|
|
+ ) : ScrollingWidget<LearnsetMovesScrollingWidget.MoveEntrySlot>(
|
|
+ width = listWidth,
|
|
+ height = listHeight,
|
|
+ left = pX,
|
|
+ top = pY,
|
|
+ slotHeight = LIST_SLOT_HEIGHT
|
|
+ ) {
|
|
+ private var selectedEntry: LearnsetMoveEntry? = null
|
|
+
|
|
+ fun setEntries(entries: List<LearnsetMoveEntry>) {
|
|
+ clearEntries()
|
|
+ entries.forEach { entry -> addEntry(MoveEntrySlot(entry, this)) }
|
|
+ scrollAmount = 0.0
|
|
+ }
|
|
+
|
|
+
|
|
+
|
|
+ fun setSelectedEntry(entry: LearnsetMoveEntry?) {
|
|
+ selectedEntry = entry
|
|
+ }
|
|
+
|
|
+ override fun getScrollbarPosition(): Int {
|
|
+ return left + width - 3
|
|
+ }
|
|
+
|
|
+ override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val xLeft = this.scrollbarPosition
|
|
+ val xRight = xLeft + 3
|
|
+ val yStart = y + 1
|
|
+
|
|
+ val barHeight = this.bottom - yStart
|
|
+
|
|
+ var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
|
+ yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
|
|
+ var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + yStart
|
|
+ if (yTop < yStart) yTop = yStart
|
|
+
|
|
+ context.fill(xLeft + 1, yStart, xRight - 1, this.bottom, FastColor.ARGB32.color(255, 126, 231, 229)) // track
|
|
+ context.fill(xLeft, yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 58, 150, 182)) // bar
|
|
+ }
|
|
+
|
|
+ override fun getEntry(index: Int): MoveEntrySlot {
|
|
+ return children()[index] as MoveEntrySlot
|
|
+ }
|
|
+
|
|
+ inner class MoveEntrySlot(
|
|
+ val entry: LearnsetMoveEntry,
|
|
+ private val parentList: LearnsetMovesScrollingWidget
|
|
+ ) : Slot<MoveEntrySlot>() {
|
|
+ private var lastX = 0
|
|
+ private var lastY = 0
|
|
+
|
|
+ override fun render(
|
|
+ context: GuiGraphics,
|
|
+ index: Int,
|
|
+ y: Int,
|
|
+ x: Int,
|
|
+ entryWidth: Int,
|
|
+ entryHeight: Int,
|
|
+ mouseX: Int,
|
|
+ mouseY: Int,
|
|
+ hovered: Boolean,
|
|
+ tickDelta: Float
|
|
+ ) {
|
|
+ lastX = x
|
|
+ lastY = y
|
|
+
|
|
+ val isSelected = entry == parentList.selectedEntry
|
|
+ if (hovered || isSelected) {
|
|
+ val color = if (isSelected) {
|
|
+ FastColor.ARGB32.color(140, 58, 150, 182)
|
|
+ } else {
|
|
+ FastColor.ARGB32.color(90, 126, 231, 229)
|
|
+ }
|
|
+ context.fill(x, y, x + entryWidth - 3, y + entryHeight, color)
|
|
+ }
|
|
+
|
|
+ val leftOffset = LIST_TM_ICON_OFFSET
|
|
+ if (entry.tmId != null) {
|
|
+ val (red, green, blue) = if (entry.tmUnlocked) {
|
|
+ Triple(0.32F, 0.82F, 0.46F)
|
|
+ } else {
|
|
+ Triple(0.6F, 0.6F, 0.6F)
|
|
+ }
|
|
+ val iconX = x + 1
|
|
+ val iconY = y + 1
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = tmDiscIcon,
|
|
+ x = iconX / SCALE,
|
|
+ y = iconY / SCALE,
|
|
+ width = LIST_TM_ICON_TEXTURE_SIZE,
|
|
+ height = LIST_TM_ICON_TEXTURE_SIZE,
|
|
+ red = red,
|
|
+ green = green,
|
|
+ blue = blue,
|
|
+ alpha = 1F,
|
|
+ scale = SCALE
|
|
+ )
|
|
+ val hoveringIcon = mouseX in iconX..(iconX + LIST_TM_ICON_RENDER_SIZE) &&
|
|
+ mouseY in iconY..(iconY + LIST_TM_ICON_RENDER_SIZE)
|
|
+ if (hoveringIcon) {
|
|
+ val tooltipKey = if (entry.tmUnlocked) {
|
|
+ "ui.moves.learnset.tm.discovered"
|
|
+ } else {
|
|
+ "egg_group.undiscovered"
|
|
+ }
|
|
+ val tooltipText = lang(tooltipKey)
|
|
+ val textWidth = Minecraft.getInstance().font.width(tooltipText.font(CobblemonResources.DEFAULT_LARGE))
|
|
+ val tooltipWidth = textWidth + 6
|
|
+ val listLeft = parentList.left + 2
|
|
+ val listRight = parentList.left + parentList.width - 6
|
|
+ val minCenter = listLeft + (tooltipWidth / 2)
|
|
+ val maxCenter = listRight - (tooltipWidth / 2)
|
|
+ val tooltipX = if (minCenter <= maxCenter) {
|
|
+ Mth.clamp(mouseX, minCenter, maxCenter)
|
|
+ } else {
|
|
+ mouseX
|
|
+ }
|
|
+ renderTooltip(context, tooltipText, tooltipX, mouseY, tickDelta, -14)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = moveDexTypeIcons,
|
|
+ x = (x + 1 + leftOffset) / SCALE,
|
|
+ y = (y + 1) / SCALE,
|
|
+ width = LIST_TYPE_ICON_SIZE,
|
|
+ height = LIST_TYPE_ICON_SIZE,
|
|
+ uOffset = LIST_TYPE_ICON_SIZE * entry.move.elementalType.textureXMultiplier.toFloat() + 0.1,
|
|
+ textureWidth = LIST_TYPE_ICON_SIZE * 18,
|
|
+ textureHeight = LIST_TYPE_ICON_SIZE,
|
|
+ scale = SCALE
|
|
+ )
|
|
+
|
|
+ val displayName = if (entry.isDiscovered) {
|
|
+ entry.move.displayName
|
|
+ } else {
|
|
+ Component.literal("?????")
|
|
+ }
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = displayName,
|
|
+ x = x + 12 + leftOffset,
|
|
+ y = y + 2,
|
|
+ scale = SCALE,
|
|
+ shadow = false,
|
|
+ colour = if (entry.isDiscovered) 0x606B6E else 0x8A8F91
|
|
+ )
|
|
+
|
|
+ val rightLabel = when {
|
|
+ entry.level != null -> "Lv.${entry.level}"
|
|
+ entry.source == LearnsetSource.TM -> entry.source.label
|
|
+ else -> entry.source.label
|
|
+ }
|
|
+
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = rightLabel.text(),
|
|
+ x = x + entryWidth - 6,
|
|
+ y = y + 2,
|
|
+ scale = SCALE,
|
|
+ shadow = false,
|
|
+ colour = if (entry.tmLocked || !entry.isDiscovered) 0x8A8F91 else 0x606B6E
|
|
+ )
|
|
+
|
|
+ }
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ val canClick = mouseX < (lastX + (parentList.width - 3))
|
|
+ if (canClick) {
|
|
+ onSelect(entry)
|
|
+ Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.POKEDEX_CLICK_SHORT, 1.0F))
|
|
+ }
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ override fun getNarration(): Component {
|
|
+ return if (entry.isDiscovered) entry.move.displayName else Component.literal("?????")
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private class MoveDescriptionWidget(
|
|
+ val pX: Int,
|
|
+ val pY: Int,
|
|
+ width: Int,
|
|
+ height: Int
|
|
+ ) : ScrollingWidget<MoveDescriptionWidget.TextSlot>(
|
|
+ left = pX,
|
|
+ top = pY,
|
|
+ width = width,
|
|
+ height = height,
|
|
+ slotHeight = LIST_SLOT_HEIGHT,
|
|
+ scrollBarWidth = DESCRIPTION_SCROLLBAR_WIDTH
|
|
+ ) {
|
|
+ var showPlaceholder = true
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ if (showPlaceholder) {
|
|
+ /*drawScaledText(
|
|
+ context = context,
|
|
+ text = Component.literal("Select a move."),
|
|
+ x = pX + (width / 2),
|
|
+ y = pY + (height / 2) - 3,
|
|
+ shadow = false,
|
|
+ colour = 0x606B6E,
|
|
+ scale = SCALE,
|
|
+ centered = true
|
|
+ )*/
|
|
+ return
|
|
+ }
|
|
+ super.renderWidget(context, mouseX, mouseY, delta)
|
|
+ }
|
|
+
|
|
+ fun setText(text: Collection<String>) {
|
|
+ clearEntries()
|
|
+ text.forEach { line ->
|
|
+ Minecraft.getInstance().font.splitter.splitLines(
|
|
+ Component.literal(line),
|
|
+ ((width - DESCRIPTION_SCROLLBAR_OFFSET - (POKEMON_DESCRIPTION_PADDING * 2)) / SCALE).toInt(),
|
|
+ Style.EMPTY
|
|
+ ).stream()
|
|
+ .map { it.string }
|
|
+ .forEach { addEntry(TextSlot(it)) }
|
|
+ }
|
|
+ scrollAmount = 0.0
|
|
+ }
|
|
+
|
|
+ override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val xLeft = this.scrollbarPosition
|
|
+ val xRight = xLeft + scrollBarWidth
|
|
+ val yStart = y + 1
|
|
+
|
|
+ val barHeight = this.bottom - yStart
|
|
+
|
|
+ var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
|
+ yBottom = Mth.clamp(yBottom, 16, barHeight - 6)
|
|
+ var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + yStart
|
|
+ if (yTop < yStart) yTop = yStart
|
|
+
|
|
+ context.fill(xLeft + 1, yStart, xRight - 1, this.bottom, FastColor.ARGB32.color(255, 126, 231, 229)) // track
|
|
+ context.fill(xLeft, yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 58, 150, 182)) // bar
|
|
+ }
|
|
+
|
|
+ override fun getScrollbarPosition(): Int {
|
|
+ return left + width - DESCRIPTION_SCROLLBAR_OFFSET
|
|
+ }
|
|
+
|
|
+ class TextSlot(val text: String) : Slot<TextSlot>() {
|
|
+ override fun render(
|
|
+ context: GuiGraphics,
|
|
+ index: Int,
|
|
+ y: Int,
|
|
+ x: Int,
|
|
+ entryWidth: Int,
|
|
+ entryHeight: Int,
|
|
+ mouseX: Int,
|
|
+ mouseY: Int,
|
|
+ hovered: Boolean,
|
|
+ tickDelta: Float
|
|
+ ) {
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = text.text(),
|
|
+ x = x + POKEMON_DESCRIPTION_PADDING,
|
|
+ y = y + 2,
|
|
+ scale = SCALE,
|
|
+ shadow = false,
|
|
+ colour = 0x606B6E
|
|
+ )
|
|
+ }
|
|
+
|
|
+ override fun getNarration(): Component {
|
|
+ return Component.literal(text)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt
|
|
index 770faadeaa..784de205ee 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pokedex/widgets/PokemonInfoWidget.kt
|
|
@@ -125,6 +125,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
|
|
|
var ticksElapsed = 0
|
|
var pokeBallBackgroundFrame = 0
|
|
+ var isSuppressed = false
|
|
|
|
class VariationButtonWrapper(
|
|
val parent: PokemonInfoWidget,
|
|
@@ -259,6 +260,9 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
|
).apply { addWidget(this) }
|
|
|
|
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ if (isSuppressed) {
|
|
+ return
|
|
+ }
|
|
val currentEntry = this.currentEntry ?: return
|
|
|
|
val hasKnowledge = CobblemonClient.clientPokedexData.getKnowledgeForSpecies(currentEntry.speciesId) != PokedexEntryProgress.NONE
|
|
@@ -512,6 +516,40 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
|
matrices.popPose()
|
|
}
|
|
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (isSuppressed) {
|
|
+ return false
|
|
+ }
|
|
+ return super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (isSuppressed) {
|
|
+ return false
|
|
+ }
|
|
+ return super.mouseReleased(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ override fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
|
|
+ if (isSuppressed) {
|
|
+ return false
|
|
+ }
|
|
+ return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
|
+ }
|
|
+
|
|
+ override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
|
+ if (isSuppressed) {
|
|
+ return false
|
|
+ }
|
|
+ return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
|
+ }
|
|
+
|
|
+ override fun mouseMoved(mouseX: Double, mouseY: Double) {
|
|
+ if (!isSuppressed) {
|
|
+ super.mouseMoved(mouseX, mouseY)
|
|
+ }
|
|
+ }
|
|
+
|
|
fun setDexEntry(pokedexEntry : PokedexEntry) {
|
|
this.currentEntry = pokedexEntry
|
|
this.renderablePokemon = null
|
|
@@ -624,6 +662,19 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
|
updateAspects()
|
|
}
|
|
|
|
+ fun setSelectedForm(newForm: PokedexForm) {
|
|
+ if (visibleForms.isEmpty() || currentEntry == null) {
|
|
+ return
|
|
+ }
|
|
+ val index = visibleForms.indexOfFirst { it.displayForm.equals(newForm.displayForm, ignoreCase = true) }
|
|
+ if (index == -1) {
|
|
+ return
|
|
+ }
|
|
+ selectedFormIndex = index
|
|
+ setupButtons(currentEntry!!, visibleForms[selectedFormIndex])
|
|
+ updateAspects()
|
|
+ }
|
|
+
|
|
fun updateAspects() {
|
|
if (visibleForms.isEmpty()) {
|
|
return
|
|
@@ -706,4 +757,4 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
|
fun playSound(soundEvent: SoundEvent) {
|
|
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSlotWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSlotWidget.kt
|
|
index a53cb8eca2..f7ca2e10c2 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSlotWidget.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSlotWidget.kt
|
|
@@ -36,9 +36,9 @@ class MoveSlotWidget(
|
|
): SoundlessWidget(pX, pY, MOVE_WIDTH, MOVE_HEIGHT, Component.literal(move?.name ?: "")) {
|
|
|
|
companion object {
|
|
- private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
|
- private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
|
- private val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
|
|
+ val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
|
+ val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
|
+ val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
|
|
|
|
const val MOVE_WIDTH = 108
|
|
const val MOVE_HEIGHT = 22
|
|
@@ -50,6 +50,7 @@ class MoveSlotWidget(
|
|
}.apply {
|
|
addWidget(this)
|
|
}
|
|
+
|
|
private val moveDownButton = ReorderMoveButton(x, y, false) {
|
|
movesWidget.selectMove(null)
|
|
movesWidget.reorderMove(this, false)
|
|
@@ -67,46 +68,46 @@ class MoveSlotWidget(
|
|
}.apply {
|
|
addWidget(this)
|
|
}
|
|
+
|
|
val elementalType:ElementalType = Moves.getByNameOrDummy(move?.name ?: "").getEffectiveElementalType(pokemon)
|
|
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
|
val matrices = context.pose()
|
|
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
|
|
if (move != null) {
|
|
- val moveTemplate = Moves.getByNameOrDummy(move.name)
|
|
val rgb = elementalType.hue.toRGB()
|
|
|
|
if (movesWidget.selectedMove == move) {
|
|
blitk(
|
|
- matrixStack = matrices,
|
|
- texture = moveSelectedOverlayResource,
|
|
- x = x - 1,
|
|
- y = y - 1,
|
|
- width = MOVE_WIDTH + 2,
|
|
- height = MOVE_HEIGHT + 2
|
|
+ matrixStack = matrices,
|
|
+ texture = moveSelectedOverlayResource,
|
|
+ x = x - 1,
|
|
+ y = y - 1,
|
|
+ width = MOVE_WIDTH + 2,
|
|
+ height = MOVE_HEIGHT + 2
|
|
)
|
|
}
|
|
|
|
blitk(
|
|
- matrixStack = matrices,
|
|
- texture = moveResource,
|
|
- x = x,
|
|
- y = y,
|
|
- width = MOVE_WIDTH,
|
|
- height = MOVE_HEIGHT,
|
|
- vOffset = if (isHovered) MOVE_HEIGHT else 0,
|
|
- textureHeight = MOVE_HEIGHT * 2,
|
|
- red = rgb.first,
|
|
- green = rgb.second,
|
|
- blue = rgb.third
|
|
+ matrixStack = matrices,
|
|
+ texture = moveResource,
|
|
+ x = x,
|
|
+ y = y,
|
|
+ width = MOVE_WIDTH,
|
|
+ height = MOVE_HEIGHT,
|
|
+ vOffset = if (isHovered) MOVE_HEIGHT else 0,
|
|
+ textureHeight = MOVE_HEIGHT * 2,
|
|
+ red = rgb.first,
|
|
+ green = rgb.second,
|
|
+ blue = rgb.third
|
|
)
|
|
|
|
blitk(
|
|
- matrixStack = matrices,
|
|
- texture = moveOverlayResource,
|
|
- x = x,
|
|
- y = y,
|
|
- width = MOVE_WIDTH,
|
|
- height = MOVE_HEIGHT
|
|
+ matrixStack = matrices,
|
|
+ texture = moveOverlayResource,
|
|
+ x = x,
|
|
+ y = y,
|
|
+ width = MOVE_WIDTH,
|
|
+ height = MOVE_HEIGHT
|
|
)
|
|
|
|
var movePPText = Component.literal("${move.currentPp}/${move.maxPp}").bold()
|
|
@@ -116,36 +117,28 @@ class MoveSlotWidget(
|
|
}
|
|
|
|
drawScaledText(
|
|
- context = context,
|
|
- font = CobblemonResources.DEFAULT_LARGE,
|
|
- text = movePPText,
|
|
- x = x + 93,
|
|
- y = y + 13,
|
|
- centered = true
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = movePPText,
|
|
+ x = x + 93,
|
|
+ y = y + 13,
|
|
+ centered = true
|
|
)
|
|
|
|
// Type Icon
|
|
- TypeIcon(
|
|
- x = x + 2,
|
|
- y = y + 2,
|
|
- type = elementalType
|
|
- ).render(context)
|
|
+ TypeIcon(x = x + 2, y = y + 2, type = elementalType).render(context)
|
|
|
|
// Move Category
|
|
- MoveCategoryIcon(
|
|
- x = x + 66,
|
|
- y = y + 13.5,
|
|
- category = move.damageCategory
|
|
- ).render(context)
|
|
+ MoveCategoryIcon(x = x + 66, y = y + 13.5, category = move.damageCategory).render(context)
|
|
|
|
// Move Name
|
|
drawScaledText(
|
|
- context = context,
|
|
- font = CobblemonResources.DEFAULT_LARGE,
|
|
- text = move.displayName.bold(),
|
|
- x = x + 28,
|
|
- y = y + 2,
|
|
- shadow = true
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = move.displayName.bold(),
|
|
+ x = x + 28,
|
|
+ y = y + 2,
|
|
+ shadow = true
|
|
)
|
|
|
|
// Reorder Buttons
|
|
@@ -158,9 +151,7 @@ class MoveSlotWidget(
|
|
}
|
|
|
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
- if (isHovered) {
|
|
- movesWidget.selectMove(move)
|
|
- }
|
|
+ if (isHovered) movesWidget.selectMove(move)
|
|
return super.mouseClicked(mouseX, mouseY, button)
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSwapScreen.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSwapScreen.kt
|
|
index 2f80532e5c..453cd567d7 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSwapScreen.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MoveSwapScreen.kt
|
|
@@ -162,7 +162,7 @@ class MoveSwapScreen(
|
|
|
|
drawScaledText(
|
|
context = context,
|
|
- text = pane.movesWidget.format(move.accuracy).text(),
|
|
+ text = MovesWidget.format(move.accuracy).text(),
|
|
x = rowLeft + 37,
|
|
y = tweakedRowTop + 12,
|
|
scale = MovesWidget.SCALE,
|
|
@@ -171,7 +171,7 @@ class MoveSwapScreen(
|
|
|
|
drawScaledText(
|
|
context = context,
|
|
- text = pane.movesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
|
|
+ text = MovesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
|
|
x = rowLeft + 60.5,
|
|
y = tweakedRowTop + 12,
|
|
scale = MovesWidget.SCALE,
|
|
@@ -232,4 +232,4 @@ class MoveSwapScreen(
|
|
return false
|
|
}
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MovesWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MovesWidget.kt
|
|
index 2b47353421..e12ffc65fd 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MovesWidget.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/moves/MovesWidget.kt
|
|
@@ -39,6 +39,11 @@ class MovesWidget(
|
|
it.roundingMode = RoundingMode.CEILING
|
|
}
|
|
|
|
+ fun format(input: Double): String {
|
|
+ if (input <= 0) return "ΓÇö"
|
|
+ return "${decimalFormat.format(input)}%"
|
|
+ }
|
|
+
|
|
private val movesBaseResource = cobblemonResource("textures/gui/summary/summary_moves_base.png")
|
|
val movesPowerIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_power.png")
|
|
val movesAccuracyIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_accuracy.png")
|
|
@@ -208,11 +213,6 @@ class MovesWidget(
|
|
)
|
|
}
|
|
|
|
- fun format(input: Double): String {
|
|
- if (input <= 0) return "ΓÇö"
|
|
- return "${decimalFormat.format(input)}%"
|
|
- }
|
|
-
|
|
fun selectMove(move: Move?) {
|
|
selectedMove = if (selectedMove == move || move == null) null else move
|
|
descriptionScrollList.setMoveDescription(selectedMove?.description ?: "".text())
|
|
@@ -232,4 +232,4 @@ class MovesWidget(
|
|
if (descriptionScrollList.isHovered) descriptionScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
|
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/IconButton.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/IconButton.kt
|
|
new file mode 100644
|
|
index 0000000000..e144079a42
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/IconButton.kt
|
|
@@ -0,0 +1,74 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.client.gui.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.gui.components.Button
|
|
+import net.minecraft.client.gui.narration.NarrationElementOutput
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.client.sounds.SoundManager
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+
|
|
+class IconButton(
|
|
+ var buttonX: Float,
|
|
+ var buttonY: Float,
|
|
+ val buttonWidth: Number,
|
|
+ val buttonHeight: Number,
|
|
+ var resource: ResourceLocation? = null,
|
|
+ val scale: Float = 0.5F,
|
|
+ val hoverExtendRight: Int = 0,
|
|
+ val silent: Boolean = false,
|
|
+ val clickAction: OnPress
|
|
+): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
|
+
|
|
+ override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
|
|
+ override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
|
+ resource?.let {
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = it,
|
|
+ x = buttonX / scale,
|
|
+ y = buttonY / scale,
|
|
+ width = buttonWidth,
|
|
+ height = buttonHeight,
|
|
+ vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) buttonHeight else 0,
|
|
+ textureHeight = buttonHeight.toFloat() * 2,
|
|
+ scale = scale
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (active && visible && button == 0 && isMouseOver(mouseX, mouseY)) {
|
|
+ playDownSound(Minecraft.getInstance().soundManager)
|
|
+ onClick(mouseX, mouseY)
|
|
+ return true
|
|
+ }
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ override fun isMouseOver(mouseX: Double, mouseY: Double): Boolean {
|
|
+ val minX = x.toDouble()
|
|
+ val maxX = minX + width + hoverExtendRight
|
|
+ val minY = y.toDouble()
|
|
+ val maxY = minY + (height * scale)
|
|
+ return active && visible && mouseX >= minX && mouseX < maxX && mouseY >= minY && mouseY < maxY
|
|
+ }
|
|
+
|
|
+ override fun playDownSound(soundManager: SoundManager) {
|
|
+ if (visible && !this.silent) soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MoveSearchWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MoveSearchWidget.kt
|
|
new file mode 100644
|
|
index 0000000000..41b03f81d8
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MoveSearchWidget.kt
|
|
@@ -0,0 +1,77 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.client.gui.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.text.bold
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.client.CobblemonResources
|
|
+import com.cobblemon.mod.common.client.gui.pc.TextWidget
|
|
+import com.cobblemon.mod.common.client.render.drawScaledText
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.network.chat.Component
|
|
+
|
|
+class MoveSearchWidget(
|
|
+ private var pX: Int,
|
|
+ private var pY: Int,
|
|
+ text: Component = "MoveSearchWidget".text(),
|
|
+ update: () -> (Unit)
|
|
+): TextWidget(pX, pY, text = text, update = update) {
|
|
+
|
|
+ companion object {
|
|
+ const val ICON_SIZE = 16
|
|
+ const val SCALE = 0.5F
|
|
+
|
|
+ private val iconFilterResource = cobblemonResource("textures/gui/tmmachine/icon_search.png")
|
|
+ private val labelFilter = lang("ui.pokedex.search")
|
|
+ }
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ x = (x - 10.5) / SCALE,
|
|
+ y = (y + 2.5) / SCALE,
|
|
+ texture = iconFilterResource,
|
|
+ width = ICON_SIZE,
|
|
+ height = ICON_SIZE,
|
|
+ vOffset = if (isFocused || !value.isEmpty() || iconHovered(mouseX, mouseY)) ICON_SIZE else 0,
|
|
+ textureHeight = ICON_SIZE * 2,
|
|
+ scale = SCALE
|
|
+ )
|
|
+
|
|
+ val input = applyTextCursor(value, labelFilter)
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = input.bold(),
|
|
+ x = pX,
|
|
+ y = pY + 2,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ renderCursor(context, input)
|
|
+ }
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (iconHovered(mouseX.toInt(), mouseY.toInt())) {
|
|
+ Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F, 0.25F))
|
|
+ // Simulate clicking on text area if icon is clicked
|
|
+ if (!isFocused) return super.mouseClicked(x + (width / 2.0), y + (height / 2.0), button)
|
|
+ }
|
|
+ return super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ fun iconHovered(mouseX: Int, mouseY: Int): Boolean = mouseX >= (x - 10.5) && mouseY >= (y + 2.5)
|
|
+ && mouseX <= ((x - 10.5) + (ICON_SIZE * SCALE)) && mouseY <= ((y + 2.5) + (ICON_SIZE * SCALE))
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MovesScrollingWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MovesScrollingWidget.kt
|
|
new file mode 100644
|
|
index 0000000000..371237731e
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/MovesScrollingWidget.kt
|
|
@@ -0,0 +1,200 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.reactive.SettableObservable
|
|
+import com.cobblemon.mod.common.api.text.bold
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
|
+import com.cobblemon.mod.common.client.CobblemonResources
|
|
+import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
|
+import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
|
+import com.cobblemon.mod.common.client.gui.TypeIcon
|
|
+import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSlotWidget
|
|
+import com.cobblemon.mod.common.client.gui.tmmachine.MovesScrollingWidget.ScrollSlot
|
|
+import com.cobblemon.mod.common.client.render.drawScaledText
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import com.cobblemon.mod.common.util.math.toRGB
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.util.FastColor
|
|
+import net.minecraft.util.Mth
|
|
+
|
|
+class MovesScrollingWidget(val pX: Int, val pY: Int, val tmList: SettableObservable<MutableList<TechnicalMachine>>, val setTM: (TechnicalMachine?, Boolean) -> (Unit)): ScrollingWidget<ScrollSlot>(
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT,
|
|
+ left = pX,
|
|
+ top = pY - HEIGHT,
|
|
+ slotHeight = SLOT_HEIGHT + SLOT_SPACING
|
|
+) {
|
|
+ companion object {
|
|
+ const val WIDTH = 115
|
|
+ const val HEIGHT = 82
|
|
+ const val SLOT_HEIGHT = 22
|
|
+ const val SLOT_SPACING = 4
|
|
+
|
|
+ val searchBar = cobblemonResource("textures/gui/tmmachine/search_bar.png")
|
|
+ }
|
|
+
|
|
+ var highlightedMoveId: ResourceLocation? = null
|
|
+
|
|
+ init {
|
|
+ tmList.subscribeIncludingCurrent { tmList ->
|
|
+ replaceEntries(tmList.map { tm -> ScrollSlot(tm, setTM) })
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun setSlotHighlighted(id: ResourceLocation?) {
|
|
+ highlightedMoveId = id
|
|
+ for (child in children()) {
|
|
+ if (child is ScrollSlot) {
|
|
+ child.highlighted = child.tm.id == highlightedMoveId
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun getScrollbarPosition(): Int = rowLeft + width - 3
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = searchBar,
|
|
+ x = left - 4,
|
|
+ y = y - 19,
|
|
+ width = 120,
|
|
+ height = 19
|
|
+ )
|
|
+ super.renderWidget(context, mouseX, mouseY, delta)
|
|
+ }
|
|
+
|
|
+ override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val xLeft = this.scrollbarPosition
|
|
+ val xRight = xLeft + 3
|
|
+
|
|
+ val barHeight = this.bottom - this.y
|
|
+
|
|
+ var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
|
+ yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
|
|
+ var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
|
|
+ if (yTop < this.y) {
|
|
+ yTop = this.y
|
|
+ }
|
|
+
|
|
+ context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
|
|
+ context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
|
|
+ }
|
|
+
|
|
+ override fun getEntry(index: Int): ScrollSlot = children()[index] as ScrollSlot
|
|
+
|
|
+ class ScrollSlot(val tm: TechnicalMachine, val setTM : (TechnicalMachine?, clicked: Boolean) -> Unit): Slot<ScrollSlot>() {
|
|
+ var posX: Int = 0
|
|
+ var posY: Int = 0
|
|
+ var highlighted = false
|
|
+
|
|
+ override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {
|
|
+ posX = x
|
|
+ posY = y
|
|
+ val moveTemplate = tm.moveName
|
|
+ val rgb = moveTemplate.elementalType.hue.toRGB()
|
|
+ val isHovered = hovered && (mouseX < x + (108)) // Prevent hover when over scrollbar
|
|
+ val startPosY = y + SLOT_SPACING
|
|
+ val startPosX = x + 1 // Hover border for move renders at x, so render move element to the right by 1
|
|
+
|
|
+ val matrices = context.pose()
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = MoveSlotWidget.moveResource,
|
|
+ x = startPosX,
|
|
+ y = startPosY,
|
|
+ width = MoveSlotWidget.MOVE_WIDTH,
|
|
+ height = MoveSlotWidget.MOVE_HEIGHT,
|
|
+ vOffset = if (isHovered) MoveSlotWidget.MOVE_HEIGHT else 0,
|
|
+ textureHeight = MoveSlotWidget.MOVE_HEIGHT * 2,
|
|
+ red = rgb.first,
|
|
+ green = rgb.second,
|
|
+ blue = rgb.third
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = MoveSlotWidget.moveOverlayResource,
|
|
+ x = startPosX,
|
|
+ y = startPosY,
|
|
+ width = MoveSlotWidget.MOVE_WIDTH,
|
|
+ height = MoveSlotWidget.MOVE_HEIGHT
|
|
+ )
|
|
+
|
|
+ if (highlighted || isHovered) {
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = MoveSlotWidget.moveSelectedOverlayResource,
|
|
+ x = startPosX - 1,
|
|
+ y = startPosY - 1,
|
|
+ width = MoveSlotWidget.MOVE_WIDTH + 2,
|
|
+ height = MoveSlotWidget.MOVE_HEIGHT + 2
|
|
+ )
|
|
+ }
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = lang("ui.moves.pp", moveTemplate.pp).bold(),
|
|
+ x = startPosX + 93,
|
|
+ y = startPosY + 13,
|
|
+ centered = true
|
|
+ )
|
|
+
|
|
+ // Type Icon
|
|
+ TypeIcon(
|
|
+ x = x + 2,
|
|
+ y = startPosY + 2,
|
|
+ type = moveTemplate.elementalType
|
|
+ ).render(context)
|
|
+
|
|
+ // Move Category
|
|
+ MoveCategoryIcon(
|
|
+ x = x + 66,
|
|
+ y = startPosY + 13.5,
|
|
+ category = moveTemplate.damageCategory
|
|
+ ).render(context)
|
|
+
|
|
+ // Move Name
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = moveTemplate.displayName.bold(),
|
|
+ x = x + 28,
|
|
+ y = startPosY + 2,
|
|
+ shadow = true
|
|
+ )
|
|
+
|
|
+ if (isHovered) {
|
|
+ setTM(tm, false)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ // Prevent clicking through scroll bar
|
|
+ if (mouseX < posX + (108)) {
|
|
+ setTM(tm, true)
|
|
+ Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
|
+ }
|
|
+ return super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ override fun getNarration(): Component {
|
|
+ return tm.moveName.displayName
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/StartButton.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/StartButton.kt
|
|
new file mode 100644
|
|
index 0000000000..dc513d458e
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/StartButton.kt
|
|
@@ -0,0 +1,89 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.gui.components.Button
|
|
+import net.minecraft.client.gui.narration.NarrationElementOutput
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.client.sounds.SoundManager
|
|
+
|
|
+class StartButton(
|
|
+ var buttonX: Float,
|
|
+ var buttonY: Float,
|
|
+ val clickAction: OnPress
|
|
+): Button(buttonX.toInt(), buttonY.toInt(), WIDTH, HEIGHT, "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
|
+ companion object {
|
|
+ const val WIDTH = 22
|
|
+ const val HEIGHT = 12
|
|
+
|
|
+ val base = cobblemonResource("textures/gui/tmmachine/button_start.png")
|
|
+ val baseStop = cobblemonResource("textures/gui/tmmachine/button_stop.png")
|
|
+ val baseDisabled = cobblemonResource("textures/gui/tmmachine/button_start_disabled.png")
|
|
+
|
|
+ val iconStart = cobblemonResource("textures/gui/tmmachine/button_icon_start.png")
|
|
+ val iconStop = cobblemonResource("textures/gui/tmmachine/button_icon_stop.png")
|
|
+ val iconRepeat = cobblemonResource("textures/gui/tmmachine/button_icon_repeat.png")
|
|
+ }
|
|
+
|
|
+ var disabled = false
|
|
+ var processing = false
|
|
+ var shouldRepeat = false
|
|
+
|
|
+ override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
|
|
+ override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
|
|
+
|
|
+ override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
|
+ val resource = if (disabled) baseDisabled
|
|
+ else (if (processing) baseStop else base)
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = resource,
|
|
+ x = buttonX,
|
|
+ y = buttonY,
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT,
|
|
+ vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
|
+ textureHeight = HEIGHT * 2,
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = if (processing) iconStop else (if (shouldRepeat) iconRepeat else iconStart),
|
|
+ x = buttonX,
|
|
+ y = buttonY,
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT
|
|
+ )
|
|
+ }
|
|
+
|
|
+ /*override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (active && visible && !disabled && isMouseOver(mouseX, mouseY)) {
|
|
+ super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+ return false
|
|
+ }*/
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (!active || !visible || disabled) return false
|
|
+ return super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ override fun playDownSound(soundManager: SoundManager) {
|
|
+ if (visible && !disabled) {
|
|
+ soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMMachineScreen.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMMachineScreen.kt
|
|
new file mode 100644
|
|
index 0000000000..639cc68de7
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMMachineScreen.kt
|
|
@@ -0,0 +1,1040 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.CobblemonNetwork
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.ColourLibrary
|
|
+import com.cobblemon.mod.common.api.gui.MultiLineLabelK
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+import com.cobblemon.mod.common.api.moves.Moves
|
|
+import com.cobblemon.mod.common.api.reactive.SettableObservable
|
|
+import com.cobblemon.mod.common.api.text.bold
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.api.types.ElementalType
|
|
+import com.cobblemon.mod.common.api.types.ElementalTypes
|
|
+import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
+import com.cobblemon.mod.common.client.CobblemonClient
|
|
+import com.cobblemon.mod.common.client.CobblemonResources
|
|
+import com.cobblemon.mod.common.client.gui.interact.moveselect.MoveSlotButton
|
|
+import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
|
|
+import com.cobblemon.mod.common.client.settings.ServerSettings
|
|
+import com.cobblemon.mod.common.client.render.drawScaledText
|
|
+import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
|
+import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
|
+import com.cobblemon.mod.common.pokemon.Pokemon
|
|
+import com.cobblemon.mod.common.util.asTranslated
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.isInventoryKeyPressed
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import com.cobblemon.mod.common.util.math.toRGB
|
|
+import com.mojang.blaze3d.platform.InputConstants
|
|
+import com.mojang.blaze3d.vertex.PoseStack
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.registries.BuiltInRegistries
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.sounds.SoundEvent
|
|
+import net.minecraft.world.entity.player.Inventory
|
|
+import net.minecraft.world.inventory.ClickType
|
|
+import net.minecraft.world.inventory.Slot
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import kotlin.math.ceil
|
|
+
|
|
+class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, title: Component) : AbstractContainerScreen<TMMachineMenu>(containerMenu, inventory, title) {
|
|
+ companion object {
|
|
+ const val HEIGHT = 226
|
|
+ const val WIDTH = 191
|
|
+ const val HALF_SCALE = 0.5F
|
|
+
|
|
+ const val DISC_DIAMETER = 56
|
|
+
|
|
+ const val LOGO_WIDTH = 86
|
|
+ const val LOGO_HEIGHT = 64
|
|
+
|
|
+ const val TYPE_SELECT_MODE = 1
|
|
+ const val MOVE_SELECT_MODE = 2
|
|
+ const val TM_BURN_MODE = 3
|
|
+
|
|
+ const val CRAFT_TICKS = 10 // Length for craft completion animation
|
|
+ const val RESET_DISC_TICKS = 4 // Length for disc reset animation
|
|
+
|
|
+ const val SCREEN_SAVER_TIMEOUT_TICKS = 200 // 10 Seconds
|
|
+ const val SCREEN_SAVER_WIDTH = 118
|
|
+ const val SCREEN_SAVER_HEIGHT = 110
|
|
+
|
|
+ val baseResource = cobblemonResource("textures/gui/tmmachine/base.png")
|
|
+ val tmTray = cobblemonResource("textures/gui/tmmachine/tm_tray.png")
|
|
+ val iconArrowLeft = cobblemonResource("textures/gui/tmmachine/icon_arrow_left.png")
|
|
+ val iconArrowRight = cobblemonResource("textures/gui/tmmachine/icon_arrow_right.png")
|
|
+ val emptyDiscSlot = cobblemonResource("textures/item/tms/blank_disc_empty_slot.png")
|
|
+
|
|
+ val discBase = cobblemonResource("textures/gui/tmmachine/tm_base.png")
|
|
+ val discBorder = cobblemonResource("textures/gui/tmmachine/tm_border.png")
|
|
+ val discOverlay = cobblemonResource("textures/gui/tmmachine/tm_base_overlay.png")
|
|
+ val discReflective = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective.png")
|
|
+ val discReflectiveRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective_rotating.png")
|
|
+ val discRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_rotating.png")
|
|
+
|
|
+ val logo = cobblemonResource("textures/gui/tmmachine/logo_tm.png")
|
|
+ val screenSaver = cobblemonResource("textures/gui/tmmachine/screen_saver_background.png")
|
|
+ val screenOverlay = cobblemonResource("textures/gui/tmmachine/screen_overlay.png")
|
|
+ val scanLines = cobblemonResource("textures/gui/tmmachine/scan_lines.png")
|
|
+
|
|
+ val logoResourceMap: Map<ResourceLocation, List<String>> = mapOf(
|
|
+ cobblemonResource("textures/gui/tmmachine/logo_ct.png") to listOf("fr"),
|
|
+ cobblemonResource("textures/gui/tmmachine/logo_mt.png") to listOf("es", "it", "pt")
|
|
+ )
|
|
+
|
|
+ fun getLogoResource(langCode: String?): ResourceLocation =
|
|
+ logoResourceMap.entries.find { it.value.contains(langCode?.substringBefore("_")) }?.key ?: logo
|
|
+ }
|
|
+
|
|
+ var selectedTM: TechnicalMachine? = null
|
|
+ var heldStackMove: MoveTemplate? = null
|
|
+ var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
|
|
+ var output: ItemStack? = null
|
|
+ var mode: Int = TYPE_SELECT_MODE
|
|
+ var sortType: ElementalType? = null
|
|
+ var selectedPokemon: Pokemon? = null
|
|
+
|
|
+ var partySlotList: MutableSet<TMPartySlotWidget> = mutableSetOf()
|
|
+
|
|
+ lateinit var backButton: IconButton
|
|
+ lateinit var startButton: StartButton
|
|
+ lateinit var selectedMoveButton: MoveSlotButton
|
|
+ lateinit var typesScrollList: TypesScrollingWidget
|
|
+ lateinit var movesScrollingList: MovesScrollingWidget
|
|
+ lateinit var moveSearchWidget: MoveSearchWidget
|
|
+
|
|
+ var discReflectionFrame: Int = 0
|
|
+ var discRotationFrame: Int = 0
|
|
+
|
|
+ var ticksElapsed: Int = 0
|
|
+
|
|
+ var logoResource: ResourceLocation = logo
|
|
+ var logoTint: Triple<Double, Double, Double> = ElementalTypes.all().random().secondaryColor.toRGB()
|
|
+ var logoPosX: Int = 0
|
|
+ var logoPosY: Int = 0
|
|
+ var logoSlideRight: Boolean = true
|
|
+ var logoSlideDown: Boolean = true
|
|
+ var scanPosY: Int = 0
|
|
+ var scanLineOffsetY: Double = 0.0
|
|
+
|
|
+ var initScreen: Boolean = false
|
|
+
|
|
+ override fun init() {
|
|
+ imageWidth = WIDTH
|
|
+ imageHeight = HEIGHT
|
|
+
|
|
+ super.init()
|
|
+
|
|
+ logoResource = getLogoResource(minecraft?.languageManager?.selected)
|
|
+
|
|
+ logoPosX = leftPos + 1
|
|
+ logoPosY = topPos + 1
|
|
+ scanPosY = topPos + 1
|
|
+
|
|
+ partySlotList.clear()
|
|
+ CobblemonClient.storage.party.forEachIndexed { index, pokemon ->
|
|
+ partySlotList.add(TMPartySlotWidget(
|
|
+ pX = leftPos - 60,
|
|
+ pY = topPos + 9 + ((TMPartySlotWidget.HEIGHT + 3) * index),
|
|
+ pokemon = pokemon,
|
|
+ onPress = {
|
|
+ if ((it as TMPartySlotWidget).clickable) {
|
|
+ if (heldStackMove != null && pokemon != null) {
|
|
+ heldStackMove?.let { move ->
|
|
+ if (canLearnTMMove(move, pokemon) == TMPartySlotWidget.CAN_LEARN) {
|
|
+ CobblemonNetwork.sendToServer(TMMachineTeachMovePacket(pokemon.uuid, menu.carried, move))
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (mode == TYPE_SELECT_MODE && pokemon != null) {
|
|
+ setMoveList(pokemon = pokemon)
|
|
+ setScreenFromMode(MOVE_SELECT_MODE)
|
|
+ selectedPokemon = pokemon
|
|
+ playSound(CobblemonSounds.GUI_CLICK)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ))
|
|
+ }
|
|
+
|
|
+ for (slot in partySlotList) {
|
|
+ if (!children().contains(slot)) addRenderableWidget(slot)
|
|
+ }
|
|
+
|
|
+ typesScrollList = TypesScrollingWidget(
|
|
+ pX = leftPos + 7,
|
|
+ pY = topPos + 11,
|
|
+ setType = { type ->
|
|
+ sortType = type
|
|
+ setMoveList()
|
|
+ setScreenFromMode(MOVE_SELECT_MODE)
|
|
+ }
|
|
+ )
|
|
+ if (!children().contains(typesScrollList)) addRenderableWidget(typesScrollList)
|
|
+
|
|
+ movesScrollingList = MovesScrollingWidget(
|
|
+ pX = leftPos + 4,
|
|
+ pY = topPos + 29,
|
|
+ tmList,
|
|
+ setTM = { tm, clicked -> setSelectedTM(tm, clicked) }
|
|
+ )
|
|
+ if (!children().contains(movesScrollingList)) addRenderableWidget(movesScrollingList)
|
|
+
|
|
+ moveSearchWidget = MoveSearchWidget(pX = leftPos + 16, pY = topPos + 13) {
|
|
+ if (::movesScrollingList.isInitialized && moveSearchWidget.isFocused) {
|
|
+ val input = moveSearchWidget.value
|
|
+ setMoveList(filter = if (input.isBlank()) null else input)
|
|
+ }
|
|
+ }
|
|
+ if (!children().contains(moveSearchWidget)) addRenderableWidget(moveSearchWidget)
|
|
+
|
|
+ backButton = IconButton(
|
|
+ leftPos + 90F,
|
|
+ topPos + 1F,
|
|
+ 18,
|
|
+ 18,
|
|
+ iconArrowLeft,
|
|
+ hoverExtendRight = 11
|
|
+ ) { setScreenFromMode(TYPE_SELECT_MODE) }
|
|
+ if (!children().contains(backButton)) addRenderableWidget(backButton)
|
|
+
|
|
+ selectedMoveButton = MoveSlotButton(
|
|
+ leftPos + 6,
|
|
+ topPos + 6,
|
|
+ move = selectedTM?.moveName,
|
|
+ pp = selectedTM?.moveName?.pp ?: 0,
|
|
+ ppAsFraction = false
|
|
+ ) {
|
|
+ if (!isBurnActive()) {
|
|
+ if (::movesScrollingList.isInitialized && movesScrollingList.children().isEmpty()) setMoveList()
|
|
+ playSound(CobblemonSounds.GUI_CLICK)
|
|
+ setScreenFromMode(MOVE_SELECT_MODE)
|
|
+
|
|
+ // Clear active TM
|
|
+ CobblemonNetwork.sendToServer(SetActiveTMPacket(null))
|
|
+ }
|
|
+ }
|
|
+ if (!children().contains(selectedMoveButton)) addRenderableWidget(selectedMoveButton)
|
|
+
|
|
+ startButton = StartButton(leftPos + 5F, topPos + 35F) {
|
|
+ if (!(it as StartButton).disabled) {
|
|
+ selectedTM?.let {
|
|
+ val burnActive = menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0
|
|
+ CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
|
|
+ TMMachineBlockEntity.BURN_ACTIVE_INDEX,
|
|
+ burnActive xor 1 // Toggle active state, 1 Γçå 0
|
|
+ ))
|
|
+
|
|
+ /*// If button is not active, set if process should repeat or not
|
|
+ if (burnActive == 0) {
|
|
+ CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
|
|
+ TMMachineBlockEntity.REPEAT_PROCESS_INDEX,
|
|
+ if (hasShiftDown()) 1 else 0
|
|
+ ))
|
|
+ }*/
|
|
+
|
|
+ CobblemonNetwork.sendToServer(SetActiveTMPacket(it))
|
|
+ inventory.setChanged()
|
|
+ menu.broadcastChanges()
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!children().contains(startButton)) addRenderableWidget(startButton)
|
|
+
|
|
+ setScreenFromMode(mode)
|
|
+ initScreen = false
|
|
+ }
|
|
+
|
|
+ override fun containerTick() {
|
|
+ ticksElapsed++
|
|
+ scanPosY++
|
|
+ scanLineOffsetY = (scanLineOffsetY + 0.5) % 6
|
|
+ if (scanPosY + 1 >= (topPos + SCREEN_SAVER_HEIGHT) * 2) scanPosY = 0
|
|
+
|
|
+ if (mode == TM_BURN_MODE) {
|
|
+ selectedTM?.let { tm ->
|
|
+ val resultStack = menu.inventory!!.getItem(TMMachineMenu.RESULT_SLOT)
|
|
+ val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
|
+ .also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
|
+
|
|
+ var validOutput = resultStack.isEmpty || (
|
|
+ !resultStack.isEmpty
|
|
+ && (resultStack.count < resultStack.maxStackSize)
|
|
+ && ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)
|
|
+ )
|
|
+ var validCost = !menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty // Blank TM slot
|
|
+
|
|
+ if (validOutput && validCost) {
|
|
+ val recipes = tm.getClampedRecipe() ?: listOf()
|
|
+ recipes.forEachIndexed { index, recipe ->
|
|
+ val slotStack = menu.inventory!!.getItem(index + TMMachineMenu.INGREDIENT_SLOTS.first)
|
|
+ if (!recipe.ingredient.test(slotStack) || slotStack.count < recipe.count) {
|
|
+ validCost = false
|
|
+ return@forEachIndexed
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (::startButton.isInitialized) {
|
|
+ val discResetting = menu.getPostCraftTicks() > CRAFT_TICKS
|
|
+ val burning = isBurnActive()
|
|
+
|
|
+ startButton.disabled = !burning && (discResetting || !(validCost && validOutput))
|
|
+ startButton.processing = burning
|
|
+ startButton.shouldRepeat = false
|
|
+ }
|
|
+
|
|
+ if (::selectedMoveButton.isInitialized) {
|
|
+ selectedMoveButton.enabled = !isBurnActive()
|
|
+ }
|
|
+
|
|
+ if (isBurnActive()) {
|
|
+ val progress = menu.getBurnProgressRatio()
|
|
+ if (progress > 0 && progress < 1) {
|
|
+ discReflectionFrame = (discReflectionFrame + 1) % 4
|
|
+ discRotationFrame = (discRotationFrame + 1) % 8
|
|
+ }
|
|
+ if ((progress == 1F) && (menu.getPostCraftTicks() == 0)) playSound(CobblemonSounds.TM_MACHINE_CRAFT)
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ // Check if screen should be switched upon opening UI
|
|
+ if (!initScreen) loadBurnScreenData()
|
|
+ }
|
|
+
|
|
+ val heldStack = menu.carried
|
|
+ if (heldStack.item == CobblemonItems.TECHNICAL_MACHINE) {
|
|
+ // Store move of TM item picked up by player cursor.
|
|
+ TMMoveComponent.getTMMove(heldStack)?.let { move ->
|
|
+ if (heldStackMove != move) heldStackMove = move
|
|
+ if (mode == TYPE_SELECT_MODE) updatePartySlotStatus(heldStackMove)
|
|
+ }
|
|
+ } else if (heldStackMove != null) {
|
|
+ heldStackMove = null
|
|
+ updatePartySlotStatus(null)
|
|
+ }
|
|
+
|
|
+ // Allow dragging held TMs onto party slots in both move list and burn views.
|
|
+ when (mode) {
|
|
+ TM_BURN_MODE -> {
|
|
+ for (slot in partySlotList) {
|
|
+ slot.clickable = heldStackMove != null && (selectedTM == null || heldStackMove == selectedTM?.moveName)
|
|
+ }
|
|
+ updatePartySlotStatus(selectedTM?.moveName ?: heldStackMove)
|
|
+ }
|
|
+ MOVE_SELECT_MODE -> {
|
|
+ for (slot in partySlotList) slot.clickable = heldStackMove != null
|
|
+ updatePartySlotStatus(heldStackMove)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Screen saver
|
|
+ if (ticksElapsed > SCREEN_SAVER_TIMEOUT_TICKS) {
|
|
+ logoPosX = logoPosX + (if (logoSlideRight) 1 else -1)
|
|
+ logoPosY = logoPosY + (if (logoSlideDown) 1 else -1)
|
|
+ var borderHit = false
|
|
+
|
|
+ if (logoSlideRight && (logoPosX + (LOGO_WIDTH * HALF_SCALE).toInt()) >= (leftPos + SCREEN_SAVER_WIDTH + 1)) {
|
|
+ logoSlideRight = false
|
|
+ borderHit = true
|
|
+ }
|
|
+ if ((!logoSlideRight) && logoPosX <= (leftPos + 1)) {
|
|
+ logoSlideRight = true
|
|
+ borderHit = true
|
|
+ }
|
|
+
|
|
+ if (logoSlideDown && (logoPosY + (LOGO_HEIGHT * HALF_SCALE).toInt()) >= (topPos + SCREEN_SAVER_HEIGHT + 1)) {
|
|
+ logoSlideDown = false
|
|
+ borderHit = true
|
|
+ }
|
|
+ if ((!logoSlideDown) && logoPosY <= (topPos + 1)) {
|
|
+ logoSlideDown = true
|
|
+ borderHit = true
|
|
+ }
|
|
+
|
|
+ if (borderHit) {
|
|
+ logoTint = ElementalTypes.all().random().secondaryColor.toRGB()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ super.containerTick()
|
|
+ }
|
|
+
|
|
+ private fun resetScreenSaver(): Boolean {
|
|
+ if (ticksElapsed >= SCREEN_SAVER_TIMEOUT_TICKS) {
|
|
+ ticksElapsed = 0
|
|
+ return true
|
|
+ }
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ private fun loadBurnScreenData() {
|
|
+ var tm: TechnicalMachine? = null
|
|
+
|
|
+ val blockPos = menu.containerData?.let {
|
|
+ val x = it.get(TMMachineBlockEntity.POSITION_X_INDEX)
|
|
+ val y = it.get(TMMachineBlockEntity.POSITION_Y_INDEX)
|
|
+ val z = it.get(TMMachineBlockEntity.POSITION_Z_INDEX)
|
|
+ BlockPos(x, y, z)
|
|
+ }
|
|
+
|
|
+ inventory.player.level().getBlockEntity(blockPos)?.let { blockEntity ->
|
|
+ if (blockEntity is TMMachineBlockEntity) {
|
|
+ Moves.getByName(blockEntity.activeMove)?.let {
|
|
+ tm = TechnicalMachines.moveToTM[it]
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // If burn currently in progress or if active TM set, set to burn screen
|
|
+ if (isBurnActive() || tm != null) {
|
|
+ setSelectedTM(tm, true)
|
|
+ }
|
|
+
|
|
+ initScreen = true
|
|
+ }
|
|
+
|
|
+ private fun isBurnActive(): Boolean = (menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0) == 1
|
|
+
|
|
+ private fun setScreenFromMode(screen: Int) {
|
|
+ mode = screen
|
|
+ when (mode) {
|
|
+ TYPE_SELECT_MODE -> {
|
|
+ toggleTMBurn(false)
|
|
+ toggleMoveSelect(false)
|
|
+ toggleTypeSelect(true)
|
|
+ }
|
|
+ MOVE_SELECT_MODE -> {
|
|
+ toggleTMBurn(false)
|
|
+ toggleTypeSelect(false)
|
|
+ toggleMoveSelect(true)
|
|
+ }
|
|
+ else -> {
|
|
+ toggleTypeSelect(false)
|
|
+ toggleMoveSelect(false)
|
|
+ toggleTMBurn(true)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private fun setMoveList(filter: String? = null, pokemon: Pokemon? = null) {
|
|
+ val filteredList = TechnicalMachine.filterTms(
|
|
+ search = filter,
|
|
+ type = sortType,
|
|
+ pokemon = pokemon,
|
|
+ player = inventory.player,
|
|
+ includeUnlearned = ServerSettings.unlockAllMoveDexMovesByDefault
|
|
+ ).toMutableList()
|
|
+ if (sortType == null) filteredList.sortBy { it.type }
|
|
+ tmList.set(filteredList)
|
|
+ }
|
|
+
|
|
+ private fun toggleTypeSelect(isVisible: Boolean) {
|
|
+ if (isVisible) {
|
|
+ setSelectedTM(null, false)
|
|
+ if (::moveSearchWidget.isInitialized) moveSearchWidget.value = ""
|
|
+ if (selectedPokemon != null) selectedPokemon = null
|
|
+ sortType = null
|
|
+ }
|
|
+
|
|
+ if (::typesScrollList.isInitialized) {
|
|
+ typesScrollList.apply {
|
|
+ visible = isVisible
|
|
+ scrollAmount = 0.0
|
|
+ // Resize widget to toggle input detection as screen only detects first widget added for overlapping children
|
|
+ if (isVisible) { setSize(TypesScrollingWidget.WIDTH, TypesScrollingWidget.HEIGHT) }
|
|
+ else { setSize(0, 0) }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (slot in partySlotList) slot.clickable = isVisible
|
|
+ }
|
|
+
|
|
+ private fun toggleMoveSelect(isVisible: Boolean) {
|
|
+ if (::movesScrollingList.isInitialized) {
|
|
+ movesScrollingList.apply {
|
|
+ visible = isVisible
|
|
+ scrollAmount = 0.0
|
|
+ if (isVisible) { setSize(MovesScrollingWidget.WIDTH, MovesScrollingWidget.HEIGHT) }
|
|
+ else { setSize(0, 0) }
|
|
+ }
|
|
+ }
|
|
+ if (::moveSearchWidget.isInitialized) {
|
|
+ moveSearchWidget.visible = isVisible
|
|
+ }
|
|
+ if (::backButton.isInitialized) {
|
|
+ backButton.visible = isVisible
|
|
+ }
|
|
+ heldStackMove = null
|
|
+ }
|
|
+
|
|
+ private fun toggleTMBurn(isVisible: Boolean) {
|
|
+ if (!isVisible) setSelectedTM(null, false)
|
|
+ if (::selectedMoveButton.isInitialized) selectedMoveButton.visible = isVisible
|
|
+ if (::startButton.isInitialized) startButton.visible = isVisible
|
|
+ }
|
|
+
|
|
+ fun canLearnTMMove(move: MoveTemplate, pokemon: Pokemon): Int {
|
|
+ if (pokemon.moveSet.getMoveTemplates().contains(move) || pokemon.allAccessibleMoves.contains(move)) return TMPartySlotWidget.LEARNED
|
|
+ val learnableMoves = pokemon.form.moves.tmLearnableMoves()
|
|
+ return if (learnableMoves.contains(move)) TMPartySlotWidget.CAN_LEARN else TMPartySlotWidget.CANNOT_LEARN
|
|
+ }
|
|
+
|
|
+ fun setSelectedTM(tm: TechnicalMachine?, clicked: Boolean) {
|
|
+ if (selectedTM != tm) {
|
|
+ selectedTM = tm
|
|
+ updatePartySlotStatus(tm?.moveName)
|
|
+ }
|
|
+
|
|
+ if (::movesScrollingList.isInitialized) {
|
|
+ movesScrollingList.setSlotHighlighted(if (clicked) null else selectedTM?.id)
|
|
+ }
|
|
+
|
|
+ if (clicked) {
|
|
+ CobblemonNetwork.sendToServer(SetActiveTMPacket(tm))
|
|
+
|
|
+ selectedTM?.let {
|
|
+ if (::selectedMoveButton.isInitialized) {
|
|
+ selectedMoveButton.apply {
|
|
+ move = it.moveName
|
|
+ pp = it.moveName.pp
|
|
+ }
|
|
+ setScreenFromMode(TM_BURN_MODE)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun updatePartySlotStatus(moveTemplate: MoveTemplate?) {
|
|
+ for (slot in partySlotList) {
|
|
+ if (slot.pokemon != null) {
|
|
+ slot.teachable = if (moveTemplate != null) canLearnTMMove(moveTemplate, slot.pokemon) else null
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun renderDiscTypeOverlay(poseStack: PoseStack, isDiscRotating: Boolean, alpha: Float = 1F) {
|
|
+ selectedTM?.let {
|
|
+ val primaryRgb = it.moveName.elementalType.primaryColor.toRGB()
|
|
+ val secondaryRgb = it.moveName.elementalType.secondaryColor.toRGB()
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discBase,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ red = primaryRgb.first,
|
|
+ green = primaryRgb.second,
|
|
+ blue = primaryRgb.third,
|
|
+ alpha = alpha
|
|
+ )
|
|
+
|
|
+ if (isDiscRotating) {
|
|
+ // Reflective rotation
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discReflectiveRotating,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ vOffset = DISC_DIAMETER * discReflectionFrame,
|
|
+ textureHeight = DISC_DIAMETER * 4,
|
|
+ red = secondaryRgb.first,
|
|
+ green = secondaryRgb.second,
|
|
+ blue = secondaryRgb.third,
|
|
+ alpha = alpha
|
|
+ )
|
|
+
|
|
+ // Groove rotation
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discRotating,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ vOffset = DISC_DIAMETER * discRotationFrame,
|
|
+ textureHeight = DISC_DIAMETER * 8,
|
|
+ red = secondaryRgb.first,
|
|
+ green = secondaryRgb.second,
|
|
+ blue = secondaryRgb.third,
|
|
+ alpha = alpha
|
|
+ )
|
|
+ } else {
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discReflective,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ red = secondaryRgb.first,
|
|
+ green = secondaryRgb.second,
|
|
+ blue = secondaryRgb.third,
|
|
+ alpha = alpha
|
|
+ )
|
|
+ }
|
|
+
|
|
+ val postCraftTicks = menu.getPostCraftTicks()
|
|
+ if (postCraftTicks > 0 && postCraftTicks < 8) {
|
|
+ val opacity = when {
|
|
+ postCraftTicks <= 5 -> postCraftTicks * 0.25F
|
|
+ postCraftTicks > 5 -> (CRAFT_TICKS - postCraftTicks) * 0.25F // decreases by 0.25 each tick after 5
|
|
+ else -> 0F
|
|
+ }
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discOverlay,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ alpha = opacity
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun renderDisc(poseStack: PoseStack, isDiscRotating: Boolean, baseAlpha: Float = 1F, overlayAlpha: Float = 1F) {
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discBase,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ alpha = baseAlpha
|
|
+ )
|
|
+
|
|
+ if (isDiscRotating) {
|
|
+ // Reflective rotation
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discReflectiveRotating,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ vOffset = DISC_DIAMETER * discReflectionFrame,
|
|
+ textureHeight = DISC_DIAMETER * 4,
|
|
+ alpha = baseAlpha
|
|
+ )
|
|
+
|
|
+ // Groove rotation
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discRotating,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ vOffset = DISC_DIAMETER * discRotationFrame,
|
|
+ textureHeight = DISC_DIAMETER * 8,
|
|
+ alpha = baseAlpha
|
|
+ )
|
|
+ } else {
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discReflective,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ alpha = baseAlpha
|
|
+ )
|
|
+ }
|
|
+
|
|
+ if (menu.getPostCraftTicks() < CRAFT_TICKS) {
|
|
+ renderDiscTypeOverlay(poseStack, isDiscRotating, overlayAlpha)
|
|
+ }
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = discBorder,
|
|
+ x = leftPos + 32,
|
|
+ y = topPos + 50,
|
|
+ width = DISC_DIAMETER,
|
|
+ height = DISC_DIAMETER,
|
|
+ alpha = baseAlpha
|
|
+ )
|
|
+ }
|
|
+
|
|
+ fun renderMoveInfo(context: GuiGraphics, mouseX: Int, mouseY: Int) {
|
|
+ val currentTm = selectedTM
|
|
+ val moveTemplate = currentTm?.moveName
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = MovesWidget.movesPowerIconResource,
|
|
+ x = leftPos + 127.5,
|
|
+ y = topPos + 7.5,
|
|
+ width = 5,
|
|
+ height = 5
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = MovesWidget.movesAccuracyIconResource,
|
|
+ x = leftPos + 127.5,
|
|
+ y = topPos + 19.5,
|
|
+ width = 5,
|
|
+ height = 5
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = context.pose(),
|
|
+ texture = MovesWidget.movesEffectIconResource,
|
|
+ x = leftPos + 127.5,
|
|
+ y = topPos + 31.5,
|
|
+ width = 5,
|
|
+ height = 5
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = lang("ui.power"),
|
|
+ x = leftPos + 134.5,
|
|
+ y = topPos + 8,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = lang("ui.accuracy"),
|
|
+ x = leftPos + 134.5,
|
|
+ y = topPos + 20,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = lang("ui.effect"),
|
|
+ x = leftPos + 134.5,
|
|
+ y = topPos + 32,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ val movePower = if (moveTemplate != null && moveTemplate.power.toInt() > 0) moveTemplate.power.toInt().toString().text() else "ΓÇö".text()
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = movePower,
|
|
+ x = leftPos + 183,
|
|
+ y = topPos + 8,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ val moveAccuracy = if (moveTemplate != null) MovesWidget.format(moveTemplate.accuracy).text() else "ΓÇö".text()
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = moveAccuracy,
|
|
+ x = leftPos + 183,
|
|
+ y = topPos + 20,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ val moveEffect = if (moveTemplate != null) MovesWidget.format(moveTemplate.effectChances.firstOrNull() ?: 0.0).text() else "ΓÇö".text()
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = moveEffect,
|
|
+ x = leftPos + 183,
|
|
+ y = topPos + 32,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ if (moveTemplate != null) {
|
|
+ context.pose().pushPose()
|
|
+ context.pose().scale(HALF_SCALE, HALF_SCALE, 1F)
|
|
+ MultiLineLabelK.create(
|
|
+ component = moveTemplate.description,
|
|
+ width = 55 / HALF_SCALE,
|
|
+ maxLines = 5
|
|
+ ).renderLeftAligned(
|
|
+ context = context,
|
|
+ x = (leftPos + 127.5) / HALF_SCALE,
|
|
+ y = (topPos + 44.5) / HALF_SCALE,
|
|
+ ySpacing = 6 / HALF_SCALE,
|
|
+ colour = ColourLibrary.WHITE,
|
|
+ shadow = true
|
|
+ )
|
|
+ context.pose().popPose()
|
|
+
|
|
+ val recipe = selectedTM?.getClampedRecipe() ?: listOf()
|
|
+ // Render material cost
|
|
+ recipe.forEachIndexed { index, recipe ->
|
|
+ val itemX = (leftPos + 129 + (index * 18))
|
|
+ val itemY = (topPos + 90)
|
|
+
|
|
+ val stacks = recipe.ingredient.items
|
|
+ if (stacks.isEmpty()) return@forEachIndexed
|
|
+
|
|
+ val level = Minecraft.getInstance().level ?: return@forEachIndexed
|
|
+ val index = ((level.gameTime / 20) % stacks.size).toInt()
|
|
+
|
|
+ val itemStack = stacks[index].copy().also { it.count = recipe.count }
|
|
+
|
|
+ context.renderItem(itemStack, itemX, itemY)
|
|
+ context.renderItemDecorations(Minecraft.getInstance().font, itemStack, itemX, itemY)
|
|
+
|
|
+ if (
|
|
+ mouseX >= itemX && mouseX < itemX + 16 &&
|
|
+ mouseY >= itemY && mouseY < itemY + 16
|
|
+ ) {
|
|
+ context.renderTooltip(Minecraft.getInstance().font, itemStack, mouseX, mouseY)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fun renderScreenSaver(context: GuiGraphics) {
|
|
+ val poseStack = context.pose()
|
|
+ // Screen saver
|
|
+ if (ticksElapsed > SCREEN_SAVER_TIMEOUT_TICKS) {
|
|
+ val opacity = Math.max(0.0, Math.min(1.0, (ticksElapsed - SCREEN_SAVER_TIMEOUT_TICKS) * 0.2))
|
|
+ poseStack.pushPose()
|
|
+ poseStack.translate(0.0, 0.0, 100.0)
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = screenSaver,
|
|
+ x = leftPos + 1,
|
|
+ y = topPos + 1,
|
|
+ width = SCREEN_SAVER_WIDTH,
|
|
+ height = SCREEN_SAVER_HEIGHT,
|
|
+ alpha = opacity
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = logoResource,
|
|
+ x = logoPosX / HALF_SCALE,
|
|
+ y = logoPosY / HALF_SCALE,
|
|
+ width = LOGO_WIDTH,
|
|
+ height = LOGO_HEIGHT,
|
|
+ scale = HALF_SCALE,
|
|
+ red = logoTint.first,
|
|
+ green = logoTint.second,
|
|
+ blue = logoTint.third,
|
|
+ alpha = 0.8 * opacity
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = cobblemonResource("textures/white.png"),
|
|
+ x = leftPos + 1,
|
|
+ y = scanPosY * 0.5,
|
|
+ width = SCREEN_SAVER_WIDTH,
|
|
+ height = 1,
|
|
+ red = 0,
|
|
+ green = 0,
|
|
+ blue = 0,
|
|
+ alpha = 0.1 * opacity
|
|
+ )
|
|
+
|
|
+ context.enableScissor(
|
|
+ leftPos + 1,
|
|
+ topPos + 1,
|
|
+ leftPos + 1 + SCREEN_SAVER_WIDTH,
|
|
+ topPos + 1 + SCREEN_SAVER_HEIGHT
|
|
+ )
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = scanLines,
|
|
+ x = leftPos + 1,
|
|
+ y = topPos + 1 - scanLineOffsetY,
|
|
+ width = 118,
|
|
+ height = 114,
|
|
+ alpha = opacity
|
|
+ )
|
|
+ context.disableScissor()
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = poseStack,
|
|
+ texture = screenOverlay,
|
|
+ x = leftPos + 1,
|
|
+ y = topPos + 1,
|
|
+ width = SCREEN_SAVER_WIDTH,
|
|
+ height = SCREEN_SAVER_HEIGHT,
|
|
+ alpha = 0.5 * opacity
|
|
+ )
|
|
+
|
|
+ poseStack.popPose()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun mouseMoved(mouseX: Double, mouseY: Double) {
|
|
+ ticksElapsed = 0
|
|
+ super.mouseMoved(mouseX, mouseY)
|
|
+ }
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ if (resetScreenSaver()) return false
|
|
+ return super.mouseClicked(mouseX, mouseY, button)
|
|
+ }
|
|
+
|
|
+ override fun slotClicked(slot: Slot?, slotId: Int, mouseButton: Int, type: ClickType) {
|
|
+ super.slotClicked(slot, slotId, mouseButton, type)
|
|
+ if (slot != null && slotId in 0 until Inventory.INVENTORY_SIZE && selectedTM != null) {
|
|
+ setSelectedTM(null, false)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
|
|
+ if (resetScreenSaver()) return false
|
|
+ return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY)
|
|
+ }
|
|
+
|
|
+ override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
|
+ if (resetScreenSaver()) return false
|
|
+ val searchFocused = this::moveSearchWidget.isInitialized && moveSearchWidget.isFocused
|
|
+
|
|
+ if (isInventoryKeyPressed(minecraft, keyCode, scanCode)) {
|
|
+ if (searchFocused) { return true }
|
|
+ else {
|
|
+ playSound(CobblemonSounds.TM_MACHINE_OFF)
|
|
+ Minecraft.getInstance().setScreen(null)
|
|
+ return true
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (keyCode == InputConstants.KEY_ESCAPE) {
|
|
+ if (searchFocused) {
|
|
+ // Escape from text box
|
|
+ this.focused = null
|
|
+ return true
|
|
+ } else {
|
|
+ playSound(CobblemonSounds.TM_MACHINE_OFF)
|
|
+ Minecraft.getInstance().setScreen(null)
|
|
+ return true
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return super.keyPressed(keyCode, scanCode, modifiers)
|
|
+ }
|
|
+
|
|
+ override fun renderBackground(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {}
|
|
+
|
|
+ override fun renderBg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
|
|
+
|
|
+ override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ super.renderTransparentBackground(graphics)
|
|
+
|
|
+ val matrices = graphics.pose()
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = baseResource,
|
|
+ x = leftPos,
|
|
+ y = topPos,
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT
|
|
+ )
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = emptyDiscSlot,
|
|
+ x = leftPos + 105,
|
|
+ y = topPos + 116,
|
|
+ width = 16,
|
|
+ height = 16
|
|
+ )
|
|
+
|
|
+ var label = if (mode != MOVE_SELECT_MODE) "block.cobblemon.tm_machine".asTranslated()
|
|
+ else ( if (selectedPokemon != null) selectedPokemon!!.getDisplayName() else (if (sortType != null) lang("type.suffix", sortType!!.displayName) else lang("ui.moves")))
|
|
+ drawScaledText(
|
|
+ context = graphics,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = label.bold(),
|
|
+ x = leftPos + 5,
|
|
+ y = topPos + 1
|
|
+ )
|
|
+
|
|
+ if (mode == MOVE_SELECT_MODE && ::backButton.isInitialized) {
|
|
+ drawScaledText(
|
|
+ context = graphics,
|
|
+ font = CobblemonResources.DEFAULT_LARGE,
|
|
+ text = lang("ui.back").bold(),
|
|
+ x = backButton.x + backButton.width + - 8.0,
|
|
+ y = backButton.y,
|
|
+ shadow = true
|
|
+ )
|
|
+ }
|
|
+
|
|
+ if (mode == TM_BURN_MODE) {
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = tmTray,
|
|
+ x = leftPos,
|
|
+ y = topPos,
|
|
+ width = 120,
|
|
+ height = 112
|
|
+ )
|
|
+
|
|
+ val burnProgressWidth = ceil(menu.getBurnProgressRatio() * 81).toInt()
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = CobblemonResources.WHITE,
|
|
+ x = leftPos + 33,
|
|
+ y = topPos + 36,
|
|
+ width = burnProgressWidth,
|
|
+ height = 2
|
|
+ )
|
|
+
|
|
+ if (!menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty) {
|
|
+ val postCraftTicks = menu.getPostCraftTicks()
|
|
+ val baseOpacity = when {
|
|
+ ((postCraftTicks >= (CRAFT_TICKS + 1)) && (postCraftTicks <= (CRAFT_TICKS + RESET_DISC_TICKS))) -> (postCraftTicks - (CRAFT_TICKS + 1)) * 0.25F
|
|
+ postCraftTicks == (CRAFT_TICKS) -> 0F
|
|
+ else -> 1F
|
|
+ }
|
|
+
|
|
+ val overlayOpacity = if (postCraftTicks < CRAFT_TICKS) menu.getBurnProgressRatio() else 0F
|
|
+ renderDisc(matrices, (menu.getBurnProgressRatio() < 1F) && isBurnActive(), baseOpacity, overlayOpacity)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ renderScreenSaver(graphics)
|
|
+
|
|
+ super.render(graphics, mouseX, mouseY, delta)
|
|
+
|
|
+ renderMoveInfo(graphics, mouseX, mouseY)
|
|
+
|
|
+ this.renderTooltip(graphics, mouseX, mouseY)
|
|
+ }
|
|
+
|
|
+ override fun renderBlurredBackground(partialTick: Float) {}
|
|
+ override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {}
|
|
+
|
|
+ fun playSound(soundEvent: SoundEvent) {
|
|
+ Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMPartySlotWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMPartySlotWidget.kt
|
|
new file mode 100644
|
|
index 0000000000..a56cf62c7a
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TMPartySlotWidget.kt
|
|
@@ -0,0 +1,187 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
|
+import com.cobblemon.mod.common.client.render.drawScaledText
|
|
+import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
|
+import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
|
+import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
|
+import com.cobblemon.mod.common.pokemon.Gender
|
|
+import com.cobblemon.mod.common.pokemon.Pokemon
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
|
+import net.minecraft.client.gui.components.Button
|
|
+import net.minecraft.client.sounds.SoundManager
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.network.chat.MutableComponent
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import org.joml.Quaternionf
|
|
+import org.joml.Vector3f
|
|
+
|
|
+class TMPartySlotWidget(
|
|
+ pX: Number,
|
|
+ pY: Number,
|
|
+ val pokemon: Pokemon?,
|
|
+ onPress: OnPress
|
|
+) : Button(pX.toInt(), pY.toInt(), WIDTH, HEIGHT, Component.literal("PartyMember"), onPress, DEFAULT_NARRATION) {
|
|
+ val state = FloatingState()
|
|
+ var teachable: Int? = null
|
|
+ var clickable: Boolean = true
|
|
+
|
|
+ companion object {
|
|
+ const val WIDTH = 57
|
|
+ const val HEIGHT = 31
|
|
+ private const val PORTRAIT_DIAMETER = 30
|
|
+ const val HALF_SCALE = 0.5F
|
|
+
|
|
+ const val CAN_LEARN = 0
|
|
+ const val CANNOT_LEARN = 1
|
|
+ const val LEARNED = 2
|
|
+
|
|
+ private val slotResource = cobblemonResource("textures/gui/tmmachine/party_slot.png")
|
|
+ private val slotLearnableResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable.png")
|
|
+ private val slotLearnableDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable_disabled.png")
|
|
+ private val slotDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_disabled.png")
|
|
+ private val slotLearnedResource = cobblemonResource("textures/gui/tmmachine/party_slot_learned.png")
|
|
+ val genderIconMale = cobblemonResource("textures/gui/pc/gender_icon_male.png")
|
|
+ val genderIconFemale = cobblemonResource("textures/gui/pc/gender_icon_female.png")
|
|
+
|
|
+ private val canLearnLabel = lang("ui.tm_machine.can_learn")
|
|
+ private val cannotLearnLabel = lang("ui.tm_machine.cannot_learn")
|
|
+ private val learnedLabel = lang("ui.tm_machine.learned")
|
|
+ }
|
|
+
|
|
+ private fun getResource(): ResourceLocation = when (teachable) {
|
|
+ CAN_LEARN -> if (clickable) slotLearnableResource else slotLearnableDisabledResource
|
|
+ CANNOT_LEARN -> slotDisabledResource
|
|
+ LEARNED -> slotLearnedResource
|
|
+ else -> slotResource
|
|
+ }
|
|
+
|
|
+ private fun getLabel(): MutableComponent? = when (teachable) {
|
|
+ CAN_LEARN -> canLearnLabel
|
|
+ CANNOT_LEARN -> cannotLearnLabel
|
|
+ LEARNED -> learnedLabel
|
|
+ else -> null
|
|
+ }
|
|
+
|
|
+ override fun renderWidget(context: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ state.currentAspects = pokemon?.aspects ?: emptySet()
|
|
+ val matrices = context.pose()
|
|
+
|
|
+ if (pokemon != null) {
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = getResource(),
|
|
+ x = x,
|
|
+ y = y,
|
|
+ width = width,
|
|
+ height = height,
|
|
+ vOffset = if (clickable && isMouseOver(mouseX.toDouble(), mouseY.toDouble())) height else 0,
|
|
+ textureHeight = height * 2,
|
|
+ )
|
|
+
|
|
+ val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
|
|
+ val ballHeight = 22
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = ballIcon,
|
|
+ x = (x - 2) / HALF_SCALE,
|
|
+ y = (y - 3) / HALF_SCALE,
|
|
+ height = ballHeight,
|
|
+ width = 18,
|
|
+ textureHeight = ballHeight * 2,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ // Render Pokémon
|
|
+ matrices.pushPose()
|
|
+ matrices.translate(x + (PORTRAIT_DIAMETER / 2.0) - 2, y - 1.0, 0.0)
|
|
+ matrices.scale(2.5F, 2.5F, 1F)
|
|
+ drawProfilePokemon(
|
|
+ species = pokemon.species.resourceIdentifier,
|
|
+ matrixStack = matrices,
|
|
+ rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
|
+ state = state,
|
|
+ scale = 4.5F,
|
|
+ partialTicks = delta
|
|
+ )
|
|
+ matrices.popPose()
|
|
+
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = lang("ui.lv.number", pokemon.level),
|
|
+ x = x + 54,
|
|
+ y = y + 13,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ if (pokemon.gender != Gender.GENDERLESS) {
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
|
|
+ x = (x + 51) / HALF_SCALE,
|
|
+ y = (y + 19) / HALF_SCALE,
|
|
+ height = 8,
|
|
+ width = 6,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+ }
|
|
+
|
|
+ drawScaledTextJustifiedRight(
|
|
+ context = context,
|
|
+ text = pokemon.getDisplayName(),
|
|
+ x = x + ( if (pokemon.gender != Gender.GENDERLESS) 50 else 54),
|
|
+ y = y + 19,
|
|
+ shadow = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+
|
|
+ getLabel()?.let { label ->
|
|
+ drawScaledText(
|
|
+ context = context,
|
|
+ text = label,
|
|
+ x = x + (width / 2F),
|
|
+ y = y + 26,
|
|
+ centered = true,
|
|
+ scale = HALF_SCALE
|
|
+ )
|
|
+ }
|
|
+
|
|
+ // Held Item
|
|
+ val heldItem = pokemon.heldItemNoCopy()
|
|
+ if (!heldItem.isEmpty) {
|
|
+ renderScaledGuiItemIcon(
|
|
+ itemStack = heldItem,
|
|
+ x = x + 15.0,
|
|
+ y = y + 13.5,
|
|
+ scale = HALF_SCALE.toDouble(),
|
|
+ matrixStack = matrices
|
|
+ )
|
|
+ }
|
|
+ } else {
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = slotDisabledResource,
|
|
+ x = x + 55,
|
|
+ y = y,
|
|
+ width = 5,
|
|
+ height = height,
|
|
+ textureWidth = width,
|
|
+ textureHeight = height * 2
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun playDownSound(soundManager: SoundManager) {}
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TypesScrollingWidget.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TypesScrollingWidget.kt
|
|
new file mode 100644
|
|
index 0000000000..4e43249bac
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/tmmachine/TypesScrollingWidget.kt
|
|
@@ -0,0 +1,151 @@
|
|
+/*
|
|
+ * 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.tmmachine
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.gui.blitk
|
|
+import com.cobblemon.mod.common.api.text.text
|
|
+import com.cobblemon.mod.common.api.types.ElementalType
|
|
+import com.cobblemon.mod.common.api.types.ElementalTypes
|
|
+import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
|
+import com.cobblemon.mod.common.client.gui.TypeIcon
|
|
+import com.cobblemon.mod.common.client.gui.tmmachine.TypesScrollingWidget.ScrollSlotRow
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.client.Minecraft
|
|
+import net.minecraft.client.gui.GuiGraphics
|
|
+import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.util.FastColor
|
|
+import net.minecraft.util.Mth
|
|
+
|
|
+class TypesScrollingWidget(val pX: Int, val pY: Int, val setType: (ElementalType?) -> (Unit)): ScrollingWidget<ScrollSlotRow>(
|
|
+ width = WIDTH,
|
|
+ height = HEIGHT,
|
|
+ left = pX,
|
|
+ top = pY - HEIGHT,
|
|
+ slotHeight = SLOT_SIZE + SLOT_SPACING
|
|
+) {
|
|
+ companion object {
|
|
+ const val WIDTH = 112
|
|
+ const val HEIGHT = 100
|
|
+ const val SLOT_SIZE = 22
|
|
+ const val SLOT_SPACING = 5
|
|
+ }
|
|
+
|
|
+ init {
|
|
+ (listOf(null) + ElementalTypes.all()).chunked(4).forEachIndexed { index, listChunk ->
|
|
+ addEntry(ScrollSlotRow(listChunk, setType))
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun getScrollbarPosition(): Int = rowLeft + width - 3
|
|
+
|
|
+ override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val xLeft = this.scrollbarPosition
|
|
+ val xRight = xLeft + 3
|
|
+ val barHeight = this.bottom - this.y
|
|
+
|
|
+ var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
|
+ yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
|
|
+ var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
|
|
+ if (yTop < this.y) {
|
|
+ yTop = this.y
|
|
+ }
|
|
+
|
|
+ context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
|
|
+ context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
|
|
+ }
|
|
+
|
|
+ fun renderEntry(context: GuiGraphics, mouseX: Int, mouseY: Int, index: Int, x: Int, y: Int) {
|
|
+ val entry = this.getEntry(index)
|
|
+ entry.x = x
|
|
+ entry.y = y
|
|
+ entry.renderRow( context, y, x, mouseX, mouseY)
|
|
+ }
|
|
+
|
|
+ override fun getEntry(index: Int): ScrollSlotRow = children()[index] as ScrollSlotRow
|
|
+
|
|
+ override fun renderListItems(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
|
+ val rowX = rowLeft
|
|
+
|
|
+ for (index in 0 until this.itemCount) {
|
|
+ val rowY = this.getRowTop(index)
|
|
+ val rowBottom = this.getRowBottom(index)
|
|
+ if (rowBottom >= this.y && rowY <= this.bottom) {
|
|
+ this.renderEntry(context!!, mouseX, mouseY, index, rowX, rowY)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ class ScrollSlotRow(val types: List<ElementalType?>, val setType : (ElementalType?) -> Unit): Slot<ScrollSlotRow>() {
|
|
+ companion object {
|
|
+ private val allSlotResource = cobblemonResource("textures/gui/tmmachine/type_slot_all.png")
|
|
+ private val slotResource = cobblemonResource("textures/gui/tmmachine/type_slot.png")
|
|
+ }
|
|
+
|
|
+ var x: Int = 0
|
|
+ var y: Int = 0
|
|
+
|
|
+ fun renderRow(context: GuiGraphics, y: Int, x: Int, mouseX: Int, mouseY: Int) {
|
|
+ types.forEachIndexed { index, type ->
|
|
+ val matrices = context.pose()
|
|
+
|
|
+ val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
|
|
+ val startPosY = y + SLOT_SPACING
|
|
+
|
|
+ val slotHovered = getHoveredSlotIndex(mouseX, mouseY) == index
|
|
+
|
|
+ blitk(
|
|
+ matrixStack = matrices,
|
|
+ texture = if (type != null) slotResource else allSlotResource,
|
|
+ x = startPosX,
|
|
+ y = startPosY,
|
|
+ width = SLOT_SIZE,
|
|
+ height = SLOT_SIZE,
|
|
+ vOffset = if (slotHovered) SLOT_SIZE else 0,
|
|
+ textureHeight = SLOT_SIZE * 2
|
|
+ )
|
|
+
|
|
+ if (type != null) {
|
|
+ TypeIcon(startPosX + 2, startPosY + 2, type).render(context)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {}
|
|
+
|
|
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
+ val hoverIndex = getHoveredSlotIndex(mouseX.toInt(), mouseY.toInt())
|
|
+ if (hoverIndex > -1 && hoverIndex < types.size) {
|
|
+ setType.invoke(types[hoverIndex])
|
|
+ Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
|
+ }
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ private fun getHoveredSlotIndex(mouseX: Int, mouseY: Int): Int {
|
|
+ types.forEachIndexed { index, _ ->
|
|
+ val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
|
|
+ val startPosY = y + SLOT_SPACING + 1
|
|
+
|
|
+ if (mouseX in startPosX..(startPosX + SLOT_SIZE) && mouseY in startPosY..(startPosY + SLOT_SIZE)) {
|
|
+ return index
|
|
+ }
|
|
+ }
|
|
+ return -1
|
|
+ }
|
|
+
|
|
+ override fun getNarration(): Component {
|
|
+ if (types.isNotEmpty()) {
|
|
+ return "${types[0]}-${types[types.size - 1]}".text()
|
|
+ }
|
|
+ return "".text()
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/CobblemonToast.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/CobblemonToast.kt
|
|
index f197d20c94..b6f8723abb 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/CobblemonToast.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/CobblemonToast.kt
|
|
@@ -26,16 +26,27 @@ import net.minecraft.util.FormattedCharSequence
|
|
@Environment(EnvType.CLIENT)
|
|
class CobblemonToast(
|
|
val id: UUID,
|
|
- var icon: ItemStack,
|
|
+ var icons: List<ItemStack>,
|
|
var title: Component,
|
|
var description: Component,
|
|
var frameTexture: ResourceLocation,
|
|
var progress: Float,
|
|
- var progressColor: Int
|
|
+ var progressColor: Int,
|
|
+ var durationMs: Long? = null
|
|
) : Toast {
|
|
|
|
- constructor(packet: ToastPacket) : this(packet.uuid, packet.icon, packet.title, packet.description, packet.frameTexture, packet.progress, packet.progressColor)
|
|
+ constructor(packet: ToastPacket) : this(
|
|
+ packet.uuid,
|
|
+ packet.icons,
|
|
+ packet.title,
|
|
+ packet.description,
|
|
+ packet.frameTexture,
|
|
+ packet.progress,
|
|
+ packet.progressColor,
|
|
+ packet.durationMs
|
|
+ )
|
|
|
|
+ private var startTime: Long = -1
|
|
private var lastProgress = 0F
|
|
private var lastTime = 0L
|
|
internal var nextVisibility: Toast.Visibility = Toast.Visibility.SHOW
|
|
@@ -45,6 +56,17 @@ class CobblemonToast(
|
|
}
|
|
|
|
override fun render(context: GuiGraphics, manager: ToastComponent, startTime: Long): Toast.Visibility {
|
|
+ if (this.startTime == -1L) this.startTime = startTime
|
|
+
|
|
+ val elapsedTime = startTime - this.startTime
|
|
+ val localDuration = this.durationMs
|
|
+ if (localDuration != null && elapsedTime >= localDuration) {
|
|
+ ToastTracker.remove(this)
|
|
+ return Toast.Visibility.HIDE
|
|
+ }
|
|
+
|
|
+ context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height())
|
|
+
|
|
val maxWidth = 125
|
|
val textRenderer = manager.minecraft.font
|
|
|
|
@@ -52,19 +74,38 @@ class CobblemonToast(
|
|
|
|
context.drawString(textRenderer, this.title, 30, 7, this.title.style.color?.value ?: -1, false)
|
|
context.drawWordWrap(textRenderer, this.description, 30, 18, maxWidth, this.description.style.color?.value ?: -1)
|
|
- context.renderFakeItem(this.icon, 8, 8)
|
|
+
|
|
+ val iconIndex = if (localDuration != null && localDuration > 0) {
|
|
+ val repeatsPerIcon = 2
|
|
+ val totalIcons = icons.size * repeatsPerIcon
|
|
+ val timePerIcon = (localDuration / totalIcons).coerceAtLeast(1L)
|
|
+
|
|
+ val iconIndex = (elapsedTime / timePerIcon)
|
|
+ .toInt()
|
|
+ .coerceIn(0, totalIcons - 1)
|
|
+
|
|
+ iconIndex / repeatsPerIcon
|
|
+ } else {
|
|
+ val switchIntervalMs = 500L
|
|
+ ((elapsedTime / switchIntervalMs) % icons.size).toInt()
|
|
+ }
|
|
+
|
|
+ val icon = this.icons[iconIndex]
|
|
+ context.renderFakeItem(icon, 8, 8)
|
|
if (this.hasProgressBar()) {
|
|
context.fill(3, 28, 157, 29, -1)
|
|
val f = Mth.clampedLerp(this.lastProgress, this.progress, (startTime - this.lastTime).toFloat() / 100F)
|
|
context.fill(3, 28, (3F + 154F * f).toInt(), 29, this.progressColor)
|
|
this.lastProgress = f
|
|
}
|
|
+
|
|
this.lastTime = startTime
|
|
return this.nextVisibility
|
|
}
|
|
|
|
internal fun updateFrom(packet: ToastPacket) {
|
|
- this.icon = packet.icon
|
|
+ val newIcons = this.icons + packet.icons
|
|
+ this.icons = newIcons
|
|
this.title = packet.title
|
|
this.description = packet.description
|
|
this.frameTexture = packet.frameTexture
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/ToastTracker.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/ToastTracker.kt
|
|
index 4c178b4c8d..0faf912f86 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/ToastTracker.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/gui/toast/ToastTracker.kt
|
|
@@ -30,4 +30,7 @@ object ToastTracker {
|
|
}
|
|
}
|
|
|
|
+ fun remove(toast: CobblemonToast) {
|
|
+ toasts.remove(toast.id)
|
|
+ }
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/OpenTMMPacketHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/OpenTMMPacketHandler.kt
|
|
new file mode 100644
|
|
index 0000000000..a7c51a68d4
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/OpenTMMPacketHandler.kt
|
|
@@ -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.client.net.gui
|
|
+
|
|
+import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.OpenTMMPacket
|
|
+import net.minecraft.client.Minecraft
|
|
+
|
|
+object OpenTMMPacketHandler : ClientNetworkPacketHandler<OpenTMMPacket> {
|
|
+ override fun handle(packet: OpenTMMPacket, client: Minecraft) {
|
|
+ //client.setScreen()
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetActiveTMPacketHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetActiveTMPacketHandler.kt
|
|
new file mode 100644
|
|
index 0000000000..7f44796473
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetActiveTMPacketHandler.kt
|
|
@@ -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.client.net.gui
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
|
+import net.minecraft.server.MinecraftServer
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.world.level.block.Block
|
|
+
|
|
+object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket> {
|
|
+ override fun handle(packet: SetActiveTMPacket, server: MinecraftServer, player: ServerPlayer) {
|
|
+ val menu = player.containerMenu as? TMMachineMenu ?: return
|
|
+ val inventory = menu.inventory ?: return
|
|
+ val tmData = Cobblemon.playerDataManager.getTMData(player)
|
|
+
|
|
+ if (packet.tm != null && (!packet.tm.isPassivelyObtained() && packet.tm.id !in tmData.learnedTMs)) {
|
|
+ return // Hacker smh
|
|
+ }
|
|
+
|
|
+ menu.tmMachineEntity?.let { blockEntity ->
|
|
+ blockEntity.activeMove = packet.tm?.moveName?.name ?: ""
|
|
+ blockEntity.setChanged()
|
|
+ player.level().sendBlockUpdated(blockEntity.blockPos, blockEntity.blockState, blockEntity.blockState, Block.UPDATE_CLIENTS)
|
|
+ }
|
|
+
|
|
+ inventory.setChanged()
|
|
+ menu.broadcastChanges()
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetTMMachineContainerDataPacketHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetTMMachineContainerDataPacketHandler.kt
|
|
new file mode 100644
|
|
index 0000000000..3e4d9ab5f0
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/gui/SetTMMachineContainerDataPacketHandler.kt
|
|
@@ -0,0 +1,34 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.client.net.gui
|
|
+
|
|
+import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
|
+import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
+import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
|
+import net.minecraft.server.MinecraftServer
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+object SetTMMachineContainerDataPacketHandler : ServerNetworkPacketHandler<SetTMMachineContainerDataPacket> {
|
|
+
|
|
+ override fun handle(packet: SetTMMachineContainerDataPacket, server: MinecraftServer, player: ServerPlayer) {
|
|
+ val menu = player.containerMenu as? TMMachineMenu ?: return
|
|
+ val slotIndex = packet.dataSlotIndex
|
|
+ val slotValue = packet.value
|
|
+
|
|
+ menu.containerData?.set(slotIndex, slotValue)
|
|
+
|
|
+ // If setting to not active, also reset progress
|
|
+ if (slotIndex == TMMachineBlockEntity.BURN_ACTIVE_INDEX && slotValue == 0) {
|
|
+ menu.containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
|
|
+ }
|
|
+
|
|
+ menu.broadcastChanges()
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/net/settings/ServerSettingsPacketHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/settings/ServerSettingsPacketHandler.kt
|
|
index 5378c110a4..cb4e49e86d 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/net/settings/ServerSettingsPacketHandler.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/net/settings/ServerSettingsPacketHandler.kt
|
|
@@ -21,6 +21,7 @@ object ServerSettingsPacketHandler : ClientNetworkPacketHandler<ServerSettingsPa
|
|
ServerSettings.maxPokemonLevel = packet.maxPokemonLevel
|
|
ServerSettings.maxPokemonFriendship = packet.maxPokemonFriendship
|
|
ServerSettings.maxDynamaxLevel = packet.maxDynamaxLevel
|
|
+ ServerSettings.unlockAllMoveDexMovesByDefault = packet.unlockAllMoveDexMovesByDefault
|
|
}
|
|
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/TMShelfBlockEntityRenderer.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/TMShelfBlockEntityRenderer.kt
|
|
new file mode 100644
|
|
index 0000000000..2e0ebd1440
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/TMShelfBlockEntityRenderer.kt
|
|
@@ -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.client.render.block
|
|
+
|
|
+import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.mojang.blaze3d.vertex.PoseStack
|
|
+import com.mojang.blaze3d.vertex.VertexConsumer
|
|
+import com.mojang.math.Axis
|
|
+import net.minecraft.client.renderer.LevelRenderer
|
|
+import net.minecraft.client.renderer.MultiBufferSource
|
|
+import net.minecraft.client.renderer.RenderType
|
|
+import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
|
+import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.core.registries.BuiltInRegistries
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.util.FastColor
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import net.minecraft.world.level.BlockAndTintGetter
|
|
+import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
|
+import org.joml.Matrix4f
|
|
+
|
|
+class TMShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
|
|
+ BlockEntityRenderer<TMShelfBlockEntity> {
|
|
+
|
|
+ private val slotWidthPixels = 6
|
|
+ private val slotHeightPixels = 2
|
|
+ private val textureSize = 16f
|
|
+
|
|
+ private val leftMargin = 1f
|
|
+ private val rightMargin = 1f
|
|
+ private val topMargin = 1f
|
|
+ private val bottomMargin = 1f
|
|
+ private val interRowSpacing = 0f
|
|
+ private val tmOverlayDepthOffset = -0.0002f
|
|
+ private val slotBrightnessMultiplier = 0.65f
|
|
+
|
|
+ private val tmBaseTexture = cobblemonResource("textures/block/tm_shelf/technical_machine_base.png")
|
|
+ private val tmOverlayTexture = cobblemonResource("textures/block/tm_shelf/technical_machine_overlay.png")
|
|
+
|
|
+ override fun render(
|
|
+ entity: TMShelfBlockEntity,
|
|
+ partialTicks: Float,
|
|
+ poseStack: PoseStack,
|
|
+ buffer: MultiBufferSource,
|
|
+ light: Int,
|
|
+ overlay: Int
|
|
+ ) {
|
|
+ val facing = entity.blockState.getValue(HorizontalDirectionalBlock.FACING)
|
|
+ val rotation = when (facing) {
|
|
+ Direction.NORTH -> 0f
|
|
+ Direction.SOUTH -> 180f
|
|
+ Direction.WEST -> 90f
|
|
+ Direction.EAST -> -90f
|
|
+ else -> 0f
|
|
+ }
|
|
+
|
|
+ val lightGetter = entity.level as? BlockAndTintGetter
|
|
+ val packedLight = lightGetter?.let {
|
|
+ LevelRenderer.getLightColor(it, entity.blockPos.relative(facing))
|
|
+ } ?: if (light != 0) light else 0xF000F0
|
|
+
|
|
+ poseStack.pushPose()
|
|
+ poseStack.translate(0.5, 0.5, 0.5)
|
|
+ poseStack.mulPose(Axis.YP.rotationDegrees(rotation))
|
|
+ poseStack.translate(-0.5, -0.5, -0.5)
|
|
+
|
|
+ val slotWidth = slotWidthPixels / textureSize
|
|
+ val slotHeight = slotHeightPixels / textureSize
|
|
+ val verticalSpacing = (slotHeightPixels + interRowSpacing) / textureSize
|
|
+ val totalHeight = slotHeight * 7 + interRowSpacing * 6 / textureSize
|
|
+ val startY = topMargin / textureSize
|
|
+
|
|
+ for ((index, item) in entity.items.withIndex()) {
|
|
+ if (!item.isEmpty) {
|
|
+ val row = index / 2
|
|
+ val col = index % 2
|
|
+ val visualCol = if (facing == Direction.NORTH || facing == Direction.SOUTH) 1 - col else col
|
|
+
|
|
+ val isLeftColumn = visualCol == 0
|
|
+ val posX = if (isLeftColumn)
|
|
+ leftMargin / textureSize
|
|
+ else
|
|
+ (16f - rightMargin - slotWidthPixels) / textureSize
|
|
+
|
|
+ val posY = startY + (6 - row) * verticalSpacing
|
|
+
|
|
+ poseStack.pushPose()
|
|
+ poseStack.translate(posX, posY, -0.001f)
|
|
+
|
|
+ if (item.item == CobblemonItems.TECHNICAL_MACHINE) {
|
|
+ val baseColor = TechnicalMachineItemColorProvider.getColor(item, 0)
|
|
+ val overlayColor = TechnicalMachineItemColorProvider.getColor(item, 1)
|
|
+
|
|
+ renderSlotQuad(
|
|
+ poseStack,
|
|
+ buffer,
|
|
+ tmBaseTexture,
|
|
+ baseColor,
|
|
+ packedLight,
|
|
+ slotWidth,
|
|
+ slotHeight,
|
|
+ tint = true,
|
|
+ useTextRenderType = true
|
|
+ )
|
|
+
|
|
+ poseStack.pushPose()
|
|
+ poseStack.translate(0.0, 0.0, tmOverlayDepthOffset.toDouble())
|
|
+ renderSlotQuad(
|
|
+ poseStack,
|
|
+ buffer,
|
|
+ tmOverlayTexture,
|
|
+ overlayColor,
|
|
+ packedLight,
|
|
+ slotWidth,
|
|
+ slotHeight,
|
|
+ tint = true,
|
|
+ useTextRenderType = true
|
|
+ )
|
|
+ poseStack.popPose()
|
|
+ } else {
|
|
+ val tex = getItemTexture(item)
|
|
+ renderSlotQuad(
|
|
+ poseStack,
|
|
+ buffer,
|
|
+ tex,
|
|
+ 0xFFFFFF,
|
|
+ packedLight,
|
|
+ slotWidth,
|
|
+ slotHeight,
|
|
+ tint = false,
|
|
+ useTextRenderType = true
|
|
+ )
|
|
+ }
|
|
+
|
|
+ poseStack.popPose()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ poseStack.popPose()
|
|
+ }
|
|
+
|
|
+ private fun renderSlotQuad(
|
|
+ poseStack: PoseStack,
|
|
+ buffer: MultiBufferSource,
|
|
+ texture: ResourceLocation,
|
|
+ color: Int,
|
|
+ light: Int,
|
|
+ width: Float,
|
|
+ height: Float,
|
|
+ tint: Boolean,
|
|
+ useTextRenderType: Boolean
|
|
+ ) {
|
|
+ val renderType = if (useTextRenderType) RenderType.text(texture) else RenderType.entityCutout(texture)
|
|
+ val consumer: VertexConsumer = buffer.getBuffer(renderType)
|
|
+ val matrix: Matrix4f = poseStack.last().pose()
|
|
+
|
|
+ val (rBase, gBase, bBase, a) = if (tint) {
|
|
+ listOf(
|
|
+ FastColor.ARGB32.red(color) / 255f,
|
|
+ FastColor.ARGB32.green(color) / 255f,
|
|
+ FastColor.ARGB32.blue(color) / 255f,
|
|
+ 1.0f
|
|
+ )
|
|
+ } else listOf(1f, 1f, 1f, 1f)
|
|
+ val r = (rBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
|
|
+ val g = (gBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
|
|
+ val b = (bBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
|
|
+
|
|
+ consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 1f)
|
|
+ .setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
|
+ consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
|
|
+ .setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
|
+ consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 0f)
|
|
+ .setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
|
+ consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 1f)
|
|
+ .setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
|
+ }
|
|
+
|
|
+ private fun getItemTexture(itemStack: ItemStack): ResourceLocation {
|
|
+ val id = BuiltInRegistries.ITEM.getKey(itemStack.item)
|
|
+ return cobblemonResource("textures/block/tm_shelf/${id.path}.png")
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/render/color/TechnicalMachineItemColorProvider.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/color/TechnicalMachineItemColorProvider.kt
|
|
new file mode 100644
|
|
index 0000000000..0290cb329c
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/render/color/TechnicalMachineItemColorProvider.kt
|
|
@@ -0,0 +1,23 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.client.render.color
|
|
+
|
|
+import com.cobblemon.mod.common.api.types.ElementalTypes
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent.Companion.getTMMove
|
|
+import net.minecraft.client.color.item.ItemColor
|
|
+import net.minecraft.util.FastColor
|
|
+import net.minecraft.world.item.ItemStack
|
|
+
|
|
+object TechnicalMachineItemColorProvider : ItemColor {
|
|
+ override fun getColor(itemStack: ItemStack, layer: Int): Int {
|
|
+ val moveType = getTMMove(itemStack)?.elementalType ?: ElementalTypes.NORMAL
|
|
+ val color = if (layer == 0) moveType.primaryColor else moveType.secondaryColor
|
|
+ return FastColor.ARGB32.opaque(color)
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/settings/ServerSettings.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/settings/ServerSettings.kt
|
|
index 27606dbeb4..b241b3183e 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/client/settings/ServerSettings.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/settings/ServerSettings.kt
|
|
@@ -27,5 +27,6 @@ object ServerSettings {
|
|
var maxPokemonLevel = Cobblemon.config.maxPokemonLevel
|
|
var maxPokemonFriendship = Cobblemon.config.maxPokemonFriendship
|
|
var maxDynamaxLevel = Cobblemon.config.maxDynamaxLevel
|
|
+ var unlockAllMoveDexMovesByDefault = Cobblemon.config.unlockAllMoveDexMovesByDefault
|
|
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/client/tooltips/TechnicalMachineTooltipGenerator.kt b/common/src/main/kotlin/com/cobblemon/mod/common/client/tooltips/TechnicalMachineTooltipGenerator.kt
|
|
new file mode 100644
|
|
index 0000000000..a9def727d6
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/client/tooltips/TechnicalMachineTooltipGenerator.kt
|
|
@@ -0,0 +1,108 @@
|
|
+/*
|
|
+ * 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.tooltips
|
|
+
|
|
+import com.cobblemon.mod.common.api.text.*
|
|
+import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import net.minecraft.core.component.DataComponents
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.world.item.ItemStack
|
|
+
|
|
+object TechnicalMachineTooltipGenerator : TooltipGenerator() {
|
|
+ private val technicalMachineHeader by lazy { lang("technical_machine_effect_header").blue() }
|
|
+ private val technicalMachineItemClass by lazy { lang("item_class.technical_machine").blue() }
|
|
+
|
|
+ // Max characters per description line
|
|
+ private const val MAX_LINE_LENGTH = 35
|
|
+
|
|
+ override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
|
+ if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) return null
|
|
+ if (stack.item !is TechnicalMachineItem) return null
|
|
+ return mutableListOf(technicalMachineItemClass)
|
|
+ }
|
|
+
|
|
+ override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
|
+ if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) return null
|
|
+ if (stack.item !is TechnicalMachineItem) return null
|
|
+
|
|
+ val move = TMMoveComponent.getTMMove(stack) ?: return null
|
|
+
|
|
+ val result = mutableListOf<Component>()
|
|
+
|
|
+ result.add(technicalMachineHeader)
|
|
+
|
|
+ // Type
|
|
+ val typeLabel = lang("ui.info.type").white()
|
|
+ val typeValue = move.elementalType.displayName.copy().withStyle { it.withColor(move.elementalType.primaryColor) }
|
|
+
|
|
+ // Category
|
|
+ val categoryLabel = lang("move.category").white()
|
|
+ val categoryKey = "move.category.${move.damageCategory.name.lowercase()}"
|
|
+ val categoryValue = lang(categoryKey).gold()
|
|
+
|
|
+ result.add(
|
|
+ Component.empty()
|
|
+ .append(typeLabel).append(": ").append(typeValue)
|
|
+ .append(Component.literal(" "))
|
|
+ .append(categoryLabel).append(": ").append(categoryValue)
|
|
+ )
|
|
+
|
|
+ // Power / Accuracy / PP
|
|
+ val powerLabel = lang("ui.power").white()
|
|
+ val accuracyLabel = lang("ui.accuracy").white()
|
|
+ val ppLabel = lang("ui.pp").white()
|
|
+
|
|
+ val accuracy = move.accuracy.toInt()
|
|
+
|
|
+ val powerValue = Component.literal(move.power.toInt().toString()).yellow()
|
|
+ val accuracyValue = Component.literal("${if (accuracy != -1) accuracy else 100}%").yellow()
|
|
+ val ppValue = Component.literal(move.pp.toString()).yellow()
|
|
+
|
|
+ result.add(
|
|
+ Component.empty()
|
|
+ .append(powerLabel).append(": ").append(powerValue)
|
|
+ .append(Component.literal(" "))
|
|
+ .append(accuracyLabel).append(": ").append(accuracyValue)
|
|
+ .append(Component.literal(" "))
|
|
+ .append(ppLabel).append(": ").append(ppValue)
|
|
+ )
|
|
+
|
|
+ // Description (wrapped)
|
|
+ val description = move.description.string
|
|
+ val wrappedLines = wrapText(description, MAX_LINE_LENGTH)
|
|
+ for (line in wrappedLines) {
|
|
+ result.add(Component.literal(line).gray())
|
|
+ }
|
|
+
|
|
+ return result
|
|
+ }
|
|
+
|
|
+ private fun wrapText(text: String, maxLength: Int): List<String> {
|
|
+ val words = text.split(" ")
|
|
+ val lines = mutableListOf<String>()
|
|
+ var currentLine = StringBuilder()
|
|
+
|
|
+ for (word in words) {
|
|
+ if (currentLine.length + word.length + 1 > maxLength) {
|
|
+ lines.add(currentLine.toString())
|
|
+ currentLine = StringBuilder()
|
|
+ }
|
|
+ if (currentLine.isNotEmpty()) currentLine.append(" ")
|
|
+ currentLine.append(word)
|
|
+ }
|
|
+
|
|
+ if (currentLine.isNotEmpty()) {
|
|
+ lines.add(currentLine.toString())
|
|
+ }
|
|
+
|
|
+ return lines
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/command/GiveTmCommand.kt b/common/src/main/kotlin/com/cobblemon/mod/common/command/GiveTmCommand.kt
|
|
new file mode 100644
|
|
index 0000000000..71cee4207d
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/command/GiveTmCommand.kt
|
|
@@ -0,0 +1,89 @@
|
|
+/*
|
|
+ * 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.command
|
|
+
|
|
+import com.cobblemon.mod.common.api.permission.CobblemonPermissions
|
|
+import com.cobblemon.mod.common.command.argument.MoveArgumentType
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.util.alias
|
|
+import com.cobblemon.mod.common.util.permission
|
|
+import com.mojang.brigadier.Command
|
|
+import com.mojang.brigadier.CommandDispatcher
|
|
+import com.mojang.brigadier.context.CommandContext
|
|
+import com.mojang.brigadier.arguments.IntegerArgumentType
|
|
+import net.minecraft.commands.CommandSourceStack
|
|
+import net.minecraft.commands.Commands
|
|
+import net.minecraft.commands.arguments.EntityArgument
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+object GiveTmCommand {
|
|
+
|
|
+ private const val NAME = "givetm"
|
|
+ private const val ALIAS = "giveTM"
|
|
+ private const val PLAYER = "player"
|
|
+ private const val MOVE = "move"
|
|
+ private const val QUANTITY = "quantity"
|
|
+
|
|
+ fun register(dispatcher: CommandDispatcher<CommandSourceStack>) {
|
|
+ val command = Commands.literal(NAME)
|
|
+ .permission(CobblemonPermissions.TECHNICAL_MACHINE)
|
|
+ .then(
|
|
+ Commands.argument(MOVE, MoveArgumentType.move())
|
|
+ .executes { ctx ->
|
|
+ execute(ctx, ctx.source.playerOrException, 1)
|
|
+ }
|
|
+ .then(
|
|
+ Commands.argument(QUANTITY, IntegerArgumentType.integer(1, 2304))
|
|
+ .executes { ctx ->
|
|
+ execute(ctx, ctx.source.playerOrException, IntegerArgumentType.getInteger(ctx, QUANTITY))
|
|
+ }
|
|
+ )
|
|
+ .then(
|
|
+ Commands.argument(PLAYER, EntityArgument.player())
|
|
+ .executes { ctx ->
|
|
+ execute(ctx, EntityArgument.getPlayer(ctx, PLAYER), 1)
|
|
+ }
|
|
+ .then(
|
|
+ Commands.argument(QUANTITY, IntegerArgumentType.integer(1, 2304))
|
|
+ .executes { ctx ->
|
|
+ execute(
|
|
+ ctx,
|
|
+ EntityArgument.getPlayer(ctx, PLAYER),
|
|
+ IntegerArgumentType.getInteger(ctx, QUANTITY)
|
|
+ )
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+
|
|
+ val node = dispatcher.register(command)
|
|
+ dispatcher.register(node.alias(ALIAS))
|
|
+ }
|
|
+
|
|
+ private fun execute(context: CommandContext<CommandSourceStack>, player: ServerPlayer, quantity: Int): Int {
|
|
+ val move = MoveArgumentType.getMove(context, MOVE)
|
|
+
|
|
+ var remaining = quantity
|
|
+ while (remaining > 0) {
|
|
+ val stackSize = remaining.coerceAtMost(64)
|
|
+ val stack = TMMoveComponent.createStack(move).copyWithCount(stackSize)
|
|
+ if (!player.addItem(stack)) {
|
|
+ player.drop(stack, false)
|
|
+ }
|
|
+ remaining -= stackSize
|
|
+ }
|
|
+
|
|
+ context.source.sendSuccess(
|
|
+ { Component.literal("Gave $quantity TM ${move.displayName.string} to ${player.name.string}") },
|
|
+ true
|
|
+ )
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/command/TmCommand.kt b/common/src/main/kotlin/com/cobblemon/mod/common/command/TmCommand.kt
|
|
new file mode 100644
|
|
index 0000000000..68fa415f79
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/command/TmCommand.kt
|
|
@@ -0,0 +1,180 @@
|
|
+/*
|
|
+ * 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.command
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.api.permission.CobblemonPermissions
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.command.argument.TmArgumentType
|
|
+import com.cobblemon.mod.common.util.permission
|
|
+import com.mojang.brigadier.Command
|
|
+import com.mojang.brigadier.CommandDispatcher
|
|
+import com.mojang.brigadier.context.CommandContext
|
|
+import net.minecraft.commands.CommandSourceStack
|
|
+import net.minecraft.commands.Commands
|
|
+import net.minecraft.commands.arguments.EntityArgument
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+object TmCommand {
|
|
+
|
|
+ private const val NAME = "technicalmachine"
|
|
+ private const val UNLOCK = "unlock"
|
|
+ private const val LOCK = "lock"
|
|
+ private const val CHECK = "check"
|
|
+ private const val PLAYER = "player"
|
|
+ private const val ONLY = "only"
|
|
+ private const val ALL = "all"
|
|
+ private const val TM = "TM"
|
|
+
|
|
+ fun register(dispatcher: CommandDispatcher<CommandSourceStack>) {
|
|
+ val command =
|
|
+ Commands.literal(NAME)
|
|
+ .permission(CobblemonPermissions.TECHNICAL_MACHINE)
|
|
+ .then(Commands.literal(UNLOCK)
|
|
+ .then(Commands.argument(PLAYER, EntityArgument.players())
|
|
+ .then(Commands.literal(ONLY)
|
|
+ .then(Commands.argument(TM, TmArgumentType.tm())
|
|
+ .executes { ctx ->
|
|
+ executeUnlockOnly(ctx, EntityArgument.getPlayers(ctx, PLAYER))
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+ .then(Commands.literal(ALL)
|
|
+ .executes { ctx ->
|
|
+ executeUnlockAll(ctx, EntityArgument.getPlayers(ctx, PLAYER))
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ .then(Commands.literal(LOCK)
|
|
+ .then(Commands.argument(PLAYER, EntityArgument.players())
|
|
+ .then(Commands.literal(ONLY)
|
|
+ .then(Commands.argument(TM, TmArgumentType.tm())
|
|
+ .executes { ctx ->
|
|
+ executeLockOnly(ctx, EntityArgument.getPlayers(ctx, PLAYER))
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+ .then(Commands.literal(ALL)
|
|
+ .executes { ctx ->
|
|
+ executeLockAll(ctx, EntityArgument.getPlayers(ctx, PLAYER))
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ .then(Commands.literal(CHECK)
|
|
+ .then(Commands.argument(PLAYER, EntityArgument.players())
|
|
+ .executes { ctx ->
|
|
+ executeCheck(ctx, EntityArgument.getPlayer(ctx, PLAYER))
|
|
+ }
|
|
+ )
|
|
+ )
|
|
+
|
|
+ dispatcher.register(command)
|
|
+ }
|
|
+
|
|
+ private fun executeUnlockOnly(context: CommandContext<CommandSourceStack>, players: Collection<ServerPlayer>) : Int {
|
|
+ val tm = TmArgumentType.getTm(context, TM)
|
|
+
|
|
+ players.forEach { player ->
|
|
+ val playerTMData = Cobblemon.playerDataManager.getTMData(player.uuid)
|
|
+ playerTMData.learn(listOf(tm.id))
|
|
+ }
|
|
+
|
|
+ val selectorStr =
|
|
+ if (players.size == 1) players.first().name.string
|
|
+ else "${players.size} players"
|
|
+
|
|
+ context.source.sendSystemMessage(
|
|
+ Component.literal("Unlocked TM ${tm.id} for $selectorStr")
|
|
+ )
|
|
+
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+
|
|
+ private fun executeLockOnly(context: CommandContext<CommandSourceStack>, players: Collection<ServerPlayer>) : Int {
|
|
+ val tm = TmArgumentType.getTm(context, TM)
|
|
+
|
|
+ players.forEach { player ->
|
|
+ val playerTMData = Cobblemon.playerDataManager.getTMData(player.uuid)
|
|
+ playerTMData.unlearn(listOf(tm.id))
|
|
+ }
|
|
+
|
|
+ val selectorStr =
|
|
+ if (players.size == 1) players.first().name.string
|
|
+ else "${players.size} players"
|
|
+
|
|
+ context.source.sendSystemMessage(
|
|
+ Component.literal("Locked TM ${tm.id} for $selectorStr")
|
|
+ )
|
|
+
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+
|
|
+ private fun executeUnlockAll(context: CommandContext<CommandSourceStack>, players: Collection<ServerPlayer>) : Int {
|
|
+ val allTms = TechnicalMachines.getAllResourceLocations()
|
|
+
|
|
+ players.forEach { player ->
|
|
+ val playerTMData = Cobblemon.playerDataManager.getTMData(player.uuid)
|
|
+ playerTMData.learn(allTms)
|
|
+ }
|
|
+
|
|
+ val selectorStr =
|
|
+ if (players.size == 1) players.first().name.string
|
|
+ else "${players.size} players"
|
|
+
|
|
+ context.source.sendSystemMessage(
|
|
+ Component.literal("Unlocked ${allTms.size} TMs for $selectorStr")
|
|
+ )
|
|
+
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+
|
|
+ private fun executeLockAll(context: CommandContext<CommandSourceStack>, players: Collection<ServerPlayer>) : Int {
|
|
+ val allTms = TechnicalMachines.getAllResourceLocations()
|
|
+
|
|
+ players.forEach { player ->
|
|
+ val playerTMData = Cobblemon.playerDataManager.getTMData(player.uuid)
|
|
+ playerTMData.unlearn(allTms)
|
|
+ }
|
|
+
|
|
+ val selectorStr =
|
|
+ if (players.size == 1) players.first().name.string
|
|
+ else "${players.size} players"
|
|
+
|
|
+ context.source.sendSystemMessage(
|
|
+ Component.literal("Locked ${allTms.size} TMs for $selectorStr")
|
|
+ )
|
|
+
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+
|
|
+ private fun executeCheck(context: CommandContext<CommandSourceStack>, player: ServerPlayer) : Int {
|
|
+ val playerTMData = Cobblemon.playerDataManager.getTMData(player.uuid)
|
|
+ val playerPc = Cobblemon.storage.getPC(player)
|
|
+ val playerParty = Cobblemon.storage.getParty(player)
|
|
+ val allPlayerPokemon = playerPc.toList() + playerParty.toList()
|
|
+
|
|
+ val movesToLearn = mutableSetOf<ResourceLocation>()
|
|
+ for (pokemon in allPlayerPokemon) {
|
|
+ val learnableMoves = playerTMData.getLearnableTMsFromPokemon(pokemon)
|
|
+ movesToLearn.addAll(learnableMoves)
|
|
+ }
|
|
+
|
|
+ playerTMData.learn(movesToLearn)
|
|
+
|
|
+ context.source.sendSystemMessage(
|
|
+ Component.literal("Unlocked ${movesToLearn.size} TMs for ${player.name.string}")
|
|
+ )
|
|
+
|
|
+ return Command.SINGLE_SUCCESS
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/command/argument/TmArgumentType.kt b/common/src/main/kotlin/com/cobblemon/mod/common/command/argument/TmArgumentType.kt
|
|
new file mode 100644
|
|
index 0000000000..e01b231d38
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/command/argument/TmArgumentType.kt
|
|
@@ -0,0 +1,59 @@
|
|
+/*
|
|
+ * 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.command.argument
|
|
+
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.util.commandLang
|
|
+import com.mojang.brigadier.StringReader
|
|
+import com.mojang.brigadier.arguments.ArgumentType
|
|
+import com.mojang.brigadier.context.CommandContext
|
|
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
|
|
+import com.mojang.brigadier.suggestion.Suggestions
|
|
+import com.mojang.brigadier.suggestion.SuggestionsBuilder
|
|
+import net.minecraft.commands.SharedSuggestionProvider
|
|
+import net.minecraft.network.chat.MutableComponent
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import java.util.concurrent.CompletableFuture
|
|
+
|
|
+class TmArgumentType : ArgumentType<TechnicalMachine> {
|
|
+
|
|
+ override fun parse(reader: StringReader): TechnicalMachine {
|
|
+ val resourceLocation = ResourceLocation.read(reader)
|
|
+ if (resourceLocation == null) {
|
|
+ throw SimpleCommandExceptionType(INVALID_TM).createWithContext(reader)
|
|
+ }
|
|
+
|
|
+ val tm = TechnicalMachines.getByResourceLocation(resourceLocation)
|
|
+ if (tm == null) {
|
|
+ throw SimpleCommandExceptionType(INVALID_TM).createWithContext(reader)
|
|
+ }
|
|
+
|
|
+ return tm
|
|
+ }
|
|
+
|
|
+ override fun <S : Any> listSuggestions(
|
|
+ context: CommandContext<S>,
|
|
+ builder: SuggestionsBuilder
|
|
+ ): CompletableFuture<Suggestions> {
|
|
+ val resourceLocations = TechnicalMachines.getAllResourceLocations()
|
|
+ val suggestions = resourceLocations.map { it.toString() }
|
|
+ return SharedSuggestionProvider.suggest(suggestions, builder)
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ val INVALID_TM: MutableComponent = commandLang("technicalmachine.invalid-tm")
|
|
+
|
|
+ fun tm() = TmArgumentType()
|
|
+
|
|
+ fun <S> getTm(context: CommandContext<S>, name: String): TechnicalMachine {
|
|
+ return context.getArgument(name, TechnicalMachine::class.java)
|
|
+ }
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt b/common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
|
|
index d5b1476244..cb9cafe597 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
|
|
@@ -274,6 +274,12 @@ class CobblemonConfig {
|
|
@CobblemonConfigField(Category.Pokedex, lang = "max_pokedex_scanning_detection_range", SERVER)
|
|
var maxPokedexScanningDetectionRange = 10.0
|
|
|
|
+ @CobblemonConfigField(Category.Pokedex, lang = "unlock_all_move_dex_moves_by_default", SERVER)
|
|
+ var unlockAllMoveDexMovesByDefault = false
|
|
+
|
|
+ @CobblemonConfigField(Category.Pokemon, lang = "infinite_tm_uses", SERVER)
|
|
+ var infiniteTmUses = false
|
|
+
|
|
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex", CLIENT)
|
|
var hideUnimplementedPokemonInThePokedex = false
|
|
|
|
@@ -360,4 +366,4 @@ class CobblemonConfig {
|
|
}
|
|
return newConfig
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt b/common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
|
|
index 4b4794d84a..044aa64da4 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
|
|
@@ -48,6 +48,7 @@ import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
|
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
|
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
|
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
import com.cobblemon.mod.common.battles.BagItems
|
|
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
|
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
|
|
@@ -111,6 +112,7 @@ object CobblemonDataProvider : DataProvider {
|
|
this.register(PokemonInteractions, reloadable = false)
|
|
this.register(SpawnBaitEffects, reloadable = false)
|
|
this.register(CobblemonRideSettings, reloadable = true)
|
|
+ this.register(TechnicalMachines, reloadable = false)
|
|
SpawnBait.Effects.setupEffects()
|
|
|
|
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt
|
|
index 275e159533..f4374f4aae 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/events/AdvancementHandler.kt
|
|
@@ -31,6 +31,7 @@ import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
|
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
|
import com.cobblemon.mod.common.util.effectiveName
|
|
import com.cobblemon.mod.common.util.getPlayer
|
|
+import com.cobblemon.mod.common.util.tmList
|
|
import java.util.*
|
|
|
|
object AdvancementHandler : EventHandler {
|
|
@@ -85,6 +86,7 @@ object AdvancementHandler : EventHandler {
|
|
fun onEvolve(event: EvolutionCompleteEvent) {
|
|
val player = event.pokemon.getOwnerPlayer()
|
|
if (player != null) {
|
|
+ player.tmList()?.syncTMsFromPokemon(event.pokemon)
|
|
if (event.pokemon.preEvolution != null) {
|
|
val playerData = Cobblemon.playerDataManager.getGenericData(player)
|
|
val advancementData = playerData.advancementData
|
|
@@ -145,6 +147,7 @@ object AdvancementHandler : EventHandler {
|
|
}
|
|
|
|
fun onLevelUp(event : LevelUpEvent) {
|
|
+ event.pokemon.getOwnerPlayer()?.tmList()?.syncTMsFromPokemon(event.pokemon)
|
|
event.pokemon.getOwnerPlayer()?.let { CobblemonCriteria.LEVEL_UP.trigger(it, LevelUpContext(event.newLevel, event.pokemon)) }
|
|
}
|
|
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/events/PokedexHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/events/PokedexHandler.kt
|
|
index 36e56fc763..8028f20b59 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/events/PokedexHandler.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/events/PokedexHandler.kt
|
|
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.events
|
|
import com.cobblemon.mod.common.Cobblemon
|
|
import com.cobblemon.mod.common.api.Priority
|
|
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
|
+import com.cobblemon.mod.common.api.events.pokemon.LevelUpEvent
|
|
import com.cobblemon.mod.common.api.events.pokemon.PokemonAspectsChangedEvent
|
|
import com.cobblemon.mod.common.api.events.pokemon.PokemonGainedEvent
|
|
import com.cobblemon.mod.common.api.events.pokemon.PokemonSeenEvent
|
|
@@ -20,6 +21,7 @@ object PokedexHandler : EventHandler {
|
|
CobblemonEvents.POKEMON_GAINED.subscribe(Priority.NORMAL, ::onPokemonGained)
|
|
CobblemonEvents.POKEMON_SEEN.subscribe(Priority.NORMAL, ::onPokemonSeen)
|
|
CobblemonEvents.POKEMON_ASPECTS_CHANGED.subscribe(Priority.NORMAL, ::onPokemonAspectsChanged)
|
|
+ CobblemonEvents.LEVEL_UP_EVENT.subscribe(Priority.NORMAL, ::onLevelUp)
|
|
}
|
|
|
|
fun onPokemonGained(event: PokemonGainedEvent) {
|
|
@@ -35,4 +37,8 @@ object PokedexHandler : EventHandler {
|
|
Cobblemon.playerDataManager.getPokedexData(event.ownerId).catch(event.pokemon)
|
|
}
|
|
}
|
|
+
|
|
+ fun onLevelUp(event: LevelUpEvent) {
|
|
+ Cobblemon.playerDataManager.getPokedexData(event.pokemon.getOwnerUUID() ?: return).obtain(event.pokemon)
|
|
+ }
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/item/TypeGemItem.kt b/common/src/main/kotlin/com/cobblemon/mod/common/item/TypeGemItem.kt
|
|
new file mode 100644
|
|
index 0000000000..7bb73ad822
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/item/TypeGemItem.kt
|
|
@@ -0,0 +1,63 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.item
|
|
+
|
|
+import com.cobblemon.mod.common.block.TypeGemBlock
|
|
+import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
|
+import com.cobblemon.mod.common.block.TypeGemCoreBlock
|
|
+import net.minecraft.world.InteractionResult
|
|
+import net.minecraft.world.item.Item
|
|
+import net.minecraft.world.item.context.UseOnContext
|
|
+import net.minecraft.world.level.block.Block
|
|
+import net.minecraft.world.level.block.DirectionalBlock
|
|
+
|
|
+class TypeGemItem(
|
|
+ private val clusterBlock: Block,
|
|
+ properties: Properties
|
|
+) : Item(properties) {
|
|
+
|
|
+ override fun useOn(context: UseOnContext): InteractionResult {
|
|
+ val level = context.level
|
|
+ val clickedPos = context.clickedPos
|
|
+ val clickedState = level.getBlockState(clickedPos)
|
|
+
|
|
+ val placePos = clickedPos.relative(context.clickedFace)
|
|
+
|
|
+ // Only place the cluster on TypeGemCore or TypeGemBlock
|
|
+ if (level.isEmptyBlock(placePos) || level.getBlockState(placePos).canBeReplaced()) {
|
|
+ val placePos = clickedPos.relative(context.clickedFace)
|
|
+
|
|
+ if (level.isEmptyBlock(placePos) || level.getBlockState(placePos).canBeReplaced()) {
|
|
+ val player = context.player
|
|
+ val itemInHand = context.itemInHand
|
|
+
|
|
+ if (!level.isClientSide) {
|
|
+ val facing = context.clickedFace
|
|
+
|
|
+ val shouldGrow = clickedState.block is TypeGemCoreBlock
|
|
+
|
|
+ val state = clusterBlock.defaultBlockState()
|
|
+ .setValue(DirectionalBlock.FACING, facing)
|
|
+ .setValue(TypeGemClusterBlock.STAGE, 0)
|
|
+ .setValue(TypeGemClusterBlock.SHOULD_GROW, shouldGrow)
|
|
+
|
|
+ level.setBlock(placePos, state, 3)
|
|
+
|
|
+ if (player == null || !player.isCreative) {
|
|
+ itemInHand.shrink(1)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return InteractionResult.sidedSuccess(level.isClientSide)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return InteractionResult.PASS
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/item/components/TMMoveComponent.kt b/common/src/main/kotlin/com/cobblemon/mod/common/item/components/TMMoveComponent.kt
|
|
new file mode 100644
|
|
index 0000000000..4a296b0f20
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/item/components/TMMoveComponent.kt
|
|
@@ -0,0 +1,61 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.item.components
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.CobblemonItemComponents
|
|
+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.ElementalTypes
|
|
+import com.mojang.serialization.Codec
|
|
+import com.mojang.serialization.codecs.RecordCodecBuilder
|
|
+import io.netty.buffer.ByteBuf
|
|
+import net.minecraft.network.FriendlyByteBuf
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.network.codec.ByteBufCodecs
|
|
+import net.minecraft.network.codec.StreamCodec
|
|
+import net.minecraft.world.item.ItemStack
|
|
+
|
|
+data class TMMoveComponent(val move: MoveTemplate) {
|
|
+ companion object {
|
|
+ // Codec to serialize/deserialize TMMoveComponent
|
|
+ val CODEC: Codec<TMMoveComponent> = RecordCodecBuilder.create { instance ->
|
|
+ instance.group(
|
|
+ Codec.STRING.fieldOf("move").forGetter { it.move.name } // Serialize the move's name
|
|
+ ).apply(instance) { moveName -> TMMoveComponent(Moves.getByNameOrDummy(moveName)) }
|
|
+ }
|
|
+
|
|
+ val PACKET_CODEC: StreamCodec<ByteBuf, TMMoveComponent> = ByteBufCodecs.fromCodec(CODEC)
|
|
+
|
|
+ fun getTMMove(stack: ItemStack): MoveTemplate? {
|
|
+ return stack.get(CobblemonItemComponents.TM_MOVE)?.move
|
|
+ }
|
|
+
|
|
+ fun setTMMove(stack: ItemStack, move: MoveTemplate): ItemStack {
|
|
+ stack.set(CobblemonItemComponents.TM_MOVE, TMMoveComponent(move))
|
|
+ return stack
|
|
+ }
|
|
+
|
|
+ fun removeTMMove(stack: ItemStack): ItemStack {
|
|
+ stack.remove(CobblemonItemComponents.TM_MOVE)
|
|
+ return stack
|
|
+ }
|
|
+
|
|
+ fun createStack(move: MoveTemplate?): ItemStack {
|
|
+ if (move == null) {
|
|
+ Cobblemon.LOGGER.error("Attempted to create TM stack with null move!")
|
|
+ throw IllegalArgumentException("MoveTemplate cannot be null in createStack.")
|
|
+ }
|
|
+ val stack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
|
+ return setTMMove(stack, move)
|
|
+ }
|
|
+
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt b/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
|
|
index 0de0c62c56..56c004415d 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
|
|
@@ -9,6 +9,7 @@
|
|
package com.cobblemon.mod.common.item.group
|
|
|
|
import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.CobblemonBlocks
|
|
import com.cobblemon.mod.common.CobblemonItemComponents
|
|
import com.cobblemon.mod.common.CobblemonItems
|
|
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
|
@@ -249,6 +250,7 @@ object CobblemonItemGroups {
|
|
entries.accept(CobblemonItems.PC)
|
|
entries.accept(CobblemonItems.HEALING_MACHINE)
|
|
entries.accept(CobblemonItems.PASTURE)
|
|
+ entries.accept(CobblemonItems.TM_MACHINE)
|
|
|
|
entries.accept(CobblemonItems.GILDED_CHEST)
|
|
entries.accept(CobblemonItems.YELLOW_GILDED_CHEST)
|
|
@@ -307,6 +309,26 @@ object CobblemonItemGroups {
|
|
entries.accept(CobblemonItems.BLACK_TUMBLESTONE_BLOCK)
|
|
entries.accept(CobblemonItems.SKY_TUMBLESTONE_BLOCK)
|
|
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_CORE)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_NORMAL)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_FIRE)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_WATER)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_ELECTRIC)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_GRASS)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_ICE)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_FIGHTING)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_POISON)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_GROUND)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_FLYING)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_PSYCHIC)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_BUG)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_ROCK)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_GHOST)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_DRAGON)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_DARK)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_STEEL)
|
|
+ entries.accept(CobblemonItems.TYPE_GEM_BLOCK_FAIRY)
|
|
+
|
|
entries.accept(CobblemonItems.POLISHED_TUMBLESTONE)
|
|
entries.accept(CobblemonItems.POLISHED_TUMBLESTONE_STAIRS)
|
|
entries.accept(CobblemonItems.POLISHED_TUMBLESTONE_SLAB)
|
|
@@ -544,6 +566,8 @@ object CobblemonItemGroups {
|
|
entries.accept(CobblemonItems.ABILITY_CAPSULE)
|
|
entries.accept(CobblemonItems.ABILITY_PATCH)
|
|
|
|
+ entries.accept(CobblemonItems.BLANK_TM)
|
|
+
|
|
entries.accept(CobblemonItems.POKE_BAIT)
|
|
entries.accept(CobblemonItems.POKE_SNACK)
|
|
entries.accept(CobblemonItems.POKE_CAKE)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/TechnicalMachineItem.kt b/common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/TechnicalMachineItem.kt
|
|
new file mode 100644
|
|
index 0000000000..da250d983f
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/TechnicalMachineItem.kt
|
|
@@ -0,0 +1,65 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.item.interactive
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.interaction.PokemonEntityInteraction
|
|
+import com.cobblemon.mod.common.api.moves.BenchedMove
|
|
+import com.cobblemon.mod.common.api.text.gray
|
|
+import com.cobblemon.mod.common.api.text.green
|
|
+import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
|
+import com.cobblemon.mod.common.item.CobblemonItem
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.util.lang
|
|
+import net.minecraft.network.chat.Component
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.sounds.SoundSource
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import net.minecraft.world.item.TooltipFlag
|
|
+
|
|
+class TechnicalMachineItem(properties: Properties) : CobblemonItem(properties), PokemonEntityInteraction {
|
|
+ override val accepted: Set<PokemonEntityInteraction.Ownership> = setOf(PokemonEntityInteraction.Ownership.OWNER)
|
|
+
|
|
+ override fun processInteraction(player: ServerPlayer, entity: PokemonEntity, stack: ItemStack): Boolean {
|
|
+ val moveTemplate = TMMoveComponent.getTMMove(stack) ?: return false
|
|
+ val pokemon = entity.pokemon
|
|
+
|
|
+ val tmLearnableMoves = pokemon.form.moves.tmLearnableMoves()
|
|
+
|
|
+ if (moveTemplate !in tmLearnableMoves) {
|
|
+ player.displayClientMessage(lang("tms.cannot_learn", pokemon.getDisplayName(), moveTemplate.displayName), true)
|
|
+ return false
|
|
+ }
|
|
+ if (pokemon.moveSet.getMoveTemplates().contains(moveTemplate) || pokemon.allAccessibleMoves.contains(moveTemplate)) {
|
|
+ player.displayClientMessage(lang("tms.already_known", pokemon.getDisplayName(), moveTemplate.displayName), true)
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ if (!player.isCreative && !Cobblemon.config.infiniteTmUses) stack.shrink(1)
|
|
+
|
|
+ if (pokemon.moveSet.hasSpace()) {
|
|
+ pokemon.moveSet.add(moveTemplate.create())
|
|
+ } else {
|
|
+ pokemon.benchedMoves.add(BenchedMove(moveTemplate, 0))
|
|
+ }
|
|
+
|
|
+ player.displayClientMessage(lang("tms.teach_move", pokemon.getDisplayName(), moveTemplate.displayName).green(), true)
|
|
+ player.level().playSound(null, player.blockPosition(), CobblemonSounds.TM_USE, SoundSource.PLAYERS, 1.0F, 1.0F)
|
|
+ entity.cry()
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ override fun appendHoverText(stack: ItemStack, context: TooltipContext, tooltip: MutableList<Component>, tooltipFlag: TooltipFlag) {
|
|
+ val move = TMMoveComponent.getTMMove(stack)
|
|
+ val text = move?.displayName ?: lang("tms.unknown_move")
|
|
+ tooltip.add(text.gray())
|
|
+ super.appendHoverText(stack, context, tooltip, tooltipFlag)
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/settings/ServerSettingsPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/settings/ServerSettingsPacket.kt
|
|
index 620ca254d2..b88e2aadc6 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/settings/ServerSettingsPacket.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/settings/ServerSettingsPacket.kt
|
|
@@ -25,7 +25,8 @@ class ServerSettingsPacket internal constructor(
|
|
val displayEntityNameLabel: Boolean,
|
|
val maxPokemonLevel: Int,
|
|
val maxPokemonFriendship: Int,
|
|
- val maxDynamaxLevel: Int
|
|
+ val maxDynamaxLevel: Int,
|
|
+ val unlockAllMoveDexMovesByDefault: Boolean
|
|
) : NetworkPacket<ServerSettingsPacket> {
|
|
override val id = ID
|
|
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
@@ -35,9 +36,10 @@ class ServerSettingsPacket internal constructor(
|
|
buffer.writeInt(Cobblemon.config.maxPokemonLevel)
|
|
buffer.writeInt(Cobblemon.config.maxPokemonFriendship)
|
|
buffer.writeInt(Cobblemon.config.maxDynamaxLevel)
|
|
+ buffer.writeBoolean(Cobblemon.config.unlockAllMoveDexMovesByDefault)
|
|
}
|
|
companion object {
|
|
val ID = cobblemonResource("server_settings")
|
|
- fun decode(buffer: RegistryFriendlyByteBuf) = ServerSettingsPacket(buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readInt(), buffer.readInt(), buffer.readInt())
|
|
+ fun decode(buffer: RegistryFriendlyByteBuf) = ServerSettingsPacket(buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readInt(), buffer.readInt(), buffer.readInt(), buffer.readBoolean())
|
|
}
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/tms/TechnicalMachineRegistrySyncPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/tms/TechnicalMachineRegistrySyncPacket.kt
|
|
new file mode 100644
|
|
index 0000000000..aba543ada0
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/tms/TechnicalMachineRegistrySyncPacket.kt
|
|
@@ -0,0 +1,82 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.messages.client.tms
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.api.moves.Moves
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachineRecipe
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.net.messages.client.data.DataRegistrySyncPacket
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.world.item.crafting.Ingredient
|
|
+
|
|
+class TechnicalMachineRegistrySyncPacket(tms: List<TechnicalMachine>) : DataRegistrySyncPacket<TechnicalMachine, TechnicalMachineRegistrySyncPacket>(tms) {
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("technical_machines")
|
|
+
|
|
+ fun decode(buffer: RegistryFriendlyByteBuf): TechnicalMachineRegistrySyncPacket {
|
|
+ return TechnicalMachineRegistrySyncPacket(emptyList()).apply { decodeBuffer(buffer) }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override val id = ID
|
|
+
|
|
+ override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: TechnicalMachine) {
|
|
+ buffer.writeResourceLocation(entry.id)
|
|
+ buffer.writeUtf(entry.moveName.name)
|
|
+ if (entry.recipe != null) {
|
|
+ buffer.writeBoolean(true)
|
|
+ buffer.writeVarInt(entry.recipe.size)
|
|
+ entry.recipe.forEach {
|
|
+ Ingredient.CONTENTS_STREAM_CODEC.encode(buffer, it.ingredient)
|
|
+ buffer.writeVarInt(it.count)
|
|
+ }
|
|
+ } else {
|
|
+ buffer.writeBoolean(false)
|
|
+ }
|
|
+ buffer.writeVarInt(entry.obtainMethods.size)
|
|
+ entry.obtainMethods.forEach { it.writeToBuffer(buffer) }
|
|
+ buffer.writeUtf(entry.type)
|
|
+ }
|
|
+
|
|
+ override fun decodeEntry(buffer: RegistryFriendlyByteBuf): TechnicalMachine {
|
|
+ val id = buffer.readResourceLocation()
|
|
+ val moveName = buffer.readUtf()
|
|
+
|
|
+ val recipe = if (buffer.readBoolean()) {
|
|
+ val recipeCount = buffer.readVarInt()
|
|
+ List(recipeCount) {
|
|
+ val ingredient = Ingredient.CONTENTS_STREAM_CODEC.decode(buffer)
|
|
+ val count = buffer.readVarInt()
|
|
+ TechnicalMachineRecipe(ingredient, count)
|
|
+ }
|
|
+ } else null
|
|
+
|
|
+ val obtainMethods = List(buffer.readVarInt()) { ObtainMethod.readFromBuffer(buffer) }
|
|
+ val type = buffer.readUtf()
|
|
+
|
|
+ val moveTemplate = Moves.getByName(moveName)
|
|
+ if (moveTemplate == null) {
|
|
+ Cobblemon.LOGGER.error("MoveTemplate not found for move name: $moveName")
|
|
+ throw IllegalStateException("MoveTemplate not found for move name: $moveName")
|
|
+ }
|
|
+
|
|
+ return TechnicalMachine(moveTemplate, recipe, obtainMethods, type).apply { this.id = id }
|
|
+ }
|
|
+
|
|
+
|
|
+ override fun synchronizeDecoded(entries: Collection<TechnicalMachine>) {
|
|
+ TechnicalMachines.reload(entries.associateBy { it.id })
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/toast/ToastPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/toast/ToastPacket.kt
|
|
index 945a850967..eb95b519ca 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/toast/ToastPacket.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/toast/ToastPacket.kt
|
|
@@ -19,12 +19,13 @@ import net.minecraft.resources.ResourceLocation
|
|
class ToastPacket(
|
|
val title: Component,
|
|
val description: Component,
|
|
- val icon: ItemStack,
|
|
+ val icons: List<ItemStack>,
|
|
val frameTexture: ResourceLocation,
|
|
val progress: Float,
|
|
val progressColor: Int,
|
|
val uuid: UUID,
|
|
- val behaviour: Behaviour
|
|
+ val behaviour: Behaviour,
|
|
+ val durationMs: Long? = null
|
|
) : NetworkPacket<ToastPacket> {
|
|
|
|
override val id: ResourceLocation = ID
|
|
@@ -32,12 +33,15 @@ class ToastPacket(
|
|
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
buffer.writeText(this.title)
|
|
buffer.writeText(this.description)
|
|
- buffer.writeItemStack(this.icon)
|
|
+ buffer.writeCollection(this.icons) { _, icon ->
|
|
+ buffer.writeItemStack(icon)
|
|
+ }
|
|
buffer.writeIdentifier(this.frameTexture)
|
|
buffer.writeFloat(this.progress)
|
|
buffer.writeInt(this.progressColor)
|
|
buffer.writeUUID(this.uuid)
|
|
buffer.writeEnumConstant(this.behaviour)
|
|
+ if (durationMs != null) buffer.writeLong(durationMs)
|
|
}
|
|
|
|
companion object {
|
|
@@ -47,12 +51,13 @@ class ToastPacket(
|
|
fun decode(buffer: RegistryFriendlyByteBuf): ToastPacket = ToastPacket(
|
|
buffer.readText(),
|
|
buffer.readText(),
|
|
- buffer.readItemStack(),
|
|
+ buffer.readList { _ -> buffer.readItemStack() },
|
|
buffer.readIdentifier(),
|
|
buffer.readFloat(),
|
|
buffer.readInt(),
|
|
buffer.readUUID(),
|
|
- buffer.readEnumConstant(Behaviour::class.java)
|
|
+ buffer.readEnumConstant(Behaviour::class.java),
|
|
+ durationMs = if (buffer.readableBytes() >= 8) buffer.readLong() else null
|
|
)
|
|
|
|
}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/OpenTMMPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/OpenTMMPacket.kt
|
|
new file mode 100644
|
|
index 0000000000..74409a9095
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/OpenTMMPacket.kt
|
|
@@ -0,0 +1,22 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.messages.client.ui
|
|
+
|
|
+import com.cobblemon.mod.common.api.net.NetworkPacket
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+
|
|
+class OpenTMMPacket: NetworkPacket<OpenTMMPacket> {
|
|
+ override val id = cobblemonResource("open_tm")
|
|
+
|
|
+ override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
+
|
|
+ }
|
|
+
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetActiveTMPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetActiveTMPacket.kt
|
|
new file mode 100644
|
|
index 0000000000..8688c0f720
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetActiveTMPacket.kt
|
|
@@ -0,0 +1,39 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.messages.client.ui
|
|
+
|
|
+import com.cobblemon.mod.common.api.net.NetworkPacket
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
|
+import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+
|
|
+/**
|
|
+ * Tells the server to set active [TechnicalMachineItem] using the [TMMachineBlock]
|
|
+ *
|
|
+ * Handled by [SetActiveTMPacketHandler]
|
|
+ */
|
|
+class SetActiveTMPacket(val tm: TechnicalMachine?): NetworkPacket<SetActiveTMPacket> {
|
|
+ override val id = ID
|
|
+
|
|
+ override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeNullable(tm) { _, v -> buffer.writeResourceLocation(v.id) }
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("set_active_tm")
|
|
+
|
|
+ fun decode(buffer: RegistryFriendlyByteBuf) = SetActiveTMPacket(
|
|
+ TechnicalMachines.tmMap[buffer.readNullable { buffer.readResourceLocation() }]
|
|
+ )
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetTMMachineContainerDataPacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetTMMachineContainerDataPacket.kt
|
|
new file mode 100644
|
|
index 0000000000..b10f6a0be2
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/ui/SetTMMachineContainerDataPacket.kt
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.messages.client.ui
|
|
+
|
|
+import com.cobblemon.mod.common.api.net.NetworkPacket
|
|
+import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
|
+import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+
|
|
+/**
|
|
+ * Tells the server to set container data within [TMMachineBlockEntity]
|
|
+ *
|
|
+ * Handled by [SetTMMachineContainerDataPacketHandler]
|
|
+ */
|
|
+class SetTMMachineContainerDataPacket(val dataSlotIndex: Int, val value: Int): NetworkPacket<SetTMMachineContainerDataPacket> {
|
|
+ override val id = ID
|
|
+
|
|
+ override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeInt(dataSlotIndex)
|
|
+ buffer.writeInt(value)
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("tm_machine_set_container_data")
|
|
+
|
|
+ fun decode(buffer: RegistryFriendlyByteBuf) = SetTMMachineContainerDataPacket(
|
|
+ buffer.readInt(),
|
|
+ buffer.readInt()
|
|
+ )
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/server/block/TMMachineTeachMovePacket.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/server/block/TMMachineTeachMovePacket.kt
|
|
new file mode 100644
|
|
index 0000000000..6ffec9e870
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/messages/server/block/TMMachineTeachMovePacket.kt
|
|
@@ -0,0 +1,43 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.messages.server.block
|
|
+
|
|
+import com.cobblemon.mod.common.api.moves.MoveTemplate
|
|
+import com.cobblemon.mod.common.api.moves.Moves
|
|
+import com.cobblemon.mod.common.api.net.NetworkPacket
|
|
+import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
|
|
+import com.cobblemon.mod.common.util.*
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.world.item.ItemStack
|
|
+import java.util.UUID
|
|
+
|
|
+/**
|
|
+ * Handled by [TMMachineTeachMoveHandler].
|
|
+ */
|
|
+class TMMachineTeachMovePacket(val uuid: UUID, val heldStack: ItemStack, val moveTemplate: MoveTemplate?, val isParty: Boolean = true) : NetworkPacket<TMMachineTeachMovePacket> {
|
|
+ override val id = ID
|
|
+ override fun encode(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeUUID(uuid)
|
|
+ buffer.writeNullable(moveTemplate) { _, v -> buffer.writeString(v.name) }
|
|
+ buffer.writeBoolean(isParty)
|
|
+ buffer.writeItemStack(heldStack)
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("teach_move")
|
|
+ fun decode(buffer: RegistryFriendlyByteBuf): TMMachineTeachMovePacket {
|
|
+ val uuid = buffer.readUUID()
|
|
+ val moveName = buffer.readNullable { buffer.readString() }
|
|
+ val moveTemplate = if (moveName == null) null else Moves.getByName(moveName)
|
|
+ val isParty = buffer.readBoolean()
|
|
+ val heldStack = buffer.readItemStack()
|
|
+ return TMMachineTeachMovePacket(uuid, heldStack, moveTemplate, isParty)
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/net/serverhandling/block/TMMachineTeachMoveHandler.kt b/common/src/main/kotlin/com/cobblemon/mod/common/net/serverhandling/block/TMMachineTeachMoveHandler.kt
|
|
new file mode 100644
|
|
index 0000000000..2fb8ed55df
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/net/serverhandling/block/TMMachineTeachMoveHandler.kt
|
|
@@ -0,0 +1,54 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.net.serverhandling.block
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.CobblemonSounds
|
|
+import com.cobblemon.mod.common.api.moves.BenchedMove
|
|
+import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
|
+import com.cobblemon.mod.common.api.storage.PokemonStore
|
|
+import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
|
+import com.cobblemon.mod.common.item.components.TMMoveComponent
|
|
+import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
|
+import com.cobblemon.mod.common.util.party
|
|
+import com.cobblemon.mod.common.util.pc
|
|
+import net.minecraft.server.MinecraftServer
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.world.item.ItemStack
|
|
+
|
|
+object TMMachineTeachMoveHandler : ServerNetworkPacketHandler<TMMachineTeachMovePacket> {
|
|
+
|
|
+ override fun handle(packet: TMMachineTeachMovePacket, server: MinecraftServer, player: ServerPlayer) {
|
|
+ val pokemonStore: PokemonStore<*> = if (packet.isParty) player.party() else player.pc()
|
|
+ val pokemon = pokemonStore[packet.uuid] ?: return
|
|
+ val moveTemplate = packet.moveTemplate ?: return
|
|
+ val menu = player.containerMenu as? TMMachineMenu ?: return
|
|
+ val inventory = menu.inventory ?: return
|
|
+
|
|
+ if (ItemStack.isSameItemSameComponents(packet.heldStack, menu.carried) &&
|
|
+ moveTemplate == TMMoveComponent.getTMMove(packet.heldStack) &&
|
|
+ packet.heldStack.count == menu.carried.count &&
|
|
+ !pokemon.moveSet.getMoveTemplates().contains(moveTemplate) &&
|
|
+ !pokemon.allAccessibleMoves.contains(moveTemplate)
|
|
+ ) {
|
|
+ if (!player.isCreative && !Cobblemon.config.infiniteTmUses) menu.carried.shrink(1)
|
|
+
|
|
+ if (pokemon.moveSet.hasSpace()) {
|
|
+ pokemon.moveSet.add(moveTemplate.create())
|
|
+ } else {
|
|
+ pokemon.benchedMoves.add(BenchedMove(moveTemplate, 0))
|
|
+ }
|
|
+
|
|
+ inventory.setChanged()
|
|
+ menu.broadcastChanges()
|
|
+
|
|
+ menu.tmMachineEntity?.playSound(CobblemonSounds.TM_USE)
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
|
|
index a29cf4cbc0..995210f724 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
|
|
@@ -67,6 +67,8 @@ import com.cobblemon.mod.common.api.storage.StoreCoordinates
|
|
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
|
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
|
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
|
+import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
|
+import com.cobblemon.mod.common.api.tms.TMMoveManager
|
|
import com.cobblemon.mod.common.api.types.ElementalType
|
|
import com.cobblemon.mod.common.api.types.ElementalTypes
|
|
import com.cobblemon.mod.common.api.types.tera.TeraType
|
|
@@ -114,6 +116,7 @@ import com.cobblemon.mod.common.util.playSoundServer
|
|
import com.cobblemon.mod.common.util.server
|
|
import com.cobblemon.mod.common.util.setPositionSafely
|
|
import com.cobblemon.mod.common.util.toBlockPos
|
|
+import com.cobblemon.mod.common.util.tmList
|
|
import com.google.gson.JsonObject
|
|
import com.mojang.datafixers.util.Pair
|
|
import com.mojang.serialization.Codec
|
|
@@ -520,7 +523,11 @@ open class Pokemon : ShowdownIdentifiable {
|
|
* swap in moves they've used before at any time, while holding onto the remaining PP
|
|
* that they had last.
|
|
*/
|
|
- var benchedMoves = BenchedMoves().also { it.changeFunction = { onChange(BenchedMovesUpdatePacket({ this }, it)) }}
|
|
+ var benchedMoves = BenchedMoves().also { it.changeFunction = {
|
|
+ onChange(BenchedMovesUpdatePacket({ this }, it))
|
|
+ this.getOwnerUUID()?.let { it1 -> Cobblemon.playerDataManager.getTMData(it1)?.syncTMsFromPokemon(this) } // we want to make sure to update the players TM Data store when any moves are learned
|
|
+ }
|
|
+ }
|
|
internal set(value) {
|
|
val oldChangeFunction = field.changeFunction
|
|
field.changeFunction = {}
|
|
@@ -1564,8 +1571,7 @@ open class Pokemon : ShowdownIdentifiable {
|
|
originalTrainer = fakeTrainerName
|
|
}
|
|
|
|
- fun refreshOriginalTrainer()
|
|
- {
|
|
+ fun refreshOriginalTrainer() {
|
|
when (originalTrainerType)
|
|
{
|
|
OriginalTrainerType.PLAYER -> {
|
|
@@ -1584,8 +1590,7 @@ open class Pokemon : ShowdownIdentifiable {
|
|
}
|
|
}
|
|
|
|
- fun removeOriginalTrainer()
|
|
- {
|
|
+ fun removeOriginalTrainer() {
|
|
originalTrainer = null
|
|
originalTrainerType = OriginalTrainerType.NONE
|
|
originalTrainerName = null
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/controller/ClientEvolutionController.kt b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/controller/ClientEvolutionController.kt
|
|
index 2d5271d4fd..668b9e28f9 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/controller/ClientEvolutionController.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/controller/ClientEvolutionController.kt
|
|
@@ -82,7 +82,7 @@ class ClientEvolutionController(
|
|
override fun asIntermediate(): Intermediate = Intermediate(this.evolutions)
|
|
|
|
fun sendPlayerNotification() {
|
|
- if (pokemon.heldItem?.item != CobblemonItems.EVERSTONE) {
|
|
+ if (pokemon.heldItem.item != CobblemonItems.EVERSTONE) {
|
|
PartyOverlayDataControl.pokemonGainedEvo(pokemon.uuid, 1)
|
|
}
|
|
}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/DefaultObtainMethod.kt b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/DefaultObtainMethod.kt
|
|
new file mode 100644
|
|
index 0000000000..9cc2c19617
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/DefaultObtainMethod.kt
|
|
@@ -0,0 +1,40 @@
|
|
+/*
|
|
+ * 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.tms.obtain
|
|
+
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+/**
|
|
+ * An [ObtainMethod] that unlocks immediately by default.
|
|
+ * Used for TMs that aren't unlocked and are available immediately.
|
|
+ * TMs with this [ObtainMethod] will not display an unlock text.
|
|
+ *
|
|
+ * @author Plastered_Crab
|
|
+ */
|
|
+class DefaultObtainMethod : ObtainMethod {
|
|
+ override val passive = true
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("default")
|
|
+
|
|
+ fun readFromBuffer(buffer: RegistryFriendlyByteBuf): DefaultObtainMethod {
|
|
+ return DefaultObtainMethod()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun matches(player: ServerPlayer) = true
|
|
+
|
|
+ override fun writeToBuffer(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeUtf("cobblemon:default")
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerHasAdvancementObtainMethod.kt b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerHasAdvancementObtainMethod.kt
|
|
new file mode 100644
|
|
index 0000000000..c6e4da1a82
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerHasAdvancementObtainMethod.kt
|
|
@@ -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.tms.obtain
|
|
+
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+
|
|
+/**
|
|
+ * An [ObtainMethod] that triggers when the player has an advancement.
|
|
+ */
|
|
+class PlayerHasAdvancementObtainMethod(val advancement: ResourceLocation? = null) : ObtainMethod {
|
|
+
|
|
+ // todo we can leave this here in case we want to use it later.... or addons?
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("advancement")
|
|
+
|
|
+ fun readFromBuffer(buffer: RegistryFriendlyByteBuf): PlayerHasAdvancementObtainMethod {
|
|
+ val hasAdvancement = buffer.readBoolean()
|
|
+ val advancement = if (hasAdvancement) buffer.readResourceLocation() else null
|
|
+ return PlayerHasAdvancementObtainMethod(advancement)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override val passive = true
|
|
+
|
|
+ override fun matches(player: ServerPlayer): Boolean {
|
|
+ if (advancement == null) return false
|
|
+ val advancementInstance = player.server.getAdvancements().get(advancement) ?: return false
|
|
+ val progress = player.advancements.getOrStartProgress(advancementInstance)
|
|
+ return progress != null && progress.isDone
|
|
+ }
|
|
+
|
|
+ override fun writeToBuffer(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeUtf("cobblemon:advancement")
|
|
+ buffer.writeResourceLocation(advancement ?: ResourceLocation.tryParse("minecraft:empty"))
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerYObtainMethod.kt b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerYObtainMethod.kt
|
|
new file mode 100644
|
|
index 0000000000..9ad786ba94
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/PlayerYObtainMethod.kt
|
|
@@ -0,0 +1,48 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.tms.obtain
|
|
+
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+/**
|
|
+ * An [ObtainMethod] that checks the player's Y level.
|
|
+ *
|
|
+ * @param yLevel The Y level to check for.
|
|
+ * @param operator The operator to use. Can be "equals", "less", or "greater".
|
|
+ * @author whatsy
|
|
+ */
|
|
+class PlayerYObtainMethod(val yLevel: Int, val operator: String) : ObtainMethod {
|
|
+ override val passive = true
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("y_level")
|
|
+
|
|
+ fun readFromBuffer(buffer: RegistryFriendlyByteBuf): PlayerYObtainMethod {
|
|
+ val yLevel = buffer.readVarInt()
|
|
+ val operator = buffer.readUtf()
|
|
+ return PlayerYObtainMethod(yLevel, operator)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun matches(player: ServerPlayer) = when (operator) {
|
|
+ "equals" -> player.y.toInt() == yLevel
|
|
+ "less" -> player.y.toInt() < yLevel
|
|
+ "greater" -> player.y.toInt() > yLevel
|
|
+ else -> false
|
|
+ }
|
|
+
|
|
+ override fun writeToBuffer(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeUtf("cobblemon:y_level")
|
|
+ buffer.writeVarInt(yLevel)
|
|
+ buffer.writeUtf(operator)
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/UnlockableObtainMethod.kt b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/UnlockableObtainMethod.kt
|
|
new file mode 100644
|
|
index 0000000000..00e5becb98
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/tms/obtain/UnlockableObtainMethod.kt
|
|
@@ -0,0 +1,36 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.tms.obtain
|
|
+
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.util.cobblemonResource
|
|
+import net.minecraft.network.RegistryFriendlyByteBuf
|
|
+import net.minecraft.server.level.ServerPlayer
|
|
+
|
|
+/**
|
|
+ * An [ObtainMethod] that triggers when you get the move on a Pokemon you own or insert into a Data Monitor
|
|
+ * @author Plastered_Crab
|
|
+ */
|
|
+class UnlockableObtainMethod : ObtainMethod {
|
|
+ override val passive = false
|
|
+
|
|
+ companion object {
|
|
+ val ID = cobblemonResource("unlockable")
|
|
+
|
|
+ fun readFromBuffer(buffer: RegistryFriendlyByteBuf): UnlockableObtainMethod {
|
|
+ return UnlockableObtainMethod()
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun matches(player: ServerPlayer) = false
|
|
+
|
|
+ override fun writeToBuffer(buffer: RegistryFriendlyByteBuf) {
|
|
+ buffer.writeUtf("cobblemon:unlockable")
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
|
|
index 7c56effca3..858f7e93a0 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/DataKeys.kt
|
|
@@ -211,6 +211,7 @@ object DataKeys {
|
|
const val HAS_CREATED_POKEMON = "HasCreatedPokemon"
|
|
const val FOSSIL_OWNER = "FossilOwner"
|
|
const val FOSSIL_STATE = "FossilState"
|
|
+ const val MONITOR_DISK = "MonitorDisk"
|
|
|
|
// Generic Block Entity
|
|
const val BLOCK_ENTITY_USER_AMOUNT = "userAmount"
|
|
@@ -266,4 +267,4 @@ object DataKeys {
|
|
|
|
// PokemonProperties
|
|
const val ELEMENTAL_TYPE = "ElementalType"
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
|
|
index b0b44635db..b29f3e1e71 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/PlayerExtensions.kt
|
|
@@ -60,6 +60,7 @@ import kotlin.math.sin
|
|
fun ServerPlayer.party() = Cobblemon.storage.getParty(this)
|
|
fun ServerPlayer.pc() = Cobblemon.storage.getPC(this)
|
|
fun ServerPlayer.pokedex() = Cobblemon.playerDataManager.getPokedexData(this)
|
|
+fun ServerPlayer.tmList() = Cobblemon.playerDataManager.getTMData(this)
|
|
val ServerPlayer.activeDialogue: ActiveDialogue?
|
|
get() = DialogueManager.activeDialogues[uuid]
|
|
val ServerPlayer.isInDialogue: Boolean
|
|
@@ -490,4 +491,4 @@ fun ServerPlayer.updateShoulderNbt(pokemon: Pokemon) {
|
|
nbt.put(DataKeys.SHOULDER_ASPECTS, pokemon.aspects.map(StringTag::valueOf).toNbtList())
|
|
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, pokemon.scaleModifier)
|
|
if ((pokemon.state as ShoulderedState).isLeftShoulder) shoulderEntityLeft = nbt else shoulderEntityRight = nbt
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/IdentifierAdapter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/IdentifierAdapter.kt
|
|
index 15d4912b86..1dc3d68101 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/IdentifierAdapter.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/IdentifierAdapter.kt
|
|
@@ -26,4 +26,4 @@ import net.minecraft.resources.ResourceLocation
|
|
object IdentifierAdapter : JsonSerializer<ResourceLocation>, JsonDeserializer<ResourceLocation> {
|
|
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext) = ResourceLocation.parse(json.asString)
|
|
override fun serialize(src: ResourceLocation, type: Type, ctx: JsonSerializationContext) = JsonPrimitive(src.toString())
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/TMObtainMethodAdapter.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/TMObtainMethodAdapter.kt
|
|
new file mode 100644
|
|
index 0000000000..a4b365aa6f
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/TMObtainMethodAdapter.kt
|
|
@@ -0,0 +1,65 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.common.util.adapters
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.common.api.tms.ObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.DefaultObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
|
|
+import com.cobblemon.mod.common.tms.obtain.UnlockableObtainMethod
|
|
+import com.google.gson.JsonDeserializationContext
|
|
+import com.google.gson.JsonDeserializer
|
|
+import com.google.gson.JsonElement
|
|
+import com.google.gson.JsonSerializationContext
|
|
+import com.google.gson.JsonSerializer
|
|
+import java.lang.reflect.Type
|
|
+import kotlin.reflect.KClass
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+
|
|
+object TMObtainMethodAdapter : JsonSerializer<ObtainMethod>, JsonDeserializer<ObtainMethod> {
|
|
+ private const val VARIANT = "variant"
|
|
+ private val types = hashMapOf<String, KClass<out ObtainMethod>>()
|
|
+
|
|
+ init {
|
|
+ this.register(UnlockableObtainMethod::class, UnlockableObtainMethod.ID)
|
|
+ this.register(PlayerHasAdvancementObtainMethod::class, PlayerHasAdvancementObtainMethod.ID)
|
|
+ this.register(DefaultObtainMethod::class, DefaultObtainMethod.ID)
|
|
+ this.register(PlayerYObtainMethod::class, PlayerYObtainMethod.ID)
|
|
+ }
|
|
+
|
|
+ fun register(type: KClass<out ObtainMethod>, identifier: ResourceLocation) {
|
|
+ val existing = this.types.put(identifier.toString(), type)
|
|
+ if (existing != null) {
|
|
+ Cobblemon.LOGGER.debug(
|
|
+ "Replaced {} under ID {} with {} in the {}",
|
|
+ existing::class.qualifiedName,
|
|
+ identifier.toString(),
|
|
+ type.qualifiedName,
|
|
+ this::class.qualifiedName
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ override fun deserialize(jElement: JsonElement, type: Type, context: JsonDeserializationContext): ObtainMethod {
|
|
+ val json = jElement.asJsonObject
|
|
+ val variant = json.get(VARIANT).asString.lowercase()
|
|
+ val registeredType = this.types[variant]
|
|
+ ?: throw IllegalArgumentException("Cannot resolve type for variant $variant")
|
|
+ return context.deserialize(json, registeredType.java)
|
|
+ }
|
|
+
|
|
+ override fun serialize(method: ObtainMethod, type: Type, context: JsonSerializationContext): JsonElement {
|
|
+ val json = context.serialize(method).asJsonObject
|
|
+ val variant = this.types.entries.find { it.value == method::class }?.key
|
|
+ ?: throw IllegalArgumentException("Cannot resolve variant for type ${method::class.qualifiedName}")
|
|
+ json.addProperty(VARIANT, variant)
|
|
+ return json
|
|
+ }
|
|
+}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonConfiguredFeatures.kt b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonConfiguredFeatures.kt
|
|
index 1307cc5c16..ece8e50c82 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonConfiguredFeatures.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonConfiguredFeatures.kt
|
|
@@ -43,6 +43,8 @@ object CobblemonConfiguredFeatures {
|
|
val REVIVAL_HERBS_KEY = of("revival_herb")
|
|
@JvmField
|
|
val BERRY_GROVE_KEY = of("berry_groves")
|
|
+ @JvmField
|
|
+ val TYPE_GEM_KEY = of("type_gems")
|
|
|
|
private fun of(id: String): ResourceKey<ConfiguredFeature<*, *>> = ResourceKey.create(Registries.CONFIGURED_FEATURE, cobblemonResource(id))
|
|
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonFeatures.kt b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonFeatures.kt
|
|
index d03317a86c..96f66f488c 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonFeatures.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonFeatures.kt
|
|
@@ -28,4 +28,6 @@ object CobblemonFeatures : PlatformRegistry<Registry<Feature<*>>, ResourceKey<Re
|
|
val MINT_FEATURE = create("mint", MintBlockFeature())
|
|
@JvmField
|
|
val BERRY_GROVE_FEATURE = CobblemonFeatures.create("berry_grove", BerryGroveFeature())
|
|
+ @JvmField
|
|
+ val TYPE_GEM_FEATURE = create("type_gem", TypeGemFeature())
|
|
}
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonPlacedFeatures.kt b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonPlacedFeatures.kt
|
|
index a432ab0756..6b930ce743 100644
|
|
--- a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonPlacedFeatures.kt
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonPlacedFeatures.kt
|
|
@@ -54,6 +54,9 @@ object CobblemonPlacedFeatures {
|
|
@JvmField
|
|
val BERRY_GROVE = of("berry_groves")
|
|
|
|
+ @JvmField
|
|
+ val TYPE_GEM = of("type_gems")
|
|
+
|
|
@JvmField
|
|
val SWAMP_GRAINS = of("swamp_grains")
|
|
@JvmField
|
|
@@ -71,6 +74,7 @@ object CobblemonPlacedFeatures {
|
|
Cobblemon.implementation.addFeatureToWorldGen(BIG_ROOT, GenerationStep.Decoration.VEGETAL_DECORATION, BiomeTags.IS_OVERWORLD)
|
|
Cobblemon.implementation.addFeatureToWorldGen(REVIVAL_HERB, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.HAS_REVIVAL_HERBS)
|
|
Cobblemon.implementation.addFeatureToWorldGen(BERRY_GROVE, GenerationStep.Decoration.VEGETAL_DECORATION, BiomeTags.IS_OVERWORLD)
|
|
+ Cobblemon.implementation.addFeatureToWorldGen(TYPE_GEM, GenerationStep.Decoration.UNDERGROUND_ORES, BiomeTags.IS_OVERWORLD)
|
|
Cobblemon.implementation.addFeatureToWorldGen(SWAMP_GRAINS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_SWAMP)
|
|
Cobblemon.implementation.addFeatureToWorldGen(PLAINS_GRAINS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_PLAINS)
|
|
Cobblemon.implementation.addFeatureToWorldGen(GALARICA_NUTS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_BEACH)
|
|
diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/TypeGemFeature.kt b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/TypeGemFeature.kt
|
|
new file mode 100644
|
|
index 0000000000..34f74246d4
|
|
--- /dev/null
|
|
+++ b/common/src/main/kotlin/com/cobblemon/mod/common/world/feature/TypeGemFeature.kt
|
|
@@ -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.world.feature
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlocks
|
|
+import com.cobblemon.mod.common.CobblemonBlocks.TYPE_GEM_CORE
|
|
+import com.cobblemon.mod.common.CobblemonBlocks.typeGemBlocks
|
|
+import com.cobblemon.mod.common.CobblemonBlocks.typeGemClusters
|
|
+import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
|
+import com.cobblemon.mod.common.block.TypeGemCoreBlock
|
|
+import com.cobblemon.mod.common.util.random
|
|
+import net.minecraft.core.BlockPos
|
|
+import net.minecraft.core.Direction
|
|
+import net.minecraft.resources.ResourceLocation
|
|
+import net.minecraft.tags.BlockTags
|
|
+import net.minecraft.world.level.WorldGenLevel
|
|
+import net.minecraft.world.level.block.Block.UPDATE_ALL
|
|
+import net.minecraft.world.level.levelgen.feature.Feature
|
|
+import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
|
|
+import net.minecraft.world.level.levelgen.feature.configurations.BlockStateConfiguration
|
|
+
|
|
+class TypeGemFeature : Feature<BlockStateConfiguration>(BlockStateConfiguration.CODEC) {
|
|
+ override fun place(context: FeaturePlaceContext<BlockStateConfiguration?>): Boolean {
|
|
+ val worldGenLevel = context.level()
|
|
+ val random = context.random()
|
|
+ val origin = context.origin()
|
|
+
|
|
+ val originBlock = worldGenLevel.getBlockState(origin)
|
|
+ if (!originBlock.`is`(BlockTags.DEEPSLATE_ORE_REPLACEABLES)) {
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ val neighborAirBlocks = getNeighborAirBlocks(worldGenLevel, origin)
|
|
+ if (neighborAirBlocks.isEmpty()) {
|
|
+ return false
|
|
+ }
|
|
+
|
|
+ val chosenPos = neighborAirBlocks[random.nextInt(neighborAirBlocks.size)]
|
|
+ val typeGemBlocks = typeGemBlocks().toList()
|
|
+ val (gemId, gemBlock) = typeGemBlocks[random.nextInt(typeGemBlocks.size)]
|
|
+ val gemState = gemBlock.defaultBlockState()
|
|
+ val coreState = TYPE_GEM_CORE.defaultBlockState()
|
|
+
|
|
+ // Place core and initial gem
|
|
+ worldGenLevel.setBlock(origin, coreState, UPDATE_ALL)
|
|
+ worldGenLevel.setBlock(chosenPos, gemState, UPDATE_ALL)
|
|
+
|
|
+ // Trigger forced growth with clusters
|
|
+ val coreBlock = coreState.block as TypeGemCoreBlock
|
|
+ coreBlock.forceGrow(worldGenLevel, origin, random, 0.5f)
|
|
+
|
|
+ // println("[TypeGemFeature] Placed ${gemId.path} at $chosenPos")
|
|
+ return true
|
|
+ }
|
|
+
|
|
+ fun getNeighborAirBlocks(worldGenLevel: WorldGenLevel, origin: BlockPos): List<BlockPos> {
|
|
+ val airBlocks = mutableListOf<BlockPos>()
|
|
+
|
|
+ for (direction: Direction in Direction.entries) {
|
|
+ val pos = origin.offset(direction.normal)
|
|
+ val blockState = worldGenLevel.getBlockState(pos)
|
|
+ if (blockState.isAir) {
|
|
+ airBlocks.add(pos)
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return airBlocks
|
|
+ }
|
|
+}
|
|
diff --git a/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/CobblemonFabricDataGenerator.kt b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/CobblemonFabricDataGenerator.kt
|
|
new file mode 100644
|
|
index 0000000000..1d7b02520b
|
|
--- /dev/null
|
|
+++ b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/CobblemonFabricDataGenerator.kt
|
|
@@ -0,0 +1,27 @@
|
|
+/*
|
|
+ * Copyright (C) 2023 Cobblemon Contributors
|
|
+ *
|
|
+ * This Source Code Form is subject to the terms of the Mozilla Public
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
+ */
|
|
+
|
|
+package com.cobblemon.mod.fabric.data.generator
|
|
+
|
|
+import com.cobblemon.mod.common.Cobblemon
|
|
+import com.cobblemon.mod.fabric.data.generator.providers.TypeGemsLootTableProvider
|
|
+import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint
|
|
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
|
|
+
|
|
+class CobblemonFabricDataGenerator : DataGeneratorEntrypoint {
|
|
+ override fun onInitializeDataGenerator(generator: FabricDataGenerator?) {
|
|
+ if (generator == null) {
|
|
+ Cobblemon.LOGGER.error("Fabric Data Generator couldn't initialize")
|
|
+ return
|
|
+ }
|
|
+
|
|
+ val pack = generator.createPack()
|
|
+
|
|
+ pack.addProvider(::TypeGemsLootTableProvider);
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/providers/TypeGemsLootTableProvider.kt b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/providers/TypeGemsLootTableProvider.kt
|
|
new file mode 100644
|
|
index 0000000000..6261a5bf05
|
|
--- /dev/null
|
|
+++ b/fabric/src/main/kotlin/com/cobblemon/mod/fabric/data/generator/providers/TypeGemsLootTableProvider.kt
|
|
@@ -0,0 +1,208 @@
|
|
+/*
|
|
+ * 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.fabric.data.generator.providers
|
|
+
|
|
+import com.cobblemon.mod.common.CobblemonBlocks
|
|
+import com.cobblemon.mod.common.CobblemonItems
|
|
+import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
|
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput
|
|
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider
|
|
+import net.minecraft.advancements.critereon.*
|
|
+import net.minecraft.core.HolderLookup
|
|
+import net.minecraft.core.registries.Registries
|
|
+import net.minecraft.world.item.Item
|
|
+import net.minecraft.world.item.enchantment.Enchantments
|
|
+import net.minecraft.world.level.block.Block
|
|
+import net.minecraft.world.level.storage.loot.IntRange
|
|
+import net.minecraft.world.level.storage.loot.LootPool
|
|
+import net.minecraft.world.level.storage.loot.LootTable
|
|
+import net.minecraft.world.level.storage.loot.entries.AlternativesEntry
|
|
+import net.minecraft.world.level.storage.loot.entries.LootItem
|
|
+import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount
|
|
+import net.minecraft.world.level.storage.loot.functions.ApplyExplosionDecay
|
|
+import net.minecraft.world.level.storage.loot.functions.LimitCount
|
|
+import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction
|
|
+import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition
|
|
+import net.minecraft.world.level.storage.loot.predicates.MatchTool
|
|
+import net.minecraft.world.level.storage.loot.providers.number.ConstantValue
|
|
+import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator
|
|
+import java.util.concurrent.CompletableFuture
|
|
+
|
|
+class TypeGemsLootTableProvider(
|
|
+ dataOutput: FabricDataOutput,
|
|
+ val registryLookup: CompletableFuture<HolderLookup.Provider>
|
|
+) : FabricBlockLootTableProvider(dataOutput, registryLookup) {
|
|
+
|
|
+ override fun getName(): String? {
|
|
+ return "Type Gems Loot Tables"
|
|
+ }
|
|
+
|
|
+ override fun generate() {
|
|
+ val lookup = registryLookup.join()
|
|
+
|
|
+ val enchantments = lookup.lookupOrThrow(Registries.ENCHANTMENT)
|
|
+ val silkTouch = enchantments.getOrThrow(Enchantments.SILK_TOUCH)
|
|
+ val fortune = enchantments.getOrThrow(Enchantments.FORTUNE)
|
|
+
|
|
+ CobblemonBlocks.typeGemBlocks().values.forEach { block ->
|
|
+ val gem = BLOCK_TO_GEM[block] ?: return@forEach
|
|
+
|
|
+ add(
|
|
+ block,
|
|
+ LootTable.lootTable().withPool(
|
|
+ LootPool.lootPool().add(
|
|
+ AlternativesEntry.alternatives(
|
|
+ LootItem.lootTableItem(block).`when`(
|
|
+ MatchTool.toolMatches(
|
|
+ ItemPredicate.Builder.item()
|
|
+ .withSubPredicate(
|
|
+ ItemSubPredicates.ENCHANTMENTS,
|
|
+ ItemEnchantmentsPredicate.enchantments(
|
|
+ listOf(
|
|
+ EnchantmentPredicate(
|
|
+ silkTouch,
|
|
+ MinMaxBounds.Ints.atLeast(1)
|
|
+ )
|
|
+ )
|
|
+ ),
|
|
+ )
|
|
+ )
|
|
+ ),
|
|
+ LootItem.lootTableItem(gem)
|
|
+ .apply(
|
|
+ SetItemCountFunction.setCount(
|
|
+ UniformGenerator.between(1f, 2f)
|
|
+ )
|
|
+ )
|
|
+ .apply(
|
|
+ ApplyBonusCount.addOreBonusCount(fortune)
|
|
+ )
|
|
+ .apply(
|
|
+ LimitCount.limitCount(IntRange.upperBound(4))
|
|
+ )
|
|
+ .apply(
|
|
+ ApplyExplosionDecay.explosionDecay()
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ }
|
|
+
|
|
+ CobblemonBlocks.typeGemClusters().values.forEach { cluster ->
|
|
+ val gem = CLUSTER_TO_GEM[cluster] ?: return@forEach
|
|
+
|
|
+ add(
|
|
+ cluster,
|
|
+ LootTable.lootTable().withPool(
|
|
+ LootPool.lootPool().add(
|
|
+ AlternativesEntry.alternatives(
|
|
+ LootItem.lootTableItem(cluster).`when`(
|
|
+ MatchTool.toolMatches(
|
|
+ ItemPredicate.Builder.item()
|
|
+ .withSubPredicate(
|
|
+ ItemSubPredicates.ENCHANTMENTS,
|
|
+ ItemEnchantmentsPredicate.enchantments(
|
|
+ listOf(
|
|
+ EnchantmentPredicate(
|
|
+ silkTouch,
|
|
+ MinMaxBounds.Ints.atLeast(1)
|
|
+ )
|
|
+ )
|
|
+ ),
|
|
+ )
|
|
+ )
|
|
+ ),
|
|
+ LootItem.lootTableItem(gem).`when`(
|
|
+ LootItemBlockStatePropertyCondition
|
|
+ .hasBlockStateProperties(cluster)
|
|
+ .setProperties(
|
|
+ StatePropertiesPredicate.Builder.properties()
|
|
+ .hasProperty(TypeGemClusterBlock.STAGE, 3)
|
|
+ )
|
|
+ )
|
|
+ .apply(
|
|
+ SetItemCountFunction.setCount(
|
|
+ UniformGenerator.between(2f, 3f)
|
|
+ )
|
|
+ )
|
|
+ .apply(
|
|
+ ApplyBonusCount.addOreBonusCount(fortune)
|
|
+ )
|
|
+ .apply(
|
|
+ LimitCount.limitCount(IntRange.upperBound(4))
|
|
+ )
|
|
+ .apply(
|
|
+ ApplyExplosionDecay.explosionDecay()
|
|
+ ),
|
|
+ LootItem.lootTableItem(gem)
|
|
+ .apply(
|
|
+ SetItemCountFunction.setCount(
|
|
+ ConstantValue.exactly(1f)
|
|
+ )
|
|
+ )
|
|
+ .apply(
|
|
+ ApplyExplosionDecay.explosionDecay()
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+ companion object {
|
|
+ val BLOCK_TO_GEM: Map<Block, Item> by lazy {
|
|
+ mapOf(
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL to CobblemonItems.NORMAL_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_FIRE to CobblemonItems.FIRE_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_WATER to CobblemonItems.WATER_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_GRASS to CobblemonItems.GRASS_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC to CobblemonItems.ELECTRIC_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_ICE to CobblemonItems.ICE_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING to CobblemonItems.FIGHTING_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_POISON to CobblemonItems.POISON_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_GROUND to CobblemonItems.GROUND_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_FLYING to CobblemonItems.FLYING_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC to CobblemonItems.PSYCHIC_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_BUG to CobblemonItems.BUG_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_ROCK to CobblemonItems.ROCK_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_GHOST to CobblemonItems.GHOST_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON to CobblemonItems.DRAGON_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_DARK to CobblemonItems.DARK_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_STEEL to CobblemonItems.STEEL_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY to CobblemonItems.FAIRY_GEM
|
|
+ )
|
|
+ }
|
|
+
|
|
+ val CLUSTER_TO_GEM: Map<Block, Item> by lazy {
|
|
+ mapOf(
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL to CobblemonItems.NORMAL_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE to CobblemonItems.FIRE_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_WATER to CobblemonItems.WATER_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS to CobblemonItems.GRASS_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC to CobblemonItems.ELECTRIC_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ICE to CobblemonItems.ICE_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING to CobblemonItems.FIGHTING_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_POISON to CobblemonItems.POISON_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND to CobblemonItems.GROUND_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING to CobblemonItems.FLYING_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC to CobblemonItems.PSYCHIC_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_BUG to CobblemonItems.BUG_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK to CobblemonItems.ROCK_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST to CobblemonItems.GHOST_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON to CobblemonItems.DRAGON_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_DARK to CobblemonItems.DARK_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL to CobblemonItems.STEEL_GEM,
|
|
+ CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY to CobblemonItems.FAIRY_GEM
|
|
+ )
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
\ No newline at end of file
|