Compare commits

...
201 changed files with 6590 additions and 109 deletions
@@ -10,13 +10,18 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.ModAPI;
import com.cobblemon.mod.common.client.render.modification.ModificationVariantModels;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.item.WearableItem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
@@ -48,12 +53,19 @@ public abstract class ItemRendererMixin {
@Shadow public abstract void render(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model);
@Shadow protected abstract void renderModelLists(BakedModel model, ItemStack stack, int light, int overlay, PoseStack matrices, VertexConsumer consumer);
@Inject(
method = "render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
if (!model.isCustomRenderer() && ModificationVariantModels.isTranslucentVariant(stack)) {
cobblemon$renderTranslucentVariant(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, model);
ci.cancel();
return;
}
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
ResourceLocation resourceLocation = null;
if (shouldBe2d) {
@@ -73,6 +85,11 @@ public abstract class ItemRendererMixin {
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void cobblemon$getItemModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
BakedModel variantModel = ModificationVariantModels.itemModel(stack);
if (variantModel != null) {
cir.setReturnValue(variantModel);
return;
}
ResourceLocation resourceLocation = null;
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel3d();
else if (stack.getItem() instanceof PokedexItem pokedexItem) {
@@ -90,6 +107,20 @@ public abstract class ItemRendererMixin {
}
}
@Unique
private void cobblemon$renderTranslucentVariant(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model) {
boolean isDirect = renderMode == ItemDisplayContext.GUI || renderMode.firstPerson();
RenderType renderType = isDirect && Minecraft.useShaderTransparency() ? Sheets.translucentItemSheet() : Sheets.translucentCullBlockSheet();
VertexConsumer consumer = isDirect
? ItemRenderer.getFoilBufferDirect(multiBufferSource, renderType, true, stack.hasFoil())
: ItemRenderer.getFoilBuffer(multiBufferSource, renderType, true, stack.hasFoil());
matrices.pushPose();
model.getTransforms().getTransform(renderMode).apply(leftHanded, matrices);
matrices.translate(-0.5F, -0.5F, -0.5F);
this.renderModelLists(model, stack, light, overlay, matrices, consumer);
matrices.popPose();
}
// this method aims to cover an issue with modernfixes and it's "dynamic resource" causing the usual .equals check to always fail, resulting in a StackOverflow
// this falls back to comparing the model contents (e.g. location of the texture, width and height)
@Unique
@@ -8,7 +8,9 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import com.cobblemon.mod.common.client.render.modification.ModificationVariantModels;
import java.util.List;
import java.util.Map;
import net.minecraft.client.color.block.BlockColors;
@@ -45,5 +47,16 @@ public abstract class ModelLoaderMixin {
var unbakedModel = this.getModel(bakingOverride.getModelLocation());
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
});
ModificationVariantModels.clearCache();
List<ResourceLocation> variantModelLocations = jsonUnbakedModels.keySet().stream()
.map(ModelBakery.MODEL_LISTER::fileToId)
.filter(ModificationVariantModels::isVariantModel)
.toList();
variantModelLocations.forEach(modelLocation -> {
UnbakedModel unbakedModel = this.getModel(modelLocation);
unbakedModel.resolveParents(this::getModel);
this.registerModel(ModificationVariantModels.bakedIdentifier(modelLocation), unbakedModel);
});
Cobblemon.LOGGER.info("Registered {} modification variant models", variantModelLocations.size());
}
}
@@ -36,6 +36,12 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val MOD_VARIANT: BlockEntityType<ModVariantBlockEntity> = this.create(
"mod_variant",
BlockEntityType.Builder.of(::ModVariantBlockEntity, CobblemonBlocks.MOD_VARIANT).build(null)
)
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@@ -22,6 +22,9 @@ 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.modification.ModVariantBlock
import com.cobblemon.mod.common.block.modification.ModFillerBlock
import com.cobblemon.mod.common.block.modification.ModificationVariantBlock
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
@@ -664,7 +667,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
@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 PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().dynamicShape().lightLevel { if (it.getValue(PCBlock.ON) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP || it.getValue(ModificationVariantBlock.MODIFIED))) 13 else 0 }))
val TM_MACHINE = create(
"tm_machine",
@@ -678,6 +681,47 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
)
@JvmField
val MODIFICATION_STATION = create(
"modification_station",
ModificationStationBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.STONE)
.sound(SoundType.STONE)
.strength(3.5F)
.noOcclusion()
.requiresCorrectToolForDrops()
)
)
@JvmField
val MOD_FILLER = create(
"mod_filler",
ModFillerBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.NONE)
.pushReaction(PushReaction.BLOCK)
.strength(2F)
.noOcclusion()
.dynamicShape()
.noLootTable()
.lightLevel { if (it.getValue(BlockStateProperties.LIT)) 13 else 0 }
)
)
@JvmField
val MOD_VARIANT = create(
"mod_variant",
ModVariantBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.STONE)
.sound(SoundType.STONE)
.pushReaction(PushReaction.BLOCK)
.strength(0.8F)
.noOcclusion()
.dynamicShape()
)
)
@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 }))
@@ -686,7 +730,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
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()))
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().dynamicShape().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
// TODO after 1.7
// @JvmField
@@ -99,6 +99,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
@JvmField
val MODIFICATION_VARIANT: DataComponentType<ModificationVariantComponent> = create("modification_variant", DataComponentType.builder<ModificationVariantComponent>()
.persistent(ModificationVariantComponent.CODEC)
.networkSynchronized(ModificationVariantComponent.PACKET_CODEC)
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
@@ -112,6 +118,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:modification_variant"), MODIFICATION_VARIANT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -459,6 +459,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
@JvmField
val MODIFICATION_STATION = blockItem("modification_station", CobblemonBlocks.MODIFICATION_STATION)
@JvmField
val MOD_VARIANT = blockItem("mod_variant", CobblemonBlocks.MOD_VARIANT)
@JvmField
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.block.modificationstation.ModificationStationMenu
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
@@ -22,6 +23,7 @@ object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<R
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
val MODIFICATION_STATION = MenuType.register("modification_station_menu", ::ModificationStationMenu)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
@@ -416,6 +416,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(ModificationVariantRegistrySyncPacket.ID, ModificationVariantRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CallbackRegistrySyncPacket.ID, CallbackRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -626,4 +627,4 @@ object CobblemonNetwork {
fun sendPacketToPlayer(player: ServerPlayer, packet: NetworkPacket<*>) {
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -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.api.modification
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.item.components.ModificationVariantComponent
import com.cobblemon.mod.common.util.rotateShape
import java.util.EnumMap
import net.minecraft.core.Direction
import net.minecraft.core.Vec3i
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.shapes.BooleanOp
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class ModificationVariant(
val identifier: ResourceLocation,
val parent: ResourceLocation,
val block: ResourceLocation,
val model: ResourceLocation,
val blockstateModels: List<ModificationVariantBlockstateModel>,
val cells: List<Vec3i>,
val hitbox: List<AABB>,
val order: Int,
val isTranslucent: Boolean
) {
private val hitboxesByFacing = EnumMap<Direction, VoxelShape>(Direction::class.java)
private val cellHitboxes = hashMapOf<Pair<Direction, Vec3i>, VoxelShape>()
fun targetBlock(): Block? = BuiltInRegistries.BLOCK.getOptional(block).orElse(null)
fun parentItem(): Item? = BuiltInRegistries.ITEM.getOptional(parent).orElse(null)
fun displayName(): MutableComponent = Component.translatable("modification_variant.${identifier.namespace}.${identifier.path}")
fun modelFor(state: BlockState): ResourceLocation = blockstateModels.firstOrNull { it.matches(state) }?.model ?: model
fun allModels(): List<ResourceLocation> = listOf(model) + blockstateModels.map { it.model }
fun createStack(): ItemStack {
val item = targetBlock()?.asItem() ?: return ItemStack.EMPTY
if (item == Items.AIR) {
return ItemStack.EMPTY
}
return ItemStack(item).apply {
set(CobblemonItemComponents.MODIFICATION_VARIANT, ModificationVariantComponent(identifier))
set(DataComponents.ITEM_NAME, displayName())
}
}
fun rotatedCells(facing: Direction): List<Vec3i> = cells.map { rotateCell(it, facing) }
fun fillerCells(facing: Direction): List<Vec3i> = rotatedCells(facing).filter { it != Vec3i.ZERO }
fun cellsMin(): Vec3i = Vec3i(cells.minOf { it.x }, cells.minOf { it.y }, cells.minOf { it.z })
fun cellsMax(): Vec3i = Vec3i(cells.maxOf { it.x }, cells.maxOf { it.y }, cells.maxOf { it.z })
fun hitbox(facing: Direction): VoxelShape = hitboxesByFacing.getOrPut(facing) { rotateShape(Direction.SOUTH, facing, unrotatedHitbox()) }
fun cellHitbox(facing: Direction, cell: Vec3i): VoxelShape = cellHitboxes.getOrPut(facing to cell) {
val shifted = hitbox(facing).move(-cell.x.toDouble(), -cell.y.toDouble(), -cell.z.toDouble())
Shapes.join(shifted, Shapes.block(), BooleanOp.AND)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(identifier)
buffer.writeResourceLocation(parent)
buffer.writeResourceLocation(block)
buffer.writeResourceLocation(model)
buffer.writeCollection(blockstateModels) { buf, blockstateModel -> blockstateModel.encode(buf) }
buffer.writeCollection(cells) { buf, cell ->
buf.writeVarInt(cell.x)
buf.writeVarInt(cell.y)
buf.writeVarInt(cell.z)
}
buffer.writeCollection(hitbox) { buf, box ->
buf.writeDouble(box.minX)
buf.writeDouble(box.minY)
buf.writeDouble(box.minZ)
buf.writeDouble(box.maxX)
buf.writeDouble(box.maxY)
buf.writeDouble(box.maxZ)
}
buffer.writeVarInt(order)
buffer.writeBoolean(isTranslucent)
}
private fun unrotatedHitbox(): VoxelShape {
val boxes = hitbox.ifEmpty { cells.map(::cellBox) }
return boxes.fold(Shapes.empty()) { combined, box ->
Shapes.or(combined, Shapes.box(box.minX / 16.0, box.minY / 16.0, box.minZ / 16.0, box.maxX / 16.0, box.maxY / 16.0, box.maxZ / 16.0))
}
}
companion object {
const val MAX_HEIGHT = 5
const val MAX_DEPTH = 3
const val MAX_HITBOX_BOXES = 64
val MIN_CELL_OFFSET = Vec3i(-2, 0, -2)
val MAX_CELL_OFFSET = Vec3i(2, 4, 2)
fun rotateCell(cell: Vec3i, facing: Direction): Vec3i {
var rotated = cell
repeat(facing.get2DDataValue()) {
rotated = Vec3i(-rotated.z, rotated.y, rotated.x)
}
return rotated
}
fun cellBox(cell: Vec3i): AABB = AABB(
cell.x * 16.0,
cell.y * 16.0,
cell.z * 16.0,
(cell.x + 1) * 16.0,
(cell.y + 1) * 16.0,
(cell.z + 1) * 16.0
)
fun decode(buffer: RegistryFriendlyByteBuf): ModificationVariant = ModificationVariant(
identifier = buffer.readResourceLocation(),
parent = buffer.readResourceLocation(),
block = buffer.readResourceLocation(),
model = buffer.readResourceLocation(),
blockstateModels = buffer.readList { buf -> ModificationVariantBlockstateModel.decode(buf) },
cells = buffer.readList { buf -> Vec3i(buf.readVarInt(), buf.readVarInt(), buf.readVarInt()) },
hitbox = buffer.readList { buf ->
AABB(buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readDouble())
},
order = buffer.readVarInt(),
isTranslucent = buffer.readBoolean()
)
}
}
@@ -0,0 +1,55 @@
/*
* 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.modification
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.Property
class ModificationVariantBlockstateModel(val conditions: Map<String, String>, val model: ResourceLocation) {
fun matches(state: BlockState): Boolean = conditions.all { (propertyName, expectedValue) ->
val property = state.block.stateDefinition.getProperty(propertyName) ?: return@all false
propertyValueName(state, property) == expectedValue
}
fun encode(buffer: FriendlyByteBuf) {
buffer.writeMap(conditions, { buf, key -> buf.writeUtf(key) }, { buf, value -> buf.writeUtf(value) })
buffer.writeResourceLocation(model)
}
companion object {
private const val CONDITION_SEPARATOR = ","
private const val VALUE_SEPARATOR = "="
fun parseConditions(raw: String): Map<String, String>? {
val conditions = linkedMapOf<String, String>()
raw.split(CONDITION_SEPARATOR).forEach { condition ->
val parts = condition.split(VALUE_SEPARATOR)
if (parts.size != 2 || parts[0].isBlank() || parts[1].isBlank()) {
return null
}
conditions[parts[0].trim()] = parts[1].trim()
}
return conditions
}
fun decode(buffer: FriendlyByteBuf): ModificationVariantBlockstateModel {
val conditions = buffer.readMap({ buf -> buf.readUtf() }, { buf -> buf.readUtf() })
return ModificationVariantBlockstateModel(conditions, buffer.readResourceLocation())
}
@Suppress("UNCHECKED_CAST")
private fun propertyValueName(state: BlockState, property: Property<*>): String {
return valueName(state, property as Property<Comparable<Any>>)
}
private fun <T : Comparable<T>> valueName(state: BlockState, property: Property<T>): String = property.getName(state.getValue(property))
}
}
@@ -0,0 +1,107 @@
/*
* 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.modification
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import net.minecraft.core.Vec3i
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.GsonHelper
import net.minecraft.world.phys.AABB
object ModificationVariantParser {
private const val MIN_HITBOX_HORIZONTAL_PIXEL = -32.0
private const val MAX_HITBOX_HORIZONTAL_PIXEL = 48.0
private const val MIN_HITBOX_VERTICAL_PIXEL = 0.0
private const val MAX_HITBOX_VERTICAL_PIXEL = 80.0
fun parse(identifier: ResourceLocation, json: JsonObject): ModificationVariant {
val parent = parseResourceLocation(json, "parent")
val block = if (json.has("block")) parseResourceLocation(json, "block") else parent
return ModificationVariant(
identifier = identifier,
parent = parent,
block = block,
model = parseResourceLocation(json, "model"),
blockstateModels = parseBlockstateModels(json),
cells = parseSize(json),
hitbox = parseHitbox(json),
order = GsonHelper.getAsInt(json, "order", 0),
isTranslucent = GsonHelper.getAsBoolean(json, "translucent", false)
)
}
private fun parseResourceLocation(json: JsonObject, key: String): ResourceLocation {
val raw = GsonHelper.getAsString(json, key)
return ResourceLocation.tryParse(raw) ?: throw JsonParseException("Invalid resource location '$raw' for '$key'")
}
private fun parseBlockstateModels(json: JsonObject): List<ModificationVariantBlockstateModel> {
if (!json.has("blockstates")) {
return emptyList()
}
return GsonHelper.getAsJsonObject(json, "blockstates").entrySet().map { (rawConditions, element) ->
val conditions = ModificationVariantBlockstateModel.parseConditions(rawConditions)
?: throw JsonParseException("Blockstate condition '$rawConditions' must look like 'property=value' with commas between properties")
val raw = GsonHelper.convertToString(element, "blockstate model")
val model = ResourceLocation.tryParse(raw) ?: throw JsonParseException("Invalid resource location '$raw' for blockstate '$rawConditions'")
ModificationVariantBlockstateModel(conditions, model)
}
}
private fun parseSize(json: JsonObject): List<Vec3i> {
if (!json.has("size")) {
return listOf(Vec3i.ZERO)
}
val size = GsonHelper.getAsJsonObject(json, "size")
val height = GsonHelper.getAsInt(size, "height", 1)
val depth = GsonHelper.getAsInt(size, "depth", 1)
if (height !in 1..ModificationVariant.MAX_HEIGHT) {
throw JsonParseException("Size height must be between 1 and ${ModificationVariant.MAX_HEIGHT}, found $height")
}
if (depth !in 1..ModificationVariant.MAX_DEPTH) {
throw JsonParseException("Size depth must be between 1 and ${ModificationVariant.MAX_DEPTH}, found $depth")
}
return (0 until depth).flatMap { z -> (0 until height).map { y -> Vec3i(0, y, z) } }
}
private fun parseHitbox(json: JsonObject): List<AABB> {
if (!json.has("hitbox")) {
return emptyList()
}
val boxes = GsonHelper.getAsJsonArray(json, "hitbox").map { element ->
parseBox(GsonHelper.convertToJsonArray(element, "hitbox box"))
}
if (boxes.size > ModificationVariant.MAX_HITBOX_BOXES) {
throw JsonParseException("Hitbox has ${boxes.size} boxes, the limit is ${ModificationVariant.MAX_HITBOX_BOXES}")
}
return boxes
}
private fun parseBox(coordinates: JsonArray): AABB {
if (coordinates.size() != 6) {
throw JsonParseException("Hitbox boxes need exactly 6 coordinates, found ${coordinates.size()}")
}
val values = coordinates.map { it.asDouble }
if (values.any { !it.isFinite() }) {
throw JsonParseException("Hitbox box $values contains a non finite coordinate")
}
val box = AABB(values[0], values[1], values[2], values[3], values[4], values[5])
if (box.minX >= box.maxX || box.minY >= box.maxY || box.minZ >= box.maxZ) {
throw JsonParseException("Hitbox box $values has a minimum that is not below its maximum")
}
val isHorizontalInRange = listOf(box.minX, box.maxX, box.minZ, box.maxZ).all { it in MIN_HITBOX_HORIZONTAL_PIXEL..MAX_HITBOX_HORIZONTAL_PIXEL }
val isVerticalInRange = listOf(box.minY, box.maxY).all { it in MIN_HITBOX_VERTICAL_PIXEL..MAX_HITBOX_VERTICAL_PIXEL }
if (!isHorizontalInRange || !isVerticalInRange) {
throw JsonParseException("Hitbox box $values is outside the allowed pixel range")
}
return box
}
}
@@ -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.api.modification
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.block.modification.ModificationVariantBlock
import com.cobblemon.mod.common.net.messages.client.data.ModificationVariantRegistrySyncPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
import com.google.gson.reflect.TypeToken
import java.io.File
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.item.ItemStack
object ModificationVariants : JsonDataRegistry<ModificationVariant> {
override val id: ResourceLocation = cobblemonResource("modification_variants")
override val type: PackType = PackType.SERVER_DATA
override val observable = SimpleObservable<ModificationVariants>()
override val typeToken: TypeToken<ModificationVariant> = TypeToken.get(ModificationVariant::class.java)
override val resourcePath = "modification_variants"
override val gson: Gson = GsonBuilder().disableHtmlEscaping().create()
private val variantsByIdentifier = linkedMapOf<ResourceLocation, ModificationVariant>()
private val variantsByParent = hashMapOf<ResourceLocation, List<ModificationVariant>>()
private val variantOrder = compareBy<ModificationVariant> { it.identifier.namespace != Cobblemon.MODID }
.thenBy { it.identifier.namespace }
.thenBy { it.order }
.thenBy { it.identifier.toString() }
override fun reload(manager: ResourceManager) {
val parsed = hashMapOf<ResourceLocation, ModificationVariant>()
manager.listResources(resourcePath) { path -> path.endsWith(JsonDataRegistry.JSON_EXTENSION) }.forEach { (location, resource) ->
val identifier = ResourceLocation.fromNamespaceAndPath(location.namespace, File(location.path).nameWithoutExtension)
try {
resource.openAsReader().use { reader ->
parsed[identifier] = ModificationVariantParser.parse(identifier, JsonParser.parseReader(reader).asJsonObject)
}
} catch (exception: Exception) {
Cobblemon.LOGGER.error("Skipping modification variant {} from {}: {}", identifier, location, exception.message)
}
}
reload(parsed)
}
override fun reload(data: Map<ResourceLocation, ModificationVariant>) {
replaceEntries(data.values.filter(::isRegistered))
}
fun replaceEntries(entries: Collection<ModificationVariant>) {
variantsByIdentifier.clear()
variantsByParent.clear()
entries.sortedWith(variantOrder).forEach { variant -> variantsByIdentifier[variant.identifier] = variant }
variantsByParent.putAll(variantsByIdentifier.values.groupBy { it.parent })
Cobblemon.LOGGER.info("Loaded {} modification variants", variantsByIdentifier.size)
observable.emit(this)
}
override fun sync(player: ServerPlayer) {
ModificationVariantRegistrySyncPacket(all()).sendToPlayer(player)
}
@JvmStatic
fun all(): List<ModificationVariant> = variantsByIdentifier.values.toList()
@JvmStatic
fun forParent(parent: ResourceLocation): List<ModificationVariant> = variantsByParent[parent] ?: emptyList()
@JvmStatic
fun getByIdentifier(identifier: ResourceLocation): ModificationVariant? = variantsByIdentifier[identifier]
@JvmStatic
fun fromStack(stack: ItemStack): ModificationVariant? = stack.get(CobblemonItemComponents.MODIFICATION_VARIANT)?.variant?.let(::getByIdentifier)
@JvmStatic
fun parentOf(stack: ItemStack): ResourceLocation = fromStack(stack)?.parent ?: BuiltInRegistries.ITEM.getKey(stack.item)
@JvmStatic
fun hasVariantsFor(stack: ItemStack): Boolean = !stack.isEmpty && forParent(parentOf(stack)).isNotEmpty()
private fun isRegistered(variant: ModificationVariant): Boolean {
val hasParent = BuiltInRegistries.BLOCK.containsKey(variant.parent) || BuiltInRegistries.ITEM.containsKey(variant.parent)
if (!hasParent) {
Cobblemon.LOGGER.warn("Skipping modification variant {} because parent {} is not a registered block or item", variant.identifier, variant.parent)
return false
}
if (variant.targetBlock() !is ModificationVariantBlock) {
Cobblemon.LOGGER.warn("Skipping modification variant {} because block {} cannot hold modification variants", variant.identifier, variant.block)
return false
}
return true
}
}
@@ -8,7 +8,10 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.block.modification.ModificationVariantBlock
import com.cobblemon.mod.common.block.modification.ModificationVariantStructures
import com.cobblemon.mod.common.item.PokeBallItem
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
@@ -32,26 +35,28 @@ import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.DirectionProperty
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
@Suppress("OVERRIDE_DEPRECATION")
class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
class DisplayCaseBlock(settings: Properties) : ModificationVariantBlock(settings) {
init {
registerDefaultState(stateDefinition.any()
registerDefaultState(unmodifiedState()
.setValue(FACING, Direction.NORTH)
.setValue(ITEM_DIRECTION, Direction.NORTH))
}
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
var blockState = defaultBlockState()
val worldView = ctx.level
val blockPos = ctx.clickedPos
ctx.nearestLookingDirections.forEach { direction ->
override fun stockPlacementState(context: BlockPlaceContext, variant: ModificationVariant?): BlockState? {
val worldView = context.level
val blockPos = context.clickedPos
context.nearestLookingDirections.forEach { direction ->
if (direction.axis.isHorizontal) {
blockState = blockState
val blockState = defaultBlockState()
.setValue(FACING, direction)
.setValue(ITEM_DIRECTION, direction)
as BlockState
if (blockState.canSurvive(worldView, blockPos)) {
val hasRoom = variant == null || ModificationVariantStructures.canPlace(context, variant, direction)
if (hasRoom && blockState.canSurvive(worldView, blockPos)) {
return blockState
}
}
@@ -59,7 +64,7 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
return null
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
override fun addStockProperties(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
builder.add(ITEM_DIRECTION)
}
@@ -124,6 +129,8 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
override fun hasAnalogOutputSignal(state: BlockState): Boolean = true
override fun stockShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = Shapes.block()
override fun codec(): MapCodec<out BaseEntityBlock> {
return CODEC
}
@@ -135,4 +142,4 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
val ITEM_DIRECTION = DirectionProperty.create("item_facing")
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.modificationstation.ModificationStationMenu
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
import net.minecraft.stats.Stats
import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.SimpleMenuProvider
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.ContainerLevelAccess
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
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.DirectionProperty
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.phys.BlockHitResult
class ModificationStationBlock(properties: Properties) : Block(properties) {
override fun codec(): MapCodec<ModificationStationBlock> = CODEC
init {
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH))
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
return defaultBlockState().setValue(FACING, context.horizontalDirection)
}
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
if (level.isClientSide) {
return InteractionResult.SUCCESS
}
player.openMenu(state.getMenuProvider(level, pos))
player.awardStat(Stats.INTERACT_WITH_STONECUTTER)
return InteractionResult.CONSUME
}
override fun getMenuProvider(state: BlockState, level: Level, pos: BlockPos): MenuProvider {
return SimpleMenuProvider(
{ syncId, inventory, _ -> ModificationStationMenu(syncId, inventory, ContainerLevelAccess.create(level, pos)) },
CONTAINER_TITLE
)
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
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 createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
companion object {
val CODEC: MapCodec<ModificationStationBlock> = simpleCodec(::ModificationStationBlock)
val CONTAINER_TITLE: Component = Component.translatable("container.cobblemon.modification_station")
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
}
}
@@ -11,10 +11,13 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.pc.link.ProximityPCLink
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.modification.ModificationVariantBlock
import com.cobblemon.mod.common.block.modification.ModificationVariantStructures
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.lang
@@ -50,7 +53,7 @@ import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterloggedBlock {
class PCBlock(properties: Properties): ModificationVariantBlock(properties), SimpleWaterloggedBlock {
companion object {
val CODEC = simpleCodec(::PCBlock)
val PART = EnumProperty.create("part", PCPart::class.java)
@@ -129,7 +132,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
}
init {
registerDefaultState(stateDefinition.any()
registerDefaultState(unmodifiedState()
.setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH)
.setValue(PART, PCPart.BOTTOM)
.setValue(ON, false)
@@ -140,7 +143,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = if (blockState.getValue(PART) == PCPart.BOTTOM) PCBlockEntity(blockPos, blockState) else null
override fun getShape(
override fun stockShape(
blockState: BlockState,
blockGetter: BlockGetter,
blockPos: BlockPos,
@@ -181,7 +184,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
private fun isBase(state: BlockState): Boolean = state.getValue(PART) == PCPart.BOTTOM
override fun setPlacedBy(
override fun onStockPlaced(
world: Level,
pos: BlockPos,
state: BlockState,
@@ -197,10 +200,11 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
state.updateNeighbourShapes(world, pos, 3)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState? {
override fun stockPlacementState(blockPlaceContext: BlockPlaceContext, variant: ModificationVariant?): BlockState? {
val abovePosition = blockPlaceContext.clickedPos.above()
val world = blockPlaceContext.level
if (world.getBlockState(abovePosition).canBeReplaced(blockPlaceContext) && !world.isOutsideBuildHeight(abovePosition)) {
val hasRoom = variant != null || (world.getBlockState(abovePosition).canBeReplaced(blockPlaceContext) && !world.isOutsideBuildHeight(abovePosition))
if (hasRoom) {
return defaultBlockState()
.setValue(HorizontalDirectionalBlock.FACING, blockPlaceContext.horizontalDirection)
.setValue(PART, PCPart.BOTTOM)
@@ -210,6 +214,8 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
return null
}
override fun anchorPosition(state: BlockState, pos: BlockPos): BlockPos = getBasePosition(state, pos)
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
val blockPos = pos.below()
val blockState = world.getBlockState(blockPos)
@@ -238,7 +244,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
pathComputationType: PathComputationType
): Boolean = false
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
override fun addStockProperties(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HorizontalDirectionalBlock.FACING, NATURAL)
builder.add(PART)
builder.add(ON)
@@ -256,10 +262,6 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
}
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
}
override fun useWithoutItem(
blockState: BlockState,
world: Level,
@@ -271,8 +273,10 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
val basePos = getBasePosition(blockState, blockPos)
// Remove any duplicate block entities that may exist
world.getBlockEntity(basePos.above())?.setRemoved()
if (!ModificationVariantStructures.isModified(blockState)) {
// Remove any duplicate block entities that may exist
world.getBlockEntity(basePos.above())?.setRemoved()
}
val baseEntity = world.getBlockEntity(basePos)
if (baseEntity !is PCBlockEntity) return InteractionResult.SUCCESS
@@ -320,6 +324,9 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
if (ModificationVariantStructures.isModified(state)) {
return state
}
val isPC = neighborState.`is`(this)
val part = state.getValue(PART)
@@ -331,4 +338,4 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
return state
}
}
}
@@ -16,9 +16,6 @@ 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.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.sounds.SoundSource
import net.minecraft.world.ContainerHelper
import net.minecraft.world.InteractionHand
@@ -29,10 +26,9 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISPLAY_CASE, pos, state),
class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : ModificationVariantBlockEntity(CobblemonBlockEntities.DISPLAY_CASE, pos, state),
WorldlyContainer {
val inv: NonNullList<ItemStack> = NonNullList.withSize(1, ItemStack.EMPTY)
@@ -109,14 +105,6 @@ class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cob
ContainerHelper.loadAllItems(nbt, inv, registryLookup)
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
return this.saveWithoutMetadata(registryLookup)
}
private fun onItemUpdated(world: Level, oldState: BlockState, newState: BlockState) {
world.sendBlockUpdated(blockPos, oldState, newState, Block.UPDATE_CLIENTS)
world.updateNeighbourForOutputSignal(blockPos, world.getBlockState(blockPos).block)
@@ -176,4 +164,4 @@ class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cob
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, dir: Direction?): Boolean = (getStack().isEmpty)
override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean = (direction == Direction.DOWN)
}
}
@@ -0,0 +1,15 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.BlockState
class ModVariantBlockEntity(pos: BlockPos, state: BlockState) : ModificationVariantBlockEntity(CobblemonBlockEntities.MOD_VARIANT, pos, state)
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.api.modification.ModificationVariants
import com.cobblemon.mod.common.item.components.ModificationVariantComponent
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.core.component.DataComponentMap
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
abstract class ModificationVariantBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BlockEntity(type, pos, state) {
var variantIdentifier: ResourceLocation? = null
fun variant(): ModificationVariant? = variantIdentifier?.let(ModificationVariants::getByIdentifier)
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
variantIdentifier?.let { tag.putString(VARIANT_TAG, it.toString()) }
}
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
variantIdentifier = if (tag.contains(VARIANT_TAG)) ResourceLocation.tryParse(tag.getString(VARIANT_TAG)) else null
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag = saveWithoutMetadata(registries)
override fun applyImplicitComponents(componentInput: DataComponentInput) {
super.applyImplicitComponents(componentInput)
variantIdentifier = componentInput.get(CobblemonItemComponents.MODIFICATION_VARIANT)?.variant
}
override fun collectImplicitComponents(builder: DataComponentMap.Builder) {
super.collectImplicitComponents(builder)
val identifier = variantIdentifier ?: return
builder.set(CobblemonItemComponents.MODIFICATION_VARIANT, ModificationVariantComponent(identifier))
ModificationVariants.getByIdentifier(identifier)?.let { builder.set(DataComponents.ITEM_NAME, it.displayName()) }
}
override fun removeComponentsFromTag(tag: CompoundTag) {
super.removeComponentsFromTag(tag)
tag.remove(VARIANT_TAG)
}
companion object {
const val VARIANT_TAG = "ModificationVariant"
}
}
@@ -13,13 +13,13 @@ import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.pc.link.ProximityPCLink
import com.cobblemon.mod.common.block.PCBlock
import com.cobblemon.mod.common.block.modification.ModificationVariantStructures
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.entity.EntityTypeTest
@@ -28,7 +28,7 @@ import net.minecraft.world.phys.AABB
class PCBlockEntity(
blockPos: BlockPos,
blockState: BlockState
) : BlockEntity(CobblemonBlockEntities.PC, blockPos, blockState) {
) : ModificationVariantBlockEntity(CobblemonBlockEntities.PC, blockPos, blockState) {
companion object {
internal val TICKER = BlockEntityTicker<PCBlockEntity> { world, _, _, blockEntity ->
@@ -43,6 +43,10 @@ class PCBlockEntity(
val world = level
if (world != null && !world.isClientSide) {
if (ModificationVariantStructures.isModified(blockState)) {
toggleModifiedPCOn(world, on)
return
}
val posBottom = pcBlock.getBasePosition(blockState, blockPos)
val stateBottom = world.getBlockState(posBottom)
@@ -69,6 +73,14 @@ class PCBlockEntity(
}
}
private fun toggleModifiedPCOn(world: Level, on: Boolean) {
val state = world.getBlockState(blockPos)
if (state.hasProperty(PCBlock.ON) && state.getValue(PCBlock.ON) != on) {
world.setBlockAndUpdate(blockPos, state.setValue(PCBlock.ON, on))
}
ModificationVariantStructures.setFillersLit(world, blockPos, on)
}
private fun isPlayerViewing(player: Player): Boolean {
val pcLink = PCLinkManager.getLink(player.uuid)
return pcLink != null
@@ -0,0 +1,172 @@
/*
* 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.modification
import com.cobblemon.mod.common.CobblemonBlocks
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.Vec3i
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.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
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.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.IntegerProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
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
class ModFillerBlock(properties: Properties) : Block(properties), SimpleWaterloggedBlock {
init {
registerDefaultState(stateDefinition.any()
.setValue(OFFSET_X, HORIZONTAL_OFFSET_SHIFT)
.setValue(OFFSET_Y, 0)
.setValue(OFFSET_Z, HORIZONTAL_OFFSET_SHIFT)
.setValue(BlockStateProperties.LIT, false)
.setValue(BlockStateProperties.WATERLOGGED, false))
}
override fun codec(): MapCodec<ModFillerBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(OFFSET_X, OFFSET_Y, OFFSET_Z, BlockStateProperties.LIT, BlockStateProperties.WATERLOGGED)
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.INVISIBLE
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val anchorPos = anchorPosition(state, pos)
val anchorState = level.getBlockState(anchorPos)
if (anchorState.block !is ModificationVariantBlock) {
return Shapes.empty()
}
val variant = ModificationVariantStructures.variantAt(level, anchorPos) ?: return Shapes.block()
return variant.cellHitbox(ModificationVariantStructures.facingOf(anchorState), cellOffset(state))
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return getShape(state, level, pos, context)
}
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
val anchorPos = anchorPosition(state, pos)
val anchorState = level.getBlockState(anchorPos)
if (anchorState.block !is ModificationVariantBlock) {
return InteractionResult.PASS
}
return anchorState.useWithoutItem(level, player, hit.withPosition(anchorPos))
}
override fun useItemOn(
stack: ItemStack,
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hand: InteractionHand,
hit: BlockHitResult
): ItemInteractionResult {
val anchorPos = anchorPosition(state, pos)
val anchorState = level.getBlockState(anchorPos)
if (anchorState.block !is ModificationVariantBlock) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
}
return anchorState.useItemOn(stack, level, player, hand, hit.withPosition(anchorPos))
}
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
val anchorPos = anchorPosition(state, pos)
val anchorState = level.getBlockState(anchorPos)
return if (anchorState.block is ModificationVariantBlock) anchorState.block.getCloneItemStack(level, anchorPos, anchorState) else ItemStack.EMPTY
}
override fun playerWillDestroy(level: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
val anchorPos = anchorPosition(state, pos)
val anchorState = level.getBlockState(anchorPos)
if (!level.isClientSide && anchorState.block is ModificationVariantBlock) {
val shouldDrop = !player.isCreative && player.hasCorrectToolForDrops(anchorState)
level.destroyBlock(anchorPos, shouldDrop, player)
}
return super.playerWillDestroy(level, pos, state, player)
}
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, isMoved: Boolean) {
if (!newState.`is`(this)) {
val anchorPos = anchorPosition(state, pos)
if (level.getBlockState(anchorPos).block is ModificationVariantBlock) {
level.destroyBlock(anchorPos, true)
}
}
super.onRemove(state, level, pos, newState, isMoved)
}
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
return level.getBlockState(anchorPosition(state, pos)).block is ModificationVariantBlock
}
override fun updateShape(
state: BlockState,
direction: Direction,
neighborState: BlockState,
level: LevelAccessor,
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
if (state.getValue(BlockStateProperties.WATERLOGGED)) {
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level))
}
return if (canSurvive(state, level, pos)) state else ModificationVariantStructures.replacementFor(state)
}
override fun getFluidState(state: BlockState): FluidState {
return if (state.getValue(BlockStateProperties.WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(state)
}
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
companion object {
val CODEC: MapCodec<ModFillerBlock> = simpleCodec(::ModFillerBlock)
private const val HORIZONTAL_OFFSET_SHIFT = 2
val OFFSET_X: IntegerProperty = IntegerProperty.create("offset_x", 0, 4)
val OFFSET_Y: IntegerProperty = IntegerProperty.create("offset_y", 0, 4)
val OFFSET_Z: IntegerProperty = IntegerProperty.create("offset_z", 0, 4)
fun stateFor(cell: Vec3i, isLit: Boolean, isWaterlogged: Boolean): BlockState {
return CobblemonBlocks.MOD_FILLER.defaultBlockState()
.setValue(OFFSET_X, cell.x + HORIZONTAL_OFFSET_SHIFT)
.setValue(OFFSET_Y, cell.y)
.setValue(OFFSET_Z, cell.z + HORIZONTAL_OFFSET_SHIFT)
.setValue(BlockStateProperties.LIT, isLit)
.setValue(BlockStateProperties.WATERLOGGED, isWaterlogged)
}
fun cellOffset(state: BlockState): Vec3i = Vec3i(
state.getValue(OFFSET_X) - HORIZONTAL_OFFSET_SHIFT,
state.getValue(OFFSET_Y),
state.getValue(OFFSET_Z) - HORIZONTAL_OFFSET_SHIFT
)
fun anchorPosition(state: BlockState, pos: BlockPos): BlockPos = pos.subtract(cellOffset(state))
}
}
@@ -0,0 +1,62 @@
/*
* 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.modification
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.block.entity.ModVariantBlockEntity
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
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.pathfinder.PathComputationType
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
class ModVariantBlock(properties: Properties) : ModificationVariantBlock(properties) {
init {
registerDefaultState(unmodifiedState().setValue(FACING, Direction.SOUTH))
}
override fun codec(): MapCodec<ModVariantBlock> = CODEC
override fun addStockProperties(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun stockPlacementState(context: BlockPlaceContext, variant: ModificationVariant?): BlockState? {
if (variant == null) {
return null
}
return defaultBlockState().setValue(FACING, context.horizontalDirection)
}
override fun stockShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = Shapes.block()
override fun newBlockEntity(pos: BlockPos, state: BlockState) = ModVariantBlockEntity(pos, state)
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.INVISIBLE
override fun rotate(state: BlockState, rotation: Rotation): BlockState = state.setValue(FACING, rotation.rotate(state.getValue(FACING)))
override fun mirror(state: BlockState, mirror: Mirror): BlockState = state.rotate(mirror.getRotation(state.getValue(FACING)))
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
companion object {
val CODEC: MapCodec<ModVariantBlock> = simpleCodec(::ModVariantBlock)
}
}
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.modification
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.api.modification.ModificationVariants
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
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.LevelReader
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
abstract class ModificationVariantBlock(properties: Properties) : BaseEntityBlock(properties) {
protected abstract fun stockShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape
protected abstract fun stockPlacementState(context: BlockPlaceContext, variant: ModificationVariant?): BlockState?
protected abstract fun addStockProperties(builder: StateDefinition.Builder<Block, BlockState>)
protected open fun onStockPlaced(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {}
protected open fun anchorPosition(state: BlockState, pos: BlockPos): BlockPos = pos
protected fun unmodifiedState(): BlockState = stateDefinition.any().setValue(MODIFIED, false)
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(MODIFIED)
addStockProperties(builder)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val variant = ModificationVariants.fromStack(context.itemInHand)
val stockState = stockPlacementState(context, variant) ?: return null
if (variant != null && !ModificationVariantStructures.canPlace(context, variant, ModificationVariantStructures.facingOf(stockState))) {
return null
}
return stockState.setValue(MODIFIED, variant != null)
}
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
super.setPlacedBy(level, pos, state, placer, stack)
val variant = ModificationVariants.fromStack(stack)
if (variant != null && ModificationVariantStructures.isModified(state)) {
ModificationVariantStructures.placeFillers(level, pos, variant, ModificationVariantStructures.facingOf(state), isLit = false)
return
}
onStockPlaced(level, pos, state, placer, stack)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return ModificationVariantStructures.anchorShape(level, pos, state, stockShape(state, level, pos, context))
}
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
return ModificationVariantStructures.withVariantComponents(level, anchorPosition(state, pos), super.getCloneItemStack(level, pos, state))
}
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block) && ModificationVariantStructures.isModified(state)) {
ModificationVariantStructures.removeFillers(level, pos)
}
super.onRemove(state, level, pos, newState, moved)
}
companion object {
val MODIFIED: BooleanProperty = BooleanProperty.create("modified")
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
}
}
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.modification
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.block.entity.ModificationVariantBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.Vec3i
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.VoxelShape
object ModificationVariantStructures {
fun variantAt(level: BlockGetter, pos: BlockPos): ModificationVariant? = (level.getBlockEntity(pos) as? ModificationVariantBlockEntity)?.variant()
fun isModified(state: BlockState): Boolean = state.hasProperty(ModificationVariantBlock.MODIFIED) && state.getValue(ModificationVariantBlock.MODIFIED)
fun facingOf(state: BlockState): Direction = if (state.hasProperty(ModificationVariantBlock.FACING)) state.getValue(ModificationVariantBlock.FACING) else Direction.SOUTH
fun canPlace(context: BlockPlaceContext, variant: ModificationVariant, facing: Direction): Boolean {
val level = context.level
return variant.fillerCells(facing).all { cell ->
val cellPos = context.clickedPos.offset(cell)
!level.isOutsideBuildHeight(cellPos) && level.getBlockState(cellPos).canBeReplaced(context)
}
}
fun placeFillers(level: LevelAccessor, anchorPos: BlockPos, variant: ModificationVariant, facing: Direction, isLit: Boolean) {
variant.fillerCells(facing).forEach { cell ->
val cellPos = anchorPos.offset(cell)
val isWaterlogged = level.getFluidState(cellPos).type == Fluids.WATER
level.setBlock(cellPos, ModFillerBlock.stateFor(cell, isLit, isWaterlogged), Block.UPDATE_ALL)
}
}
fun removeFillers(level: LevelAccessor, anchorPos: BlockPos) {
forEachFiller(level, anchorPos) { fillerPos, fillerState ->
level.setBlock(fillerPos, replacementFor(fillerState), Block.UPDATE_ALL or Block.UPDATE_SUPPRESS_DROPS)
}
}
fun setFillersLit(level: LevelAccessor, anchorPos: BlockPos, isLit: Boolean) {
forEachFiller(level, anchorPos) { fillerPos, fillerState ->
if (fillerState.getValue(BlockStateProperties.LIT) != isLit) {
level.setBlock(fillerPos, fillerState.setValue(BlockStateProperties.LIT, isLit), Block.UPDATE_ALL)
}
}
}
fun anchorShape(level: BlockGetter, pos: BlockPos, state: BlockState, fallback: VoxelShape): VoxelShape {
if (!isModified(state)) {
return fallback
}
val variant = variantAt(level, pos) ?: return fallback
return variant.cellHitbox(facingOf(state), Vec3i.ZERO)
}
fun withVariantComponents(level: BlockGetter, pos: BlockPos, stack: ItemStack): ItemStack {
val blockEntity = level.getBlockEntity(pos) ?: return stack
stack.applyComponents(blockEntity.collectComponents())
return stack
}
fun replacementFor(state: BlockState): BlockState {
val isWaterlogged = state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(BlockStateProperties.WATERLOGGED)
return if (isWaterlogged) Blocks.WATER.defaultBlockState() else Blocks.AIR.defaultBlockState()
}
private fun forEachFiller(level: LevelAccessor, anchorPos: BlockPos, action: (BlockPos, BlockState) -> Unit) {
val searchMin = anchorPos.offset(ModificationVariant.MIN_CELL_OFFSET)
val searchMax = anchorPos.offset(ModificationVariant.MAX_CELL_OFFSET)
BlockPos.betweenClosed(searchMin, searchMax).forEach { searchPos ->
val state = level.getBlockState(searchPos)
if (state.block is ModFillerBlock && ModFillerBlock.anchorPosition(state, searchPos) == anchorPos) {
action(searchPos.immutable(), state)
}
}
}
}
@@ -0,0 +1,209 @@
/*
* 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.modificationstation
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonMenuType
import com.cobblemon.mod.common.api.modification.ModificationVariants
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
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.AbstractContainerMenu
import net.minecraft.world.inventory.ContainerLevelAccess
import net.minecraft.world.inventory.DataSlot
import net.minecraft.world.inventory.ResultContainer
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
class ModificationStationMenu(
containerId: Int,
playerInventory: Inventory,
private val access: ContainerLevelAccess
) : AbstractContainerMenu(CobblemonMenuType.MODIFICATION_STATION, containerId) {
private val selectedOutputIndex = DataSlot.standalone()
private var outputs: List<ItemStack> = emptyList()
private var lastInput: ItemStack = ItemStack.EMPTY
private var lastSoundTime: Long = 0L
private var slotUpdateListener: Runnable = Runnable {}
val container: Container = object : SimpleContainer(1) {
override fun setChanged() {
super.setChanged()
this@ModificationStationMenu.slotsChanged(this)
slotUpdateListener.run()
}
}
private val resultContainer = ResultContainer()
val inputSlot: Slot
val resultSlot: Slot
init {
inputSlot = addSlot(object : Slot(container, INPUT_SLOT, 20, 33) {
override fun mayPlace(stack: ItemStack): Boolean = ModificationVariants.hasVariantsFor(stack)
})
resultSlot = addSlot(object : Slot(resultContainer, RESULT_SLOT, 143, 33) {
override fun mayPlace(stack: ItemStack): Boolean = false
override fun onTake(player: Player, stack: ItemStack) {
stack.onCraftedBy(player.level(), player, stack.count)
inputSlot.remove(1)
if (inputSlot.hasItem()) {
setupResultSlot()
} else {
selectedOutputIndex.set(-1)
this.set(ItemStack.EMPTY)
}
access.execute { level, pos ->
val gameTime = level.gameTime
if (lastSoundTime != gameTime) {
level.playSound(null, pos, SoundEvents.UI_STONECUTTER_TAKE_RESULT, SoundSource.BLOCKS, 1.0F, 1.0F)
lastSoundTime = gameTime
}
}
super.onTake(player, stack)
}
})
for (row in 0 until 3) {
for (column in 0 until 9) {
addSlot(Slot(playerInventory, column + row * 9 + 9, 8 + column * 18, 84 + row * 18))
}
}
for (hotbarSlot in 0 until 9) {
addSlot(Slot(playerInventory, hotbarSlot, 8 + hotbarSlot * 18, 142))
}
addDataSlot(selectedOutputIndex)
}
constructor(containerId: Int, playerInventory: Inventory) : this(containerId, playerInventory, ContainerLevelAccess.NULL)
fun getSelectedOutputIndex(): Int = selectedOutputIndex.get()
fun getOutputs(): List<ItemStack> = outputs
fun hasInputItem(): Boolean = inputSlot.hasItem() && outputs.isNotEmpty()
fun registerUpdateListener(listener: Runnable) {
slotUpdateListener = listener
}
override fun stillValid(player: Player): Boolean = stillValid(access, player, CobblemonBlocks.MODIFICATION_STATION)
override fun clickMenuButton(player: Player, id: Int): Boolean {
if (isValidOutputIndex(id)) {
selectedOutputIndex.set(id)
setupResultSlot()
}
return true
}
override fun slotsChanged(container: Container) {
val inputStack = inputSlot.item
if (!ItemStack.isSameItemSameComponents(inputStack, lastInput)) {
lastInput = inputStack.copy()
setupOutputs(inputStack)
}
}
override fun canTakeItemForPickAll(stack: ItemStack, slot: Slot): Boolean {
return slot.container !== resultContainer && super.canTakeItemForPickAll(stack, slot)
}
override fun quickMoveStack(player: Player, index: Int): ItemStack {
val slot = slots[index]
if (!slot.hasItem()) {
return ItemStack.EMPTY
}
val movedStack = slot.item
val copiedStack = movedStack.copy()
val wasMoved = when {
index == RESULT_SLOT -> {
movedStack.item.onCraftedBy(movedStack, player.level(), player)
moveItemStackTo(movedStack, INV_SLOT_START, USE_ROW_SLOT_END, true).also { if (it) slot.onQuickCraft(movedStack, copiedStack) }
}
index == INPUT_SLOT -> moveItemStackTo(movedStack, INV_SLOT_START, USE_ROW_SLOT_END, false)
ModificationVariants.hasVariantsFor(movedStack) -> moveItemStackTo(movedStack, INPUT_SLOT, RESULT_SLOT, false)
index in INV_SLOT_START until INV_SLOT_END -> moveItemStackTo(movedStack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)
index in USE_ROW_SLOT_START until USE_ROW_SLOT_END -> moveItemStackTo(movedStack, INV_SLOT_START, INV_SLOT_END, false)
else -> false
}
if (!wasMoved) {
return ItemStack.EMPTY
}
if (movedStack.isEmpty) {
slot.setByPlayer(ItemStack.EMPTY)
} else {
slot.setChanged()
}
if (movedStack.count == copiedStack.count) {
return ItemStack.EMPTY
}
slot.onTake(player, movedStack)
broadcastChanges()
return copiedStack
}
override fun removed(player: Player) {
super.removed(player)
resultContainer.removeItemNoUpdate(1)
access.execute { _, _ -> clearContainer(player, container) }
}
private fun isValidOutputIndex(index: Int): Boolean = index in outputs.indices
private fun setupOutputs(inputStack: ItemStack) {
outputs = emptyList()
selectedOutputIndex.set(-1)
resultSlot.set(ItemStack.EMPTY)
if (inputStack.isEmpty) {
return
}
val parent = ModificationVariants.parentOf(inputStack)
val variantOutputs = ModificationVariants.forParent(parent).map { it.createStack() }.filterNot { it.isEmpty }
if (variantOutputs.isEmpty()) {
return
}
outputs = (listOfNotNull(stockOutputFor(parent)) + variantOutputs)
.filterNot { ItemStack.isSameItemSameComponents(it, inputStack) }
}
private fun stockOutputFor(parent: ResourceLocation): ItemStack? = BuiltInRegistries.ITEM.getOptional(parent).map(::ItemStack).orElse(null)
private fun setupResultSlot() {
val selectedIndex = selectedOutputIndex.get()
resultSlot.set(if (isValidOutputIndex(selectedIndex)) outputs[selectedIndex].copy() else ItemStack.EMPTY)
broadcastChanges()
}
companion object {
const val INPUT_SLOT = 0
const val RESULT_SLOT = 1
private const val INV_SLOT_START = 2
private const val INV_SLOT_END = 29
private const val USE_ROW_SLOT_START = 29
private const val USE_ROW_SLOT_END = 38
}
}
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerDat
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.ModVariantBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.TintBlockEntity
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
@@ -28,6 +30,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.modificationstation.ModificationStationScreen
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
@@ -39,6 +42,7 @@ import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.ModificationVariantRenderer
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
@@ -252,6 +256,7 @@ object CobblemonClient {
RenderType.cutout(),
CobblemonBlocks.GILDED_CHEST,
CobblemonBlocks.FOSSIL_ANALYZER,
CobblemonBlocks.MODIFICATION_STATION,
CobblemonBlocks.APRICORN_DOOR,
CobblemonBlocks.APRICORN_TRAPDOOR,
CobblemonBlocks.APRICORN_SIGN,
@@ -421,6 +426,7 @@ object CobblemonClient {
private fun registerMenuScreens() {
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
MenuScreens.register(CobblemonMenuType.MODIFICATION_STATION, ::ModificationStationScreen)
}
private fun registerBlockEntityRenderers() {
@@ -441,6 +447,8 @@ object CobblemonClient {
)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.PC) { ModificationVariantRenderer<PCBlockEntity>(it) }
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.MOD_VARIANT) { ModificationVariantRenderer<ModVariantBlockEntity>(it) }
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
@@ -0,0 +1,175 @@
/*
* 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.modificationstation
import com.cobblemon.mod.common.block.modificationstation.ModificationStationMenu
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.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvents
import net.minecraft.util.Mth
import net.minecraft.world.entity.player.Inventory
class ModificationStationScreen(
menu: ModificationStationMenu,
playerInventory: Inventory,
title: Component
) : AbstractContainerScreen<ModificationStationMenu>(menu, playerInventory, title) {
private var scrollOffset = 0.0F
private var isScrolling = false
private var startIndex = 0
private var hasOutputs = false
init {
menu.registerUpdateListener { onOutputsChanged() }
titleLabelY -= 1
}
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
super.render(graphics, mouseX, mouseY, partialTick)
renderTooltip(graphics, mouseX, mouseY)
}
override fun renderBg(graphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight)
val scrollerY = (SCROLLER_TRAVEL * scrollOffset).toInt()
val scrollerSprite = if (isScrollBarActive()) SCROLLER_SPRITE else SCROLLER_DISABLED_SPRITE
graphics.blitSprite(scrollerSprite, leftPos + SCROLLER_X, topPos + SCROLLER_Y + scrollerY, SCROLLER_WIDTH, SCROLLER_HEIGHT)
renderOutputButtons(graphics, mouseX, mouseY)
renderOutputItems(graphics)
}
override fun renderTooltip(graphics: GuiGraphics, x: Int, y: Int) {
super.renderTooltip(graphics, x, y)
if (!hasOutputs) {
return
}
val hoveredIndex = outputIndexAt(x.toDouble(), y.toDouble()) ?: return
graphics.renderTooltip(font, menu.getOutputs()[hoveredIndex], x, y)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
isScrolling = false
val client = minecraft ?: return super.mouseClicked(mouseX, mouseY, button)
if (!hasOutputs) {
return super.mouseClicked(mouseX, mouseY, button)
}
val clickedIndex = outputIndexAt(mouseX, mouseY)
val player = client.player
if (clickedIndex != null && player != null && menu.clickMenuButton(player, clickedIndex)) {
client.soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_STONECUTTER_SELECT_RECIPE, 1.0F))
client.gameMode?.handleInventoryButtonClick(menu.containerId, clickedIndex)
return true
}
val scrollerLeft = leftPos + SCROLLER_X
val scrollerTop = topPos + SCROLLER_TRACK_Y
if (mouseX >= scrollerLeft && mouseX < scrollerLeft + SCROLLER_WIDTH && mouseY >= scrollerTop && mouseY < scrollerTop + SCROLLER_TRACK_HEIGHT) {
isScrolling = true
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, dragX: Double, dragY: Double): Boolean {
if (isScrolling && isScrollBarActive()) {
val trackTop = topPos + SCROLLER_TRACK_Y
scrollOffset = Mth.clamp((mouseY.toFloat() - trackTop - SCROLLER_HEIGHT / 2F) / (SCROLLER_TRACK_HEIGHT - SCROLLER_HEIGHT), 0.0F, 1.0F)
startIndex = (scrollOffset * offscreenRows() + 0.5F).toInt() * GRID_COLUMNS
return true
}
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY)
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
if (isScrollBarActive()) {
val rows = offscreenRows()
scrollOffset = Mth.clamp(scrollOffset - scrollY.toFloat() / rows, 0.0F, 1.0F)
startIndex = (scrollOffset * rows + 0.5F).toInt() * GRID_COLUMNS
}
return true
}
private fun renderOutputButtons(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
visibleOutputIndices().forEach { index ->
val cellX = cellX(index)
val cellY = cellY(index)
val isHovered = mouseX in cellX until cellX + CELL_WIDTH && mouseY in cellY until cellY + CELL_HEIGHT
val sprite = when {
index == menu.getSelectedOutputIndex() -> SELECTED_CELL_SPRITE
isHovered -> HIGHLIGHTED_CELL_SPRITE
else -> CELL_SPRITE
}
graphics.blitSprite(sprite, cellX, cellY - 1, CELL_WIDTH, CELL_HEIGHT)
}
}
private fun renderOutputItems(graphics: GuiGraphics) {
val outputs = menu.getOutputs()
visibleOutputIndices().forEach { index -> graphics.renderItem(outputs[index], cellX(index), cellY(index)) }
}
private fun visibleOutputIndices(): IntRange {
val lastVisible = minOf(startIndex + GRID_COLUMNS * GRID_ROWS, menu.getOutputs().size)
return startIndex until lastVisible
}
private fun outputIndexAt(mouseX: Double, mouseY: Double): Int? {
return visibleOutputIndices().firstOrNull { index ->
val cellX = cellX(index)
val cellY = cellY(index)
mouseX >= cellX && mouseX < cellX + CELL_WIDTH && mouseY >= cellY && mouseY < cellY + CELL_HEIGHT
}
}
private fun cellX(index: Int): Int = leftPos + GRID_X + (index - startIndex) % GRID_COLUMNS * CELL_WIDTH
private fun cellY(index: Int): Int = topPos + GRID_Y + (index - startIndex) / GRID_COLUMNS * CELL_HEIGHT + 2
private fun isScrollBarActive(): Boolean = hasOutputs && menu.getOutputs().size > GRID_COLUMNS * GRID_ROWS
private fun offscreenRows(): Int = (menu.getOutputs().size + GRID_COLUMNS - 1) / GRID_COLUMNS - GRID_ROWS
private fun onOutputsChanged() {
hasOutputs = menu.hasInputItem()
if (!hasOutputs) {
scrollOffset = 0.0F
startIndex = 0
}
}
companion object {
private val SCROLLER_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/scroller")
private val SCROLLER_DISABLED_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/scroller_disabled")
private val SELECTED_CELL_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe_selected")
private val HIGHLIGHTED_CELL_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe_highlighted")
private val CELL_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe")
private val BACKGROUND = ResourceLocation.withDefaultNamespace("textures/gui/container/stonecutter.png")
private const val GRID_COLUMNS = 4
private const val GRID_ROWS = 3
private const val CELL_WIDTH = 16
private const val CELL_HEIGHT = 18
private const val GRID_X = 52
private const val GRID_Y = 14
private const val SCROLLER_X = 119
private const val SCROLLER_Y = 15
private const val SCROLLER_WIDTH = 12
private const val SCROLLER_HEIGHT = 15
private const val SCROLLER_TRAVEL = 41.0F
private const val SCROLLER_TRACK_Y = 9
private const val SCROLLER_TRACK_HEIGHT = 54
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.block.DisplayCaseBlock
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.client.render.modification.ModificationVariantModels
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.item.PokedexItem
import com.mojang.blaze3d.vertex.PoseStack
@@ -34,6 +35,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
val coinPouchStack: ItemStack by lazy { ItemStack(
CobblemonItems.RELIC_COIN_POUCH
).also { it.set(DataComponents.CUSTOM_MODEL_DATA, CustomModelData(1)) } }
override fun render(
entity: DisplayCaseBlockEntity,
tickDelta: Float,
@@ -42,11 +44,13 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
light: Int,
overlay: Int
) {
ModificationVariantModels.renderBlock(entity, matrices, vertexConsumers, light, overlay)
val stack: ItemStack = if (entity.getStack().`is`(CobblemonItems.RELIC_COIN_POUCH)) {
coinPouchStack
} else {
entity.getStack()
}
val world = entity.level ?: return
val posType = getPositioningType(stack, world)
val blockState = if (entity.level != null) entity.blockState
@@ -167,6 +171,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
//stack.item == CobblemonItems.POKEMON_MODEL -> PositioningType.ITEM_MODEL
stack.item == Items.SHIELD -> PositioningType.SHIELD
stack.item == Items.DECORATED_POT -> PositioningType.MOB_HEAD
stack.item == CobblemonItems.DOME_FOSSIL -> PositioningType.DOME_FOSSIL
Minecraft.getInstance().itemRenderer.getModel(stack, world, null, 0).isGui3d -> PositioningType.BLOCK_MODEL
else -> PositioningType.ITEM_MODEL
}
@@ -186,6 +191,7 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
MOB_HEAD(1f, 1f, 1f, 0f, -0.025f, 0f, 180f),
SHIELD(1f, 1f, 1f, 0f, -0.045f, 0f, 180f),
PASTURE(1f, 1f, 1f, 0f, 0.0375f, 0f),
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f)
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f),
DOME_FOSSIL(1f, 1f, 1f, 0f, -0.15f, 0f)
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.block.entity.ModificationVariantBlockEntity
import com.cobblemon.mod.common.client.render.modification.ModificationVariantModels
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
class ModificationVariantRenderer<T : ModificationVariantBlockEntity>(context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<T> {
override fun render(entity: T, partialTick: Float, poseStack: PoseStack, buffers: MultiBufferSource, light: Int, overlay: Int) {
ModificationVariantModels.renderBlock(entity, poseStack, buffers, light, overlay)
}
override fun shouldRenderOffScreen(entity: T): Boolean = true
}
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.modification
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import net.minecraft.client.renderer.block.model.BakedQuad
import net.minecraft.client.renderer.block.model.ItemOverrides
import net.minecraft.client.renderer.block.model.ItemTransform
import net.minecraft.client.renderer.block.model.ItemTransforms
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.core.Direction
import net.minecraft.util.RandomSource
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.level.block.state.BlockState
import org.joml.Vector3f
class ModificationVariantItemModel(private val delegate: BakedModel, variant: ModificationVariant) : BakedModel {
private val cellExtent = cellExtent(variant)
private val scale = 1F / cellExtent
private val center: Vector3f
private val quadsBySide = hashMapOf<Direction?, MutableList<BakedQuad>>()
init {
val min = variant.cellsMin()
val max = variant.cellsMax()
center = Vector3f((min.x + max.x + 1) / 2F, (min.y + max.y + 1) / 2F, (min.z + max.z + 1) / 2F)
}
override fun getQuads(state: BlockState?, side: Direction?, random: RandomSource): MutableList<BakedQuad> {
if (cellExtent <= 1) {
return delegate.getQuads(state, side, random)
}
return quadsBySide.getOrPut(side) { delegate.getQuads(state, side, random).map(::scaleQuad).toMutableList() }
}
override fun useAmbientOcclusion(): Boolean = delegate.useAmbientOcclusion()
override fun isGui3d(): Boolean = true
override fun usesBlockLight(): Boolean = true
override fun isCustomRenderer(): Boolean = delegate.isCustomRenderer
override fun getParticleIcon(): TextureAtlasSprite = delegate.particleIcon
override fun getTransforms(): ItemTransforms {
return if (delegate.transforms.hasTransform(ItemDisplayContext.GUI)) delegate.transforms else BLOCK_TRANSFORMS
}
override fun getOverrides(): ItemOverrides = delegate.overrides
private fun scaleQuad(quad: BakedQuad): BakedQuad {
val vertices = quad.vertices.copyOf()
for (vertex in 0 until VERTICES_PER_QUAD) {
val positionIndex = vertex * VERTEX_STRIDE
vertices[positionIndex] = scaleCoordinate(vertices[positionIndex], center.x)
vertices[positionIndex + 1] = scaleCoordinate(vertices[positionIndex + 1], center.y)
vertices[positionIndex + 2] = scaleCoordinate(vertices[positionIndex + 2], center.z)
}
return BakedQuad(vertices, quad.tintIndex, quad.direction, quad.sprite, quad.isShade)
}
private fun scaleCoordinate(packed: Int, centerCoordinate: Float): Int {
val coordinate = Float.fromBits(packed)
return (0.5F + (coordinate - centerCoordinate) * scale).toBits()
}
companion object {
private const val VERTICES_PER_QUAD = 4
private val VERTEX_STRIDE = DefaultVertexFormat.BLOCK.vertexSize / Int.SIZE_BYTES
private val BLOCK_TRANSFORMS = ItemTransforms(
blockTransform(75F, 45F, 0F, 0F, 2.5F, 0F, 0.375F),
blockTransform(75F, 45F, 0F, 0F, 2.5F, 0F, 0.375F),
blockTransform(0F, 225F, 0F, 0F, 0F, 0F, 0.4F),
blockTransform(0F, 45F, 0F, 0F, 0F, 0F, 0.4F),
ItemTransform.NO_TRANSFORM,
blockTransform(30F, 225F, 0F, 0F, 0F, 0F, 0.625F),
blockTransform(0F, 0F, 0F, 0F, 3F, 0F, 0.25F),
blockTransform(0F, 0F, 0F, 0F, 0F, 0F, 0.5F)
)
fun cellExtent(variant: ModificationVariant): Int {
val size = variant.cellsMax().subtract(variant.cellsMin())
return maxOf(size.x, size.y, size.z) + 1
}
private fun blockTransform(
rotationX: Float,
rotationY: Float,
rotationZ: Float,
translationX: Float,
translationY: Float,
translationZ: Float,
scale: Float
): ItemTransform = ItemTransform(
Vector3f(rotationX, rotationY, rotationZ),
Vector3f(translationX / 16F, translationY / 16F, translationZ / 16F),
Vector3f(scale, scale, scale)
)
}
}
@@ -0,0 +1,107 @@
/*
* 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.modification
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.block.entity.ModificationVariantBlockEntity
import com.cobblemon.mod.common.api.modification.ModificationVariants
import com.cobblemon.mod.common.block.modification.ModificationVariantBlock
import com.cobblemon.mod.common.block.modification.ModificationVariantStructures
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.Sheets
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.state.BlockState
object ModificationVariantModels {
private const val VARIANT_MODEL_FOLDER = "mod_station/"
private const val STANDALONE_VARIANT = "standalone"
private val itemModels = hashMapOf<ResourceLocation, BakedModel>()
private val missingModelLocations = hashSetOf<ResourceLocation>()
@JvmStatic
fun isVariantModel(location: ResourceLocation): Boolean {
return location.path.startsWith("block/$VARIANT_MODEL_FOLDER") || location.path.startsWith("item/$VARIANT_MODEL_FOLDER")
}
@JvmStatic
fun bakedIdentifier(location: ResourceLocation): ModelResourceLocation = ModelResourceLocation(location, STANDALONE_VARIANT)
@JvmStatic
fun clearCache() {
itemModels.clear()
missingModelLocations.clear()
}
@JvmStatic
fun isTranslucentVariant(stack: ItemStack): Boolean = ModificationVariants.fromStack(stack)?.isTranslucent == true
@JvmStatic
fun itemModel(stack: ItemStack): BakedModel? {
val variant = ModificationVariants.fromStack(stack) ?: return null
val model = bakedModel(variant, variant.model) ?: return null
return itemModels.getOrPut(variant.model) { ModificationVariantItemModel(model, variant) }
}
fun bakedModel(variant: ModificationVariant, modelLocation: ResourceLocation): BakedModel? {
val modelManager = Minecraft.getInstance().modelManager
val model = modelManager.getModel(bakedIdentifier(modelLocation))
if (model !== modelManager.missingModel) {
return model
}
if (missingModelLocations.add(modelLocation)) {
Cobblemon.LOGGER.warn("Modification variant {} references model {} which is not under a mod_station model folder or does not exist", variant.identifier, modelLocation)
}
return null
}
fun renderBlock(entity: ModificationVariantBlockEntity, poseStack: PoseStack, buffers: MultiBufferSource, light: Int, overlay: Int) {
val state = entity.blockState
if (!ModificationVariantStructures.isModified(state)) {
return
}
if (state.block !is ModificationVariantBlock) {
return
}
val variant = entity.variant()
val variantModel = variant?.let { bakedModel(it, it.modelFor(state)) }
if (variantModel != null) {
renderRotatedModel(variantModel, state, variant.isTranslucent, poseStack, buffers, light, overlay)
return
}
val stockState = state.setValue(ModificationVariantBlock.MODIFIED, false)
val stockModel = Minecraft.getInstance().blockRenderer.getBlockModel(stockState)
val renderType = Sheets.cutoutBlockSheet()
Minecraft.getInstance().blockRenderer.modelRenderer.renderModel(poseStack.last(), buffers.getBuffer(renderType), stockState, stockModel, 1F, 1F, 1F, light, overlay)
}
private fun renderRotatedModel(
model: BakedModel,
state: BlockState,
isTranslucent: Boolean,
poseStack: PoseStack,
buffers: MultiBufferSource,
light: Int,
overlay: Int
) {
val renderType = if (isTranslucent) Sheets.translucentCullBlockSheet() else Sheets.cutoutBlockSheet()
poseStack.pushPose()
poseStack.translate(0.5, 0.0, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(-ModificationVariantStructures.facingOf(state).toYRot()))
poseStack.translate(-0.5, 0.0, -0.5)
Minecraft.getInstance().blockRenderer.modelRenderer.renderModel(poseStack.last(), buffers.getBuffer(renderType), state, model, 1F, 1F, 1F, light, overlay)
poseStack.popPose()
}
}
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.interaction.PokemonInteractions
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.modification.ModificationVariants
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.npc.NPCClasses
@@ -119,6 +120,7 @@ object CobblemonDataProvider : DataProvider {
this.register(SpawnBaitEffects, reloadable = false)
this.register(CobblemonRideSettings, reloadable = true)
this.register(TechnicalMachines, reloadable = false)
this.register(ModificationVariants, reloadable = true)
SpawnBait.Effects.setupEffects()
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.components
import com.mojang.serialization.Codec
import io.netty.buffer.ByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
data class ModificationVariantComponent(val variant: ResourceLocation) {
companion object {
val CODEC: Codec<ModificationVariantComponent> = ResourceLocation.CODEC.xmap(
::ModificationVariantComponent,
ModificationVariantComponent::variant
)
val PACKET_CODEC: StreamCodec<ByteBuf, ModificationVariantComponent> = ByteBufCodecs.fromCodec(CODEC)
}
}
@@ -251,6 +251,7 @@ object CobblemonItemGroups {
entries.accept(CobblemonItems.HEALING_MACHINE)
entries.accept(CobblemonItems.PASTURE)
entries.accept(CobblemonItems.TM_MACHINE)
entries.accept(CobblemonItems.MODIFICATION_STATION)
entries.accept(CobblemonItems.GILDED_CHEST)
entries.accept(CobblemonItems.YELLOW_GILDED_CHEST)
@@ -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.net.messages.client.data
import com.cobblemon.mod.common.api.modification.ModificationVariant
import com.cobblemon.mod.common.api.modification.ModificationVariants
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class ModificationVariantRegistrySyncPacket(variants: Collection<ModificationVariant>) :
DataRegistrySyncPacket<ModificationVariant, ModificationVariantRegistrySyncPacket>(variants) {
companion object {
val ID = cobblemonResource("modification_variant_registry_sync")
fun decode(buffer: RegistryFriendlyByteBuf): ModificationVariantRegistrySyncPacket {
return ModificationVariantRegistrySyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: ModificationVariant) = entry.encode(buffer)
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): ModificationVariant = ModificationVariant.decode(buffer)
override fun synchronizeDecoded(entries: Collection<ModificationVariant>) {
ModificationVariants.replaceEntries(entries)
}
}
@@ -1,19 +1,20 @@
{
"variants": {
"facing=south": {
"model": "cobblemon:block/display_case"
"multipart": [
{
"when": { "modified": "false", "facing": "south" },
"apply": { "model": "cobblemon:block/display_case" }
},
"facing=west": {
"model": "cobblemon:block/display_case",
"y": 90
{
"when": { "modified": "false", "facing": "west" },
"apply": { "model": "cobblemon:block/display_case", "y": 90 }
},
"facing=north": {
"model": "cobblemon:block/display_case",
"y": 180
{
"when": { "modified": "false", "facing": "north" },
"apply": { "model": "cobblemon:block/display_case", "y": 180 }
},
"facing=east": {
"model": "cobblemon:block/display_case",
"y": 270
{
"when": { "modified": "false", "facing": "east" },
"apply": { "model": "cobblemon:block/display_case", "y": 270 }
}
}
]
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "cobblemon:block/mod_empty"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "cobblemon:block/mod_empty"
}
}
}
@@ -0,0 +1,19 @@
{
"variants": {
"facing=south": {
"model": "cobblemon:block/modification_station"
},
"facing=west": {
"model": "cobblemon:block/modification_station",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/modification_station",
"y": 180
},
"facing=east": {
"model": "cobblemon:block/modification_station",
"y": 270
}
}
}
@@ -1,64 +1,68 @@
{
"variants": {
"facing=south,part=top,on=false": {
"model": "cobblemon:block/pc_top"
"multipart": [
{
"when": { "modified": "false", "part": "top", "on": "false", "facing": "south" },
"apply": { "model": "cobblemon:block/pc_top" }
},
"facing=west,part=top,on=false": {
"model": "cobblemon:block/pc_top",
"y": 90
{
"when": { "modified": "false", "part": "top", "on": "false", "facing": "west" },
"apply": { "model": "cobblemon:block/pc_top", "y": 90 }
},
"facing=north,part=top,on=false": {
"model": "cobblemon:block/pc_top",
"y": 180
{
"when": { "modified": "false", "part": "top", "on": "false", "facing": "north" },
"apply": { "model": "cobblemon:block/pc_top", "y": 180 }
},
"facing=east,part=top,on=false": {
"model": "cobblemon:block/pc_top",
"y": 270
{
"when": { "modified": "false", "part": "top", "on": "false", "facing": "east" },
"apply": { "model": "cobblemon:block/pc_top", "y": 270 }
},
"facing=south,part=top,on=true": {
"model": "cobblemon:block/pc_top_on"
{
"when": { "modified": "false", "part": "top", "on": "true", "facing": "south" },
"apply": { "model": "cobblemon:block/pc_top_on" }
},
"facing=west,part=top,on=true": {
"model": "cobblemon:block/pc_top_on",
"y": 90
{
"when": { "modified": "false", "part": "top", "on": "true", "facing": "west" },
"apply": { "model": "cobblemon:block/pc_top_on", "y": 90 }
},
"facing=north,part=top,on=true": {
"model": "cobblemon:block/pc_top_on",
"y": 180
{
"when": { "modified": "false", "part": "top", "on": "true", "facing": "north" },
"apply": { "model": "cobblemon:block/pc_top_on", "y": 180 }
},
"facing=east,part=top,on=true": {
"model": "cobblemon:block/pc_top_on",
"y": 270
{
"when": { "modified": "false", "part": "top", "on": "true", "facing": "east" },
"apply": { "model": "cobblemon:block/pc_top_on", "y": 270 }
},
"facing=south,part=bottom,on=false": {
"model": "cobblemon:block/pc_bottom"
{
"when": { "modified": "false", "part": "bottom", "on": "false", "facing": "south" },
"apply": { "model": "cobblemon:block/pc_bottom" }
},
"facing=west,part=bottom,on=false": {
"model": "cobblemon:block/pc_bottom",
"y": 90
{
"when": { "modified": "false", "part": "bottom", "on": "false", "facing": "west" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 90 }
},
"facing=north,part=bottom,on=false": {
"model": "cobblemon:block/pc_bottom",
"y": 180
{
"when": { "modified": "false", "part": "bottom", "on": "false", "facing": "north" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 180 }
},
"facing=east,part=bottom,on=false": {
"model": "cobblemon:block/pc_bottom",
"y": 270
{
"when": { "modified": "false", "part": "bottom", "on": "false", "facing": "east" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 270 }
},
"facing=south,part=bottom,on=true": {
"model": "cobblemon:block/pc_bottom"
{
"when": { "modified": "false", "part": "bottom", "on": "true", "facing": "south" },
"apply": { "model": "cobblemon:block/pc_bottom" }
},
"facing=west,part=bottom,on=true": {
"model": "cobblemon:block/pc_bottom",
"y": 90
{
"when": { "modified": "false", "part": "bottom", "on": "true", "facing": "west" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 90 }
},
"facing=north,part=bottom,on=true": {
"model": "cobblemon:block/pc_bottom",
"y": 180
{
"when": { "modified": "false", "part": "bottom", "on": "true", "facing": "north" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 180 }
},
"facing=east,part=bottom,on=true": {
"model": "cobblemon:block/pc_bottom",
"y": 270
{
"when": { "modified": "false", "part": "bottom", "on": "true", "facing": "east" },
"apply": { "model": "cobblemon:block/pc_bottom", "y": 270 }
}
}
]
}
@@ -989,6 +989,7 @@
"block.cobblemon.campfire_pot_white": "White Campfire Pot",
"block.cobblemon.campfire_pot_yellow": "Yellow Campfire Pot",
"block.cobblemon.tm_machine": "TM Machine",
"block.cobblemon.modification_station": "Modification Station",
"block.cobblemon.hearty_grain_bale": "Hearty Grain Bale",
"block.cobblemon.tatami_block": "Tatami",
"block.cobblemon.tatami_mat": "Tatami Mat",
@@ -1184,6 +1185,7 @@
"cobblemon.container.campfire_pot": "Campfire Pot",
"cobblemon.container.campfire_pot.recipe_book.toggle_recipes": "Showing Cookable",
"cobblemon.container.tm_machine": "TM Machine",
"container.cobblemon.modification_station": "Modification Station",
"cobblemon.ui.pokenav.title": "PokéNav",
"cobblemon.ui.pokenav.narrator.backbutton": "Poke Nav button pressed",
@@ -9573,5 +9575,64 @@
"stat.cobblemon.reel_ins": "PokéRod Catches",
"stat.cobblemon.riding_land": "Distance by Pokémon on Land",
"stat.cobblemon.riding_air": "Distance by Pokémon in Air",
"stat.cobblemon.riding_liquid": "Distance by Pokémon in Liquid"
"stat.cobblemon.riding_liquid": "Distance by Pokémon in Liquid",
"block.cobblemon.mod_variant": "Variant",
"block.cobblemon.mod_filler": "Variant Part",
"modification_variant.cobblemon.pc_hgss": "HeartGold & SoulSilver PC",
"modification_variant.cobblemon.pc_sm": "Sun & Moon PC",
"modification_variant.cobblemon.pc_xd": "XD PC",
"modification_variant.cobblemon.pc_bw": "Black & White PC",
"modification_variant.cobblemon.pc_dppt": "Diamond, Pearl & Platinum PC",
"modification_variant.cobblemon.pc_oras": "Omega Ruby & Alpha Sapphire PC",
"modification_variant.cobblemon.pc_swsh": "Sword & Shield PC",
"modification_variant.cobblemon.dome_fossil_block_1": "Dome Fossil Display",
"modification_variant.cobblemon.dome_fossil_block_2": "Dome Fossil Slab",
"modification_variant.cobblemon.display_case_stone_glass_color_1": "Glass Display Case 1",
"modification_variant.cobblemon.display_case_stone_no_glass_color_1": "Open Display Case 1",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_1": "Tinted Glass Display Case 1",
"modification_variant.cobblemon.display_case_stone_glass_color_2": "Glass Display Case 2",
"modification_variant.cobblemon.display_case_stone_no_glass_color_2": "Open Display Case 2",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_2": "Tinted Glass Display Case 2",
"modification_variant.cobblemon.display_case_stone_glass_color_3": "Glass Display Case 3",
"modification_variant.cobblemon.display_case_stone_no_glass_color_3": "Open Display Case 3",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_3": "Tinted Glass Display Case 3",
"modification_variant.cobblemon.display_case_stone_glass_color_4": "Glass Display Case 4",
"modification_variant.cobblemon.display_case_stone_no_glass_color_4": "Open Display Case 4",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_4": "Tinted Glass Display Case 4",
"modification_variant.cobblemon.display_case_stone_glass_color_5": "Glass Display Case 5",
"modification_variant.cobblemon.display_case_stone_no_glass_color_5": "Open Display Case 5",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_5": "Tinted Glass Display Case 5",
"modification_variant.cobblemon.display_case_stone_glass_color_6": "Glass Display Case 6",
"modification_variant.cobblemon.display_case_stone_no_glass_color_6": "Open Display Case 6",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_6": "Tinted Glass Display Case 6",
"modification_variant.cobblemon.display_case_stone_glass_color_7": "Glass Display Case 7",
"modification_variant.cobblemon.display_case_stone_no_glass_color_7": "Open Display Case 7",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_7": "Tinted Glass Display Case 7",
"modification_variant.cobblemon.display_case_stone_glass_color_8": "Glass Display Case 8",
"modification_variant.cobblemon.display_case_stone_no_glass_color_8": "Open Display Case 8",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_8": "Tinted Glass Display Case 8",
"modification_variant.cobblemon.display_case_stone_glass_color_9": "Glass Display Case 9",
"modification_variant.cobblemon.display_case_stone_no_glass_color_9": "Open Display Case 9",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_9": "Tinted Glass Display Case 9",
"modification_variant.cobblemon.display_case_stone_glass_color_10": "Glass Display Case 10",
"modification_variant.cobblemon.display_case_stone_no_glass_color_10": "Open Display Case 10",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_10": "Tinted Glass Display Case 10",
"modification_variant.cobblemon.display_case_stone_glass_color_11": "Glass Display Case 11",
"modification_variant.cobblemon.display_case_stone_no_glass_color_11": "Open Display Case 11",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_11": "Tinted Glass Display Case 11",
"modification_variant.cobblemon.display_case_stone_glass_color_12": "Glass Display Case 12",
"modification_variant.cobblemon.display_case_stone_no_glass_color_12": "Open Display Case 12",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_12": "Tinted Glass Display Case 12",
"modification_variant.cobblemon.display_case_stone_glass_color_13": "Glass Display Case 13",
"modification_variant.cobblemon.display_case_stone_no_glass_color_13": "Open Display Case 13",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_13": "Tinted Glass Display Case 13",
"modification_variant.cobblemon.display_case_stone_glass_color_14": "Glass Display Case 14",
"modification_variant.cobblemon.display_case_stone_no_glass_color_14": "Open Display Case 14",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_14": "Tinted Glass Display Case 14",
"modification_variant.cobblemon.display_case_stone_glass_color_15": "Glass Display Case 15",
"modification_variant.cobblemon.display_case_stone_no_glass_color_15": "Open Display Case 15",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_15": "Tinted Glass Display Case 15",
"modification_variant.cobblemon.display_case_stone_glass_color_16": "Glass Display Case 16",
"modification_variant.cobblemon.display_case_stone_no_glass_color_16": "Open Display Case 16",
"modification_variant.cobblemon.display_case_stone_tinted_glass_color_16": "Tinted Glass Display Case 16"
}
@@ -0,0 +1,5 @@
{
"textures": {
"particle": "minecraft:block/stone"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_1"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_10"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_11"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_12"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_13"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_14"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_15"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_16"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_2"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_3"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_4"
}
}
@@ -0,0 +1,90 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"textures": {
"0": "cobblemon:block/functional/display_case_bottom",
"1": "cobblemon:block/functional/display_case_side",
"2": "cobblemon:block/functional/display_case_top",
"3": "cobblemon:block/functional/display_case_internal",
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_5",
"particle": "cobblemon:block/functional/display_case_bottom"
},
"elements": [
{
"name": "cushion",
"from": [2, 4, 2],
"to": [14, 5, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 4, 2]},
"faces": {
"north": {"uv": [1, 13, 13, 14], "texture": "#8"},
"east": {"uv": [0, 1, 1, 13], "rotation": 270, "texture": "#8"},
"south": {"uv": [1, 0, 13, 1], "rotation": 180, "texture": "#8"},
"west": {"uv": [13, 1, 14, 13], "rotation": 90, "texture": "#8"},
"up": {"uv": [13, 13, 1, 1], "texture": "#8"}
}
},
{
"name": "base",
"from": [0, 0, 0],
"to": [16, 4, 16],
"faces": {
"north": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"east": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"south": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"west": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"up": {"uv": [0, 0, 16, 16], "texture": "#3"},
"down": {"uv": [0, 0, 16, 16], "texture": "#0", "cullface": "down"}
}
},
{
"name": "glass",
"from": [0, 4, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "north"},
"east": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "east"},
"south": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "south"},
"west": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "west"},
"up": {"uv": [0, 0, 16, 16], "texture": "#2", "cullface": "up"}
}
},
{
"name": "glass_int",
"from": [15.975, 15.975, 15.975],
"to": [0.025, 4, 0.025],
"faces": {
"north": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "south"},
"east": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "west"},
"south": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "north"},
"west": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "east"},
"down": {"uv": [16, 0, 0, 16], "texture": "#1", "cullface": "up"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 135, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, -135, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_6"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_7"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_8"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_9"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_1"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_10"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_11"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_12"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_13"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_14"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_15"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_16"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_2"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_3"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_4"
}
}
@@ -0,0 +1,65 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"textures": {
"0": "cobblemon:block/functional/display_case_bottom",
"1": "cobblemon:block/functional/display_case_side",
"3": "cobblemon:block/functional/display_case_internal",
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_5",
"particle": "cobblemon:block/functional/display_case_bottom"
},
"elements": [
{
"name": "cushion",
"from": [2, 4, 2],
"to": [14, 5, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 4, 2]},
"faces": {
"north": {"uv": [1, 13, 13, 14], "texture": "#8"},
"east": {"uv": [0, 1, 1, 13], "rotation": 270, "texture": "#8"},
"south": {"uv": [1, 0, 13, 1], "rotation": 180, "texture": "#8"},
"west": {"uv": [13, 1, 14, 13], "rotation": 90, "texture": "#8"},
"up": {"uv": [13, 13, 1, 1], "texture": "#8"}
}
},
{
"name": "base",
"from": [0, 0, 0],
"to": [16, 4, 16],
"faces": {
"north": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"east": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"south": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"west": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"up": {"uv": [0, 0, 16, 16], "texture": "#3"},
"down": {"uv": [0, 0, 16, 16], "texture": "#0", "cullface": "down"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 135, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, -135, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_6"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_7"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_8"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_no_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_9"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_1"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_10"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_11"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_12"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_13"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_14"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_15"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_16"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_2"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_3"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_4"
}
}
@@ -0,0 +1,90 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"textures": {
"0": "cobblemon:block/functional/display_case_bottom",
"1": "cobblemon:block/mod_station/display_case/display_case_side_tinted_glass",
"2": "cobblemon:block/mod_station/display_case/display_case_top_tinted_glass",
"3": "cobblemon:block/functional/display_case_internal",
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_5",
"particle": "cobblemon:block/functional/display_case_bottom"
},
"elements": [
{
"name": "cushion",
"from": [2, 4, 2],
"to": [14, 5, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 4, 2]},
"faces": {
"north": {"uv": [1, 13, 13, 14], "texture": "#8"},
"east": {"uv": [0, 1, 1, 13], "rotation": 270, "texture": "#8"},
"south": {"uv": [1, 0, 13, 1], "rotation": 180, "texture": "#8"},
"west": {"uv": [13, 1, 14, 13], "rotation": 90, "texture": "#8"},
"up": {"uv": [13, 13, 1, 1], "texture": "#8"}
}
},
{
"name": "base",
"from": [0, 0, 0],
"to": [16, 4, 16],
"faces": {
"north": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"east": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"south": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"west": {"uv": [0, 12, 16, 16], "texture": "#1", "cullface": "north"},
"up": {"uv": [0, 0, 16, 16], "texture": "#3"},
"down": {"uv": [0, 0, 16, 16], "texture": "#0", "cullface": "down"}
}
},
{
"name": "glass",
"from": [0, 4, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "north"},
"east": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "east"},
"south": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "south"},
"west": {"uv": [0, 0, 16, 12], "texture": "#1", "cullface": "west"},
"up": {"uv": [0, 0, 16, 16], "texture": "#2", "cullface": "up"}
}
},
{
"name": "glass_int",
"from": [15.975, 15.975, 15.975],
"to": [0.025, 4, 0.025],
"faces": {
"north": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "south"},
"east": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "west"},
"south": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "north"},
"west": {"uv": [0, 12, 16, 0], "texture": "#1", "cullface": "east"},
"down": {"uv": [16, 0, 0, 16], "texture": "#1", "cullface": "up"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 135, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, 135, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, -135, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_6"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_7"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_8"
}
}
@@ -0,0 +1,6 @@
{
"parent": "cobblemon:block/mod_station/display_case/display_case_stone_tinted_glass_color_5",
"textures": {
"8": "cobblemon:block/mod_station/display_case/display_case_pillow_9"
}
}
@@ -0,0 +1,42 @@
{
"textures": {
"0": "cobblemon:block/mod_station/dome_fossil/dome_fossil_block_1",
"particle": "cobblemon:block/mod_station/dome_fossil/dome_fossil_block_1"
},
"elements": [
{
"from": [4, -0.25, 3],
"to": [12, 1.75, 13],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, -0.75, 8.5]
},
"faces": {
"north": {"uv": [2.5, 12.5, 4.5, 13], "texture": "#0"},
"east": {"uv": [0, 12.5, 2.5, 13], "texture": "#0"},
"south": {"uv": [7, 12.5, 9, 13], "texture": "#0"},
"west": {"uv": [4.5, 12.5, 7, 13], "texture": "#0"},
"up": {"uv": [4.5, 12.5, 2.5, 10], "texture": "#0"},
"down": {"uv": [6.5, 10, 4.5, 12.5], "texture": "#0"}
}
},
{
"from": [4.5, -0.25, 4],
"to": [11.5, 2.75, 12],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, -0.75, 8.5]
},
"faces": {
"north": {"uv": [2, 4.75, 3.75, 5.5], "texture": "#0"},
"east": {"uv": [0, 4.75, 2, 5.5], "texture": "#0"},
"south": {"uv": [5.75, 4.75, 7.5, 5.5], "texture": "#0"},
"west": {"uv": [3.75, 4.75, 5.75, 5.5], "texture": "#0"},
"up": {"uv": [3.75, 4.75, 2, 2.75], "texture": "#0"},
"down": {"uv": [5.5, 2.75, 3.75, 4.75], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,110 @@
{
"textures": {
"0": "cobblemon:block/mod_station/dome_fossil/dome_fossil_block_1",
"particle": "cobblemon:block/mod_station/dome_fossil/dome_fossil_block_1"
},
"elements": [
{
"from": [4, 2.75, 3],
"to": [12, 4.75, 13],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 2.25, 8.5]
},
"faces": {
"north": {"uv": [2.5, 12.5, 4.5, 13], "texture": "#0"},
"east": {"uv": [0, 12.5, 2.5, 13], "texture": "#0"},
"south": {"uv": [7, 12.5, 9, 13], "texture": "#0"},
"west": {"uv": [4.5, 12.5, 7, 13], "texture": "#0"},
"up": {"uv": [4.5, 12.5, 2.5, 10], "texture": "#0"},
"down": {"uv": [6.5, 10, 4.5, 12.5], "texture": "#0"}
}
},
{
"from": [4.5, 2.75, 4],
"to": [11.5, 5.75, 12],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 2.25, 8.5]
},
"faces": {
"north": {"uv": [2, 4.75, 3.75, 5.5], "texture": "#0"},
"east": {"uv": [0, 4.75, 2, 5.5], "texture": "#0"},
"south": {"uv": [5.75, 4.75, 7.5, 5.5], "texture": "#0"},
"west": {"uv": [3.75, 4.75, 5.75, 5.5], "texture": "#0"},
"up": {"uv": [3.75, 4.75, 2, 2.75], "texture": "#0"},
"down": {"uv": [5.5, 2.75, 3.75, 4.75], "texture": "#0"}
}
},
{
"from": [2, 0, 0],
"to": [13, 4, 14],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 0, 8]
},
"faces": {
"north": {"uv": [7.5, 2.75, 10.25, 3.75], "texture": "#0"},
"east": {"uv": [5.5, 5.5, 9, 6.5], "texture": "#0"},
"south": {"uv": [7.5, 3.75, 10.25, 4.75], "texture": "#0"},
"west": {"uv": [5.5, 6.5, 9, 7.5], "texture": "#0"},
"up": {"uv": [2.75, 9, 0, 5.5], "texture": "#0"},
"down": {"uv": [5.5, 5.5, 2.75, 9], "texture": "#0"}
}
},
{
"from": [4, 0, 14],
"to": [13, 4, 16],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 0, 8]
},
"faces": {
"north": {"uv": [5.5, 7.5, 7.75, 8.5], "texture": "#0"},
"east": {"uv": [2.5, 9, 3, 10], "texture": "#0"},
"south": {"uv": [7.75, 7.5, 10, 8.5], "texture": "#0"},
"west": {"uv": [3, 9, 3.5, 10], "texture": "#0"},
"up": {"uv": [9.75, 5.25, 7.5, 4.75], "texture": "#0"},
"down": {"uv": [10.75, 0, 8.5, 0.5], "texture": "#0"}
}
},
{
"from": [13, 0, 3],
"to": [16, 3, 9],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 0, 8]
},
"faces": {
"north": {"uv": [1, 9, 1.75, 9.75], "texture": "#0"},
"east": {"uv": [8.5, 0.5, 10, 1.25], "texture": "#0"},
"south": {"uv": [1.75, 9, 2.5, 9.75], "texture": "#0"},
"west": {"uv": [8.5, 1.25, 10, 2], "texture": "#0"},
"up": {"uv": [6.25, 10, 5.5, 8.5], "texture": "#0"},
"down": {"uv": [7, 8.5, 6.25, 10], "texture": "#0"}
}
},
{
"from": [0, 0, 1],
"to": [2, 2, 10],
"rotation": {
"angle": 0,
"axis": "y",
"origin": [8, 0, 8]
},
"faces": {
"north": {"uv": [3.5, 9, 4, 9.5], "texture": "#0"},
"east": {"uv": [8.5, 2, 10.75, 2.5], "texture": "#0"},
"south": {"uv": [4, 9, 4.5, 9.5], "texture": "#0"},
"west": {"uv": [7, 8.5, 9.25, 9], "texture": "#0"},
"up": {"uv": [0.5, 11.25, 0, 9], "texture": "#0"},
"down": {"uv": [1, 9, 0.5, 11.25], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,123 @@
{
"format_version": "1.21.11",
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"2": "cobblemon:block/mod_station/pc/pc_bw",
"particle": "cobblemon:block/mod_station/pc/pc_bw"
},
"elements": [
{
"name": "main",
"from": [2, 15, 7],
"to": [14, 31, 15],
"faces": {
"north": {"uv": [3, 8.25, 6, 12.25], "texture": "#2"},
"east": {"uv": [0.25, 8.25, 2.25, 12.25], "texture": "#2"},
"south": {"uv": [9.5, 8.25, 12.5, 12.25], "texture": "#2"},
"west": {"uv": [6.75, 8.25, 8.75, 12.25], "texture": "#2"},
"up": {"uv": [6, 7.25, 3, 5.25], "texture": "#2"},
"down": {"uv": [12.5, 5.25, 9.5, 8], "texture": "#2"}
}
},
{
"name": "extrusion",
"from": [0, 17.9, 10],
"to": [2, 25.9, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0.4, 0]},
"faces": {
"north": {"uv": [6, 9.5, 6.5, 11.5], "texture": "#2"},
"east": {"uv": [1, 9.5, 1.5, 11.5], "texture": "#2"},
"south": {"uv": [9, 9.5, 9.5, 11.5], "texture": "#2"},
"west": {"uv": [7.5, 9.5, 8, 11.5], "texture": "#2"},
"up": {"uv": [6.5, 6.5, 6, 6], "texture": "#2"},
"down": {"uv": [13, 6, 12.5, 6.5], "texture": "#2"}
}
},
{
"name": "extrusion",
"from": [14, 17.9, 10],
"to": [16, 25.9, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0.4, 0]},
"faces": {
"north": {"uv": [2.5, 9.5, 3, 11.5], "texture": "#2"},
"east": {"uv": [1, 9.5, 1.5, 11.5], "texture": "#2"},
"south": {"uv": [12.5, 9.5, 13, 11.5], "texture": "#2"},
"west": {"uv": [7.5, 9.5, 8, 11.5], "texture": "#2"},
"up": {"uv": [3, 6.5, 2.5, 6], "texture": "#2"},
"down": {"uv": [9.5, 6, 9, 6.5], "texture": "#2"}
}
},
{
"name": "main",
"from": [1.99, 8.99, 3.99],
"to": [14.01, 15.01, 15.01],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]},
"faces": {
"north": {"uv": [3, 12.25, 6, 13.75], "texture": "#2"},
"east": {"uv": [0.25, 12.25, 3, 13.75], "texture": "#2"},
"south": {"uv": [9.5, 12.25, 12.5, 13.75], "texture": "#2"},
"west": {"uv": [6, 12.25, 8.75, 13.75], "texture": "#2"},
"up": {"uv": [6, 8, 3, 5.25], "texture": "#2"},
"down": {"uv": [12.5, 5.25, 9.5, 8], "texture": "#2"}
}
},
{
"name": "base",
"from": [4, 0, 7],
"to": [12, 2, 13],
"faces": {
"north": {"uv": [3.5, 15.5, 5.5, 16], "texture": "#2"},
"east": {"uv": [0.75, 15.5, 2.25, 16], "texture": "#2"},
"south": {"uv": [10, 15.5, 12, 16], "texture": "#2"},
"west": {"uv": [6.75, 15.5, 8.25, 16], "texture": "#2"},
"up": {"uv": [8.75, 6.75, 6.75, 5.25], "texture": "#2"},
"down": {"uv": [8.75, 6.75, 6.75, 8.25], "texture": "#2"}
}
},
{
"name": "pole",
"from": [6, 2, 9],
"to": [10, 9, 11],
"faces": {
"north": {"uv": [4, 13.75, 5, 15.5], "texture": "#2"},
"east": {"uv": [1.25, 13.75, 1.75, 15.5], "texture": "#2"},
"south": {"uv": [10.5, 13.75, 11.5, 15.5], "texture": "#2"},
"west": {"uv": [7.25, 13.75, 7.75, 15.5], "texture": "#2"},
"up": {"uv": [5.75, 11.375, 5.25, 11.125], "texture": "#2"},
"down": {"uv": [5.75, 11.125, 5.25, 11.375], "texture": "#2"}
}
}
],
"groups": [
{
"name": "PC_BW",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [
{
"name": "top_half",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [0, 1, 2]
},
{
"name": "bottom_half",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [3]
},
{
"name": "stand",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [4, 5]
}
]
}
]
}
@@ -0,0 +1,123 @@
{
"format_version": "1.21.11",
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"2": "cobblemon:block/mod_station/pc/pc_bw",
"particle": "cobblemon:block/mod_station/pc/pc_bw"
},
"elements": [
{
"name": "main",
"from": [2, 15, 7],
"to": [14, 31, 15],
"faces": {
"north": {"uv": [3, 1, 6, 5], "texture": "#2"},
"east": {"uv": [0.25, 8.25, 2.25, 12.25], "texture": "#2"},
"south": {"uv": [9.5, 8.25, 12.5, 12.25], "texture": "#2"},
"west": {"uv": [6.75, 8.25, 8.75, 12.25], "texture": "#2"},
"up": {"uv": [6, 7.25, 3, 5.25], "texture": "#2"},
"down": {"uv": [12.5, 5.25, 9.5, 8], "texture": "#2"}
}
},
{
"name": "extrusion",
"from": [0, 17.9, 10],
"to": [2, 25.9, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0.4, 0]},
"faces": {
"north": {"uv": [6, 9.5, 6.5, 11.5], "texture": "#2"},
"east": {"uv": [1, 9.5, 1.5, 11.5], "texture": "#2"},
"south": {"uv": [9, 9.5, 9.5, 11.5], "texture": "#2"},
"west": {"uv": [7.5, 9.5, 8, 11.5], "texture": "#2"},
"up": {"uv": [6.5, 6.5, 6, 6], "texture": "#2"},
"down": {"uv": [13, 6, 12.5, 6.5], "texture": "#2"}
}
},
{
"name": "extrusion",
"from": [14, 17.9, 10],
"to": [16, 25.9, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0.4, 0]},
"faces": {
"north": {"uv": [2.5, 9.5, 3, 11.5], "texture": "#2"},
"east": {"uv": [1, 9.5, 1.5, 11.5], "texture": "#2"},
"south": {"uv": [12.5, 9.5, 13, 11.5], "texture": "#2"},
"west": {"uv": [7.5, 9.5, 8, 11.5], "texture": "#2"},
"up": {"uv": [3, 6.5, 2.5, 6], "texture": "#2"},
"down": {"uv": [9.5, 6, 9, 6.5], "texture": "#2"}
}
},
{
"name": "main",
"from": [1.99, 8.99, 3.99],
"to": [14.01, 15.01, 15.01],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]},
"faces": {
"north": {"uv": [3, 12.25, 6, 13.75], "texture": "#2"},
"east": {"uv": [0.25, 12.25, 3, 13.75], "texture": "#2"},
"south": {"uv": [9.5, 12.25, 12.5, 13.75], "texture": "#2"},
"west": {"uv": [6, 12.25, 8.75, 13.75], "texture": "#2"},
"up": {"uv": [6, 8, 3, 5.25], "texture": "#2"},
"down": {"uv": [12.5, 5.25, 9.5, 8], "texture": "#2"}
}
},
{
"name": "base",
"from": [4, 0, 7],
"to": [12, 2, 13],
"faces": {
"north": {"uv": [3.5, 15.5, 5.5, 16], "texture": "#2"},
"east": {"uv": [0.75, 15.5, 2.25, 16], "texture": "#2"},
"south": {"uv": [10, 15.5, 12, 16], "texture": "#2"},
"west": {"uv": [6.75, 15.5, 8.25, 16], "texture": "#2"},
"up": {"uv": [8.75, 6.75, 6.75, 5.25], "texture": "#2"},
"down": {"uv": [8.75, 6.75, 6.75, 8.25], "texture": "#2"}
}
},
{
"name": "pole",
"from": [6, 2, 9],
"to": [10, 9, 11],
"faces": {
"north": {"uv": [4, 13.75, 5, 15.5], "texture": "#2"},
"east": {"uv": [1.25, 13.75, 1.75, 15.5], "texture": "#2"},
"south": {"uv": [10.5, 13.75, 11.5, 15.5], "texture": "#2"},
"west": {"uv": [7.25, 13.75, 7.75, 15.5], "texture": "#2"},
"up": {"uv": [5.75, 11.375, 5.25, 11.125], "texture": "#2"},
"down": {"uv": [5.75, 11.125, 5.25, 11.375], "texture": "#2"}
}
}
],
"groups": [
{
"name": "PC_BW",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [
{
"name": "top_half",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [0, 1, 2]
},
{
"name": "bottom_half",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [3]
},
{
"name": "stand",
"origin": [0, 0, 0],
"scope": 0,
"color": 0,
"children": [4, 5]
}
]
}
]
}
@@ -0,0 +1,128 @@
{
"format_version": "1.21.11",
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"0": "cobblemon:block/mod_station/pc/pc_dppt",
"particle": "cobblemon:block/mod_station/pc/pc_dppt"
},
"elements": [
{
"from": [2.5, 5, 2.5],
"to": [13.5, 16, 13.5],
"rotation": {"angle": 0, "axis": "y", "origin": [3.5, 11, 10.5]},
"faces": {
"north": {"uv": [4.5, 12, 7.25, 14.75], "texture": "#0"},
"east": {"uv": [8.75, 12, 11.5, 14.75], "texture": "#0"},
"south": {"uv": [12.5, 12, 15.25, 14.75], "texture": "#0"},
"west": {"uv": [0.5, 12, 3.25, 14.75], "texture": "#0"},
"up": {"uv": [7.25, 3.5, 4.5, 0.75], "texture": "#0"},
"down": {"uv": [15.25, 0.75, 12.5, 3.5], "texture": "#0"}
}
},
{
"from": [3, 0, 3],
"to": [13, 5, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [3, 6, 9]},
"faces": {
"north": {"uv": [4.75, 14.75, 7.25, 16], "texture": "#0"},
"east": {"uv": [8.75, 14.75, 11.25, 16], "texture": "#0"},
"south": {"uv": [12.75, 14.75, 15.25, 16], "texture": "#0"},
"west": {"uv": [0.75, 14.75, 3.25, 16], "texture": "#0"},
"up": {"uv": [11.25, 3.25, 8.75, 0.75], "texture": "#0"},
"down": {"uv": [11.25, 0.75, 8.75, 3.25], "texture": "#0"}
}
},
{
"from": [3.5, 16, 3.5],
"to": [12.5, 17, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4.5, 16, 10.5]},
"faces": {
"north": {"uv": [12.75, 6.25, 15, 6.5], "texture": "#0"},
"east": {"uv": [10.5, 11.75, 11.25, 12], "texture": "#0"},
"south": {"uv": [12.75, 5.25, 15, 5.5], "texture": "#0"},
"west": {"uv": [0.75, 11.75, 1.5, 12], "texture": "#0"},
"up": {"uv": [15, 6.25, 12.75, 5.5], "texture": "#0"},
"down": {"uv": [15, 5.5, 12.75, 6.25], "texture": "#0"}
}
},
{
"from": [6.5, 16, 9.5],
"to": [9.5, 20, 12.5],
"rotation": {"angle": 0, "axis": "y", "origin": [3.5, 16, 10.5]},
"faces": {
"north": {"uv": [5.5, 11, 6.25, 12], "texture": "#0"},
"east": {"uv": [9, 11, 9.75, 12], "texture": "#0"},
"south": {"uv": [13.5, 11, 14.25, 12], "texture": "#0"},
"west": {"uv": [2.25, 11, 3, 12], "texture": "#0"},
"up": {"uv": [10.5, 2, 9.5, 1], "texture": "#0"},
"down": {"uv": [10.5, 1, 9.5, 2], "texture": "#0"}
}
},
{
"from": [0, 20, 9.5],
"to": [16, 31, 13.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 21, 11.5]},
"faces": {
"north": {"uv": [4, 8.25, 8, 11], "texture": "#0"},
"east": {"uv": [8.75, 8.25, 9.75, 11], "texture": "#0"},
"south": {"uv": [12, 8.25, 16, 11], "texture": "#0"},
"west": {"uv": [2.25, 8.25, 3.25, 11], "texture": "#0"},
"up": {"uv": [8, 5.75, 4, 4.75], "texture": "#0"},
"down": {"uv": [8, 4.75, 12, 5.75], "texture": "#0"}
}
},
{
"from": [1, 30, 8.5],
"to": [15, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 21, 7.5]},
"faces": {
"north": {"uv": [4.25, 8.25, 7.75, 8.5], "texture": "#0"},
"east": {"uv": [9.75, 8.25, 10, 8.5], "texture": "#0"},
"south": {"uv": [12.25, 8.25, 15.75, 8.5], "texture": "#0"},
"west": {"uv": [2, 8.25, 2.25, 8.5], "texture": "#0"},
"up": {"uv": [7.75, 6, 4.25, 5.75], "texture": "#0"},
"down": {"uv": [3.75, 4.5, 0.25, 4.75], "texture": "#0"}
}
},
{
"from": [1, 20, 8.5],
"to": [15, 21, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 11, 7.5]},
"faces": {
"north": {"uv": [4.25, 10.75, 7.75, 11], "texture": "#0"},
"east": {"uv": [9.75, 10.75, 10, 11], "texture": "#0"},
"south": {"uv": [12.25, 10.75, 15.75, 11], "texture": "#0"},
"west": {"uv": [2, 10.75, 2.25, 11], "texture": "#0"},
"up": {"uv": [3.75, 7.25, 0.25, 7], "texture": "#0"},
"down": {"uv": [11.75, 5.75, 8.25, 6], "texture": "#0"}
}
},
{
"from": [15, 20, 8.5],
"to": [16, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 13, 7.5]},
"faces": {
"north": {"uv": [4, 8.25, 4.25, 11], "texture": "#0"},
"east": {"uv": [9.75, 8.25, 10, 11], "texture": "#0"},
"south": {"uv": [15.75, 8.25, 16, 11], "texture": "#0"},
"west": {"uv": [0, 4.5, 0.25, 7.25], "texture": "#0"},
"up": {"uv": [4.25, 6, 4, 5.75], "texture": "#0"},
"down": {"uv": [12, 5.75, 11.75, 6], "texture": "#0"}
}
},
{
"from": [0, 20, 8.5],
"to": [1, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 13, 7.5]},
"faces": {
"north": {"uv": [7.75, 8.25, 8, 11], "texture": "#0"},
"east": {"uv": [3.75, 4.5, 4, 7.25], "texture": "#0"},
"south": {"uv": [12, 8.25, 12.25, 11], "texture": "#0"},
"west": {"uv": [2, 8.25, 2.25, 11], "texture": "#0"},
"up": {"uv": [8, 6, 7.75, 5.75], "texture": "#0"},
"down": {"uv": [8.25, 5.75, 8, 6], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,128 @@
{
"format_version": "1.21.11",
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"0": "cobblemon:block/mod_station/pc/pc_dppt",
"particle": "cobblemon:block/mod_station/pc/pc_dppt"
},
"elements": [
{
"from": [2.5, 5, 2.5],
"to": [13.5, 16, 13.5],
"rotation": {"angle": 0, "axis": "y", "origin": [3.5, 11, 10.5]},
"faces": {
"north": {"uv": [4.5, 12, 7.25, 14.75], "texture": "#0"},
"east": {"uv": [8.75, 12, 11.5, 14.75], "texture": "#0"},
"south": {"uv": [12.5, 12, 15.25, 14.75], "texture": "#0"},
"west": {"uv": [0.5, 12, 3.25, 14.75], "texture": "#0"},
"up": {"uv": [7.25, 3.5, 4.5, 0.75], "texture": "#0"},
"down": {"uv": [15.25, 0.75, 12.5, 3.5], "texture": "#0"}
}
},
{
"from": [3, 0, 3],
"to": [13, 5, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [3, 6, 9]},
"faces": {
"north": {"uv": [4.75, 14.75, 7.25, 16], "texture": "#0"},
"east": {"uv": [8.75, 14.75, 11.25, 16], "texture": "#0"},
"south": {"uv": [12.75, 14.75, 15.25, 16], "texture": "#0"},
"west": {"uv": [0.75, 14.75, 3.25, 16], "texture": "#0"},
"up": {"uv": [11.25, 3.25, 8.75, 0.75], "texture": "#0"},
"down": {"uv": [11.25, 0.75, 8.75, 3.25], "texture": "#0"}
}
},
{
"from": [3.5, 16, 3.5],
"to": [12.5, 17, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4.5, 16, 10.5]},
"faces": {
"north": {"uv": [12.75, 6.25, 15, 6.5], "texture": "#0"},
"east": {"uv": [10.5, 11.75, 11.25, 12], "texture": "#0"},
"south": {"uv": [12.75, 5.25, 15, 5.5], "texture": "#0"},
"west": {"uv": [0.75, 11.75, 1.5, 12], "texture": "#0"},
"up": {"uv": [15, 6.25, 12.75, 5.5], "texture": "#0"},
"down": {"uv": [15, 5.5, 12.75, 6.25], "texture": "#0"}
}
},
{
"from": [6.5, 16, 9.5],
"to": [9.5, 20, 12.5],
"rotation": {"angle": 0, "axis": "y", "origin": [3.5, 16, 10.5]},
"faces": {
"north": {"uv": [5.5, 11, 6.25, 12], "texture": "#0"},
"east": {"uv": [9, 11, 9.75, 12], "texture": "#0"},
"south": {"uv": [13.5, 11, 14.25, 12], "texture": "#0"},
"west": {"uv": [2.25, 11, 3, 12], "texture": "#0"},
"up": {"uv": [10.5, 2, 9.5, 1], "texture": "#0"},
"down": {"uv": [10.5, 1, 9.5, 2], "texture": "#0"}
}
},
{
"from": [0, 20, 9.5],
"to": [16, 31, 13.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 21, 11.5]},
"faces": {
"north": {"uv": [0, 0.25, 4, 3], "texture": "#0"},
"east": {"uv": [8.75, 8.25, 9.75, 11], "texture": "#0"},
"south": {"uv": [12, 8.25, 16, 11], "texture": "#0"},
"west": {"uv": [2.25, 8.25, 3.25, 11], "texture": "#0"},
"up": {"uv": [8, 5.75, 4, 4.75], "texture": "#0"},
"down": {"uv": [8, 4.75, 12, 5.75], "texture": "#0"}
}
},
{
"from": [1, 30, 8.5],
"to": [15, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 21, 7.5]},
"faces": {
"north": {"uv": [4.25, 8.25, 7.75, 8.5], "texture": "#0"},
"east": {"uv": [9.75, 8.25, 10, 8.5], "texture": "#0"},
"south": {"uv": [12.25, 8.25, 15.75, 8.5], "texture": "#0"},
"west": {"uv": [2, 8.25, 2.25, 8.5], "texture": "#0"},
"up": {"uv": [7.75, 6, 4.25, 5.75], "texture": "#0"},
"down": {"uv": [3.75, 4.5, 0.25, 4.75], "texture": "#0"}
}
},
{
"from": [1, 20, 8.5],
"to": [15, 21, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 11, 7.5]},
"faces": {
"north": {"uv": [4.25, 10.75, 7.75, 11], "texture": "#0"},
"east": {"uv": [9.75, 10.75, 10, 11], "texture": "#0"},
"south": {"uv": [12.25, 10.75, 15.75, 11], "texture": "#0"},
"west": {"uv": [2, 10.75, 2.25, 11], "texture": "#0"},
"up": {"uv": [3.75, 7.25, 0.25, 7], "texture": "#0"},
"down": {"uv": [11.75, 5.75, 8.25, 6], "texture": "#0"}
}
},
{
"from": [15, 20, 8.5],
"to": [16, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 13, 7.5]},
"faces": {
"north": {"uv": [4, 8.25, 4.25, 11], "texture": "#0"},
"east": {"uv": [9.75, 8.25, 10, 11], "texture": "#0"},
"south": {"uv": [15.75, 8.25, 16, 11], "texture": "#0"},
"west": {"uv": [0, 4.5, 0.25, 7.25], "texture": "#0"},
"up": {"uv": [4.25, 6, 4, 5.75], "texture": "#0"},
"down": {"uv": [12, 5.75, 11.75, 6], "texture": "#0"}
}
},
{
"from": [0, 20, 8.5],
"to": [1, 31, 9.5],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 13, 7.5]},
"faces": {
"north": {"uv": [7.75, 8.25, 8, 11], "texture": "#0"},
"east": {"uv": [3.75, 4.5, 4, 7.25], "texture": "#0"},
"south": {"uv": [12, 8.25, 12.25, 11], "texture": "#0"},
"west": {"uv": [2, 8.25, 2.25, 11], "texture": "#0"},
"up": {"uv": [8, 6, 7.75, 5.75], "texture": "#0"},
"down": {"uv": [8.25, 5.75, 8, 6], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,99 @@
{
"textures": {
"0": "cobblemon:block/mod_station/pc/pc_hgss",
"particle": "cobblemon:block/mod_station/pc/pc_hgss"
},
"elements": [
{
"from": [2, 0, 1],
"to": [14, 2, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 2, 15]},
"faces": {
"north": {"uv": [4.5, 15.5, 7.5, 16], "texture": "#0"},
"east": {"uv": [8.25, 15.5, 11.75, 16], "texture": "#0"},
"south": {"uv": [12.5, 15.5, 15.5, 16], "texture": "#0"},
"west": {"uv": [0.25, 15.5, 3.75, 16], "texture": "#0"},
"up": {"uv": [7.5, 11.75, 4.5, 8.25], "texture": "#0"},
"down": {"uv": [11.5, 8.25, 8.5, 11.75], "texture": "#0"}
}
},
{
"from": [1, 4, 8],
"to": [15, 15, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 5, 6]},
"faces": {
"north": {"uv": [4.25, 12.25, 7.75, 15], "texture": "#0"},
"east": {"uv": [8.25, 12.25, 10, 15], "texture": "#0"},
"south": {"uv": [12.25, 12.25, 15.75, 15], "texture": "#0"},
"west": {"uv": [2, 12.25, 3.75, 15], "texture": "#0"},
"up": {"uv": [7.75, 7.75, 4.25, 6], "texture": "#0"},
"down": {"uv": [11.75, 5.75, 8.25, 7.5], "texture": "#0"}
}
},
{
"from": [1, 4, 7],
"to": [3, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-11, 5, 0]},
"faces": {
"north": {"uv": [7.25, 12.25, 7.75, 15], "texture": "#0"},
"east": {"uv": [3.25, 8.75, 3.5, 11.5], "texture": "#0"},
"south": {"uv": [7.25, 12.25, 7.75, 15], "texture": "#0"},
"west": {"uv": [1.75, 12.25, 2, 15], "texture": "#0"},
"up": {"uv": [4.75, 6, 4.25, 5.75], "texture": "#0"},
"down": {"uv": [8.75, 7.5, 8.25, 7.75], "texture": "#0"}
}
},
{
"from": [13, 4, 7],
"to": [15, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 5, 0]},
"faces": {
"north": {"uv": [4.25, 12.25, 4.75, 15], "texture": "#0"},
"east": {"uv": [10, 12.25, 10.25, 15], "texture": "#0"},
"south": {"uv": [4.25, 12.25, 4.75, 15], "texture": "#0"},
"west": {"uv": [0.5, 8.75, 0.75, 11.5], "texture": "#0"},
"up": {"uv": [7.75, 6, 7.25, 5.75], "texture": "#0"},
"down": {"uv": [11.75, 7.5, 11.25, 7.75], "texture": "#0"}
}
},
{
"from": [3, 14, 7],
"to": [13, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 15, 0]},
"faces": {
"north": {"uv": [4.75, 12.25, 7.25, 12.5], "texture": "#0"},
"east": {"uv": [4.75, 12.25, 5, 12.5], "texture": "#0"},
"south": {"uv": [4.75, 12.25, 7.25, 12.5], "texture": "#0"},
"west": {"uv": [7, 12.25, 7.25, 12.5], "texture": "#0"},
"up": {"uv": [7.25, 6, 4.75, 5.75], "texture": "#0"},
"down": {"uv": [3.25, 8.75, 0.75, 9], "texture": "#0"}
}
},
{
"from": [3, 4, 7],
"to": [13, 5, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 5, 0]},
"faces": {
"north": {"uv": [4.75, 14.75, 7.25, 15], "texture": "#0"},
"east": {"uv": [4.75, 14.75, 5, 15], "texture": "#0"},
"south": {"uv": [4.75, 14.75, 7.25, 15], "texture": "#0"},
"west": {"uv": [7, 14.75, 7.25, 15], "texture": "#0"},
"up": {"uv": [3.25, 11.5, 0.75, 11.25], "texture": "#0"},
"down": {"uv": [11.25, 7.5, 8.75, 7.75], "texture": "#0"}
}
},
{
"from": [6, 2, 9],
"to": [10, 4, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 3, 1]},
"faces": {
"north": {"uv": [5.5, 15, 6.5, 15.5], "texture": "#0"},
"east": {"uv": [8.75, 15, 9.75, 15.5], "texture": "#0"},
"south": {"uv": [13.5, 15, 14.5, 15.5], "texture": "#0"},
"west": {"uv": [2.25, 15, 3.25, 15.5], "texture": "#0"},
"up": {"uv": [6.5, 9.75, 5.5, 8.75], "texture": "#0"},
"down": {"uv": [10.5, 8.75, 9.5, 9.75], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,7 @@
{
"parent": "cobblemon:block/pc_bottom",
"textures": {
"all": "cobblemon:block/mod_station/pc/pc_hgss",
"particle": "cobblemon:block/mod_station/pc/pc_hgss"
}
}
@@ -0,0 +1,99 @@
{
"textures": {
"0": "cobblemon:block/mod_station/pc/pc_hgss",
"particle": "cobblemon:block/mod_station/pc/pc_hgss"
},
"elements": [
{
"from": [2, 0, 1],
"to": [14, 2, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 2, 15]},
"faces": {
"north": {"uv": [4.5, 15.5, 7.5, 16], "texture": "#0"},
"east": {"uv": [8.25, 15.5, 11.75, 16], "texture": "#0"},
"south": {"uv": [12.5, 15.5, 15.5, 16], "texture": "#0"},
"west": {"uv": [0.25, 15.5, 3.75, 16], "texture": "#0"},
"up": {"uv": [7.5, 11.75, 4.5, 8.25], "texture": "#0"},
"down": {"uv": [11.5, 8.25, 8.5, 11.75], "texture": "#0"}
}
},
{
"from": [1, 4, 8],
"to": [15, 15, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 5, 6]},
"faces": {
"north": {"uv": [4.25, 0.25, 7.75, 3], "texture": "#0"},
"east": {"uv": [8.25, 12.25, 10, 15], "texture": "#0"},
"south": {"uv": [12.25, 12.25, 15.75, 15], "texture": "#0"},
"west": {"uv": [2, 12.25, 3.75, 15], "texture": "#0"},
"up": {"uv": [7.75, 7.75, 4.25, 6], "texture": "#0"},
"down": {"uv": [11.75, 5.75, 8.25, 7.5], "texture": "#0"}
}
},
{
"from": [1, 4, 7],
"to": [3, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-11, 5, 0]},
"faces": {
"north": {"uv": [7.25, 12.25, 7.75, 15], "texture": "#0"},
"east": {"uv": [3.25, 8.75, 3.5, 11.5], "texture": "#0"},
"south": {"uv": [7.25, 12.25, 7.75, 15], "texture": "#0"},
"west": {"uv": [1.75, 12.25, 2, 15], "texture": "#0"},
"up": {"uv": [4.75, 6, 4.25, 5.75], "texture": "#0"},
"down": {"uv": [8.75, 7.5, 8.25, 7.75], "texture": "#0"}
}
},
{
"from": [13, 4, 7],
"to": [15, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 5, 0]},
"faces": {
"north": {"uv": [4.25, 12.25, 4.75, 15], "texture": "#0"},
"east": {"uv": [10, 12.25, 10.25, 15], "texture": "#0"},
"south": {"uv": [4.25, 12.25, 4.75, 15], "texture": "#0"},
"west": {"uv": [0.5, 8.75, 0.75, 11.5], "texture": "#0"},
"up": {"uv": [7.75, 6, 7.25, 5.75], "texture": "#0"},
"down": {"uv": [11.75, 7.5, 11.25, 7.75], "texture": "#0"}
}
},
{
"from": [3, 14, 7],
"to": [13, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 15, 0]},
"faces": {
"north": {"uv": [4.75, 12.25, 7.25, 12.5], "texture": "#0"},
"east": {"uv": [4.75, 12.25, 5, 12.5], "texture": "#0"},
"south": {"uv": [4.75, 12.25, 7.25, 12.5], "texture": "#0"},
"west": {"uv": [7, 12.25, 7.25, 12.5], "texture": "#0"},
"up": {"uv": [7.25, 6, 4.75, 5.75], "texture": "#0"},
"down": {"uv": [3.25, 8.75, 0.75, 9], "texture": "#0"}
}
},
{
"from": [3, 4, 7],
"to": [13, 5, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [-10, 5, 0]},
"faces": {
"north": {"uv": [4.75, 14.75, 7.25, 15], "texture": "#0"},
"east": {"uv": [4.75, 14.75, 5, 15], "texture": "#0"},
"south": {"uv": [4.75, 14.75, 7.25, 15], "texture": "#0"},
"west": {"uv": [7, 14.75, 7.25, 15], "texture": "#0"},
"up": {"uv": [3.25, 11.5, 0.75, 11.25], "texture": "#0"},
"down": {"uv": [11.25, 7.5, 8.75, 7.75], "texture": "#0"}
}
},
{
"from": [6, 2, 9],
"to": [10, 4, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [1, 3, 1]},
"faces": {
"north": {"uv": [5.5, 15, 6.5, 15.5], "texture": "#0"},
"east": {"uv": [8.75, 15, 9.75, 15.5], "texture": "#0"},
"south": {"uv": [13.5, 15, 14.5, 15.5], "texture": "#0"},
"west": {"uv": [2.25, 15, 3.25, 15.5], "texture": "#0"},
"up": {"uv": [6.5, 9.75, 5.5, 8.75], "texture": "#0"},
"down": {"uv": [10.5, 8.75, 9.5, 9.75], "texture": "#0"}
}
}
]
}
@@ -0,0 +1,7 @@
{
"parent": "cobblemon:block/pc_top",
"textures": {
"all": "cobblemon:block/mod_station/pc/pc_hgss",
"particle": "cobblemon:block/mod_station/pc/pc_hgss"
}
}
@@ -0,0 +1,7 @@
{
"parent": "cobblemon:block/pc_top_on",
"textures": {
"all": "cobblemon:block/mod_station/pc/pc_hgss",
"particle": "cobblemon:block/mod_station/pc/pc_hgss"
}
}

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