diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt index 47ffc4fdc..4f9fb145e 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/DataGen.kt @@ -51,6 +51,7 @@ import ru.dbotthepony.mc.otm.datagen.recipes.addDecorativesRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addPlatePressRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes +import ru.dbotthepony.mc.otm.datagen.recipes.addPainterRecipes import ru.dbotthepony.mc.otm.datagen.tags.TagsProvider import ru.dbotthepony.mc.otm.datagen.tags.addTags import ru.dbotthepony.mc.otm.matter.MatterDataProvider @@ -562,6 +563,7 @@ object DataGen { addDecorativesRecipes(recipeProvider, consumer) addShapelessRecipes(consumer) addOreSmeltingRecipes(consumer) + addPainterRecipes(consumer) } addPlatePressRecipes(recipeProvider) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index 447e72d02..b8efe97d4 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -120,6 +120,9 @@ private fun misc(provider: MatteryLanguageProvider) { gui("help.slot_filters", "Hold CTRL to setup slot filters") gui("help.slot_charging", "Hold ALT to switch slot charging") + gui("needs", "Needs %s") + gui("needs_x", "Needs %s x%d") + misc("needs_no_power", "Requires no power to operate") gui("lock_holo_screen", "Lock contents") @@ -428,6 +431,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.MATTER_RECONSTRUCTOR, "desc", "Repairs tools using matter") add(MBlocks.DEV_CHEST, "Dev Chest") add(MBlocks.DEV_CHEST, "desc", "Contains all items present in game") + add(MBlocks.PAINTER, "Painting Table") add(MBlocks.FLUID_TANK, "Fluid Tank") add(MBlocks.FLUID_TANK, "named", "Fluid Tank (%s)") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index 206dc139c..6bc478530 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -128,6 +128,9 @@ private fun misc(provider: MatteryLanguageProvider) { gui("help.slot_filters", "Удерживайте CTRL для настройки фильтрации слотов") gui("help.slot_charging", "Удерживайте ALT для переключения зарядки слотов") + gui("needs", "Требуется %s") + gui("needs_x", "Требуется %s x%d") + misc("needs_no_power", "Не требует энергии для работы") gui("lock_holo_screen", "Заблокировать содержимое") @@ -430,6 +433,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.MATTER_RECONSTRUCTOR, "desc", "Чинит инструменты используя материю") add(MBlocks.DEV_CHEST, "Сундук разработчика") add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре") + add(MBlocks.PAINTER, "Стол маляра") add(MBlocks.FLUID_TANK, "Жидкостный бак") add(MBlocks.FLUID_TANK, "named", "Жидкостный бак (%s)") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt index e5c1b8f4c..b21b8f15a 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt @@ -133,6 +133,7 @@ fun addLootTables(lootTables: LootTables) { lootTables.tile(MBlocks.ESSENCE_STORAGE) lootTables.tile(MBlocks.MATTER_RECONSTRUCTOR) lootTables.tile(MBlocks.FLUID_TANK) + lootTables.tile(MBlocks.PAINTER) lootTables.tile(MBlocks.ENERGY_SERVO) lootTables.tile(MBlocks.ENERGY_COUNTER) diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt index be441943c..6741a9151 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt @@ -433,4 +433,11 @@ fun addCraftingTableRecipes(consumer: Consumer) { .row(MItemTags.IRON_PLATES, MItemTags.IRON_PLATES, MItemTags.IRON_PLATES) .unlockedBy(Items.WATER_BUCKET) .build(consumer) + + MatteryRecipe(MItems.PAINTER, category = machinesCategory) + .row(Items.BRUSH, Items.BUCKET, Items.WATER_BUCKET) + .row(MItemTags.IRON_PLATES, Items.BUCKET, MItemTags.IRON_PLATES) + .row(MItemTags.IRON_PLATES, MItemTags.CRAFTING_TABLES, MItemTags.IRON_PLATES) + .unlockedBy(Tags.Items.DYES) + .build(consumer) } diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PainterRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PainterRecipes.kt new file mode 100644 index 000000000..f700cb50d --- /dev/null +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/PainterRecipes.kt @@ -0,0 +1,296 @@ +package ru.dbotthepony.mc.otm.datagen.recipes + +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.world.item.DyeColor +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.crafting.Ingredient +import ru.dbotthepony.mc.otm.core.registryName +import ru.dbotthepony.mc.otm.datagen.modLocation +import ru.dbotthepony.mc.otm.recipe.PainterRecipe +import ru.dbotthepony.mc.otm.registry.MItems +import ru.dbotthepony.mc.otm.registry.MRegistry +import java.util.function.Consumer + +private val Item.recipeName get() = registryName!!.namespace + "/" + registryName!!.path + +private fun generate(consumer: Consumer, items: Map, amount: Int = 1) { + for ((k1, v1) in items) { + if (k1 == null) continue + for ((k2, v2) in items) { + if (k2 == null || k1 == k2) continue + + consumer.accept(PainterRecipe( + modLocation("painter/" + v2.recipeName + "/" + v1.recipeName), + Ingredient.of(v1), + ItemStack(v2), + //if (k1 == DyeColor.WHITE || k2 == DyeColor.BLACK) setOf(k2) else setOf(DyeColor.BLACK, k2) + mapOf(k2 to amount) + ).toFinished()) + } + } +} + +private fun generate(consumer: Consumer, default: Item, items: Map, amount: Int = 1, cleaning: Boolean = true) { + generate(consumer, items) + + if (cleaning) + cleaning(consumer, default, items) + + for ((k1, v1) in items) { + if (k1 == null) continue + + consumer.accept(PainterRecipe( + modLocation("painter/" + default.recipeName + "/" + v1.recipeName), + Ingredient.of(default), + ItemStack(v1), + mapOf(k1 to amount) + ).toFinished()) + } +} + +private fun cleaning(consumer: Consumer, to: Item, from: Map) { + for ((k1, v1) in from) { + if (k1 == null) continue + + consumer.accept(PainterRecipe( + modLocation("painter/cleaning/" + to.recipeName + "/" + v1.recipeName), + Ingredient.of(v1), + ItemStack(to), + setOf() + ).toFinished()) + } +} + +private fun striped(consumer: Consumer, name: String, items: List>>, base: Map) { + for ((stripeItem, colors) in items) { + val (baseColor, stripe) = colors + + consumer.accept(PainterRecipe( + modLocation("painter/stripes_$name/${baseColor.getName()}/${stripe.getName()}"), + Ingredient.of(base[baseColor]), + ItemStack(stripeItem), + setOf(stripe) + ).toFinished()) + } +} + +fun addPainterRecipes(consumer: Consumer) { + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_WOOL, + DyeColor.ORANGE to Items.ORANGE_WOOL, + DyeColor.MAGENTA to Items.MAGENTA_WOOL, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_WOOL, + DyeColor.YELLOW to Items.YELLOW_WOOL, + DyeColor.LIME to Items.LIME_WOOL, + DyeColor.PINK to Items.PINK_WOOL, + DyeColor.GRAY to Items.GRAY_WOOL, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_WOOL, + DyeColor.CYAN to Items.CYAN_WOOL, + DyeColor.PURPLE to Items.PURPLE_WOOL, + DyeColor.BLUE to Items.BLUE_WOOL, + DyeColor.BROWN to Items.BROWN_WOOL, + DyeColor.GREEN to Items.GREEN_WOOL, + DyeColor.RED to Items.RED_WOOL, + DyeColor.BLACK to Items.BLACK_WOOL, + )) + + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_CARPET, + DyeColor.ORANGE to Items.ORANGE_CARPET, + DyeColor.MAGENTA to Items.MAGENTA_CARPET, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_CARPET, + DyeColor.YELLOW to Items.YELLOW_CARPET, + DyeColor.LIME to Items.LIME_CARPET, + DyeColor.PINK to Items.PINK_CARPET, + DyeColor.GRAY to Items.GRAY_CARPET, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_CARPET, + DyeColor.CYAN to Items.CYAN_CARPET, + DyeColor.PURPLE to Items.PURPLE_CARPET, + DyeColor.BLUE to Items.BLUE_CARPET, + DyeColor.BROWN to Items.BROWN_CARPET, + DyeColor.GREEN to Items.GREEN_CARPET, + DyeColor.RED to Items.RED_CARPET, + DyeColor.BLACK to Items.BLACK_CARPET, + )) + + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_BED, + DyeColor.ORANGE to Items.ORANGE_BED, + DyeColor.MAGENTA to Items.MAGENTA_BED, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_BED, + DyeColor.YELLOW to Items.YELLOW_BED, + DyeColor.LIME to Items.LIME_BED, + DyeColor.PINK to Items.PINK_BED, + DyeColor.GRAY to Items.GRAY_BED, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_BED, + DyeColor.CYAN to Items.CYAN_BED, + DyeColor.PURPLE to Items.PURPLE_BED, + DyeColor.BLUE to Items.BLUE_BED, + DyeColor.BROWN to Items.BROWN_BED, + DyeColor.GREEN to Items.GREEN_BED, + DyeColor.RED to Items.RED_BED, + DyeColor.BLACK to Items.BLACK_BED, + ), 3) + + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_CANDLE, + DyeColor.ORANGE to Items.ORANGE_CANDLE, + DyeColor.MAGENTA to Items.MAGENTA_CANDLE, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_CANDLE, + DyeColor.YELLOW to Items.YELLOW_CANDLE, + DyeColor.LIME to Items.LIME_CANDLE, + DyeColor.PINK to Items.PINK_CANDLE, + DyeColor.GRAY to Items.GRAY_CANDLE, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_CANDLE, + DyeColor.CYAN to Items.CYAN_CANDLE, + DyeColor.PURPLE to Items.PURPLE_CANDLE, + DyeColor.BLUE to Items.BLUE_CANDLE, + DyeColor.BROWN to Items.BROWN_CANDLE, + DyeColor.GREEN to Items.GREEN_CANDLE, + DyeColor.RED to Items.RED_CANDLE, + DyeColor.BLACK to Items.BLACK_CANDLE, + )) + + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_CONCRETE, + DyeColor.ORANGE to Items.ORANGE_CONCRETE, + DyeColor.MAGENTA to Items.MAGENTA_CONCRETE, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_CONCRETE, + DyeColor.YELLOW to Items.YELLOW_CONCRETE, + DyeColor.LIME to Items.LIME_CONCRETE, + DyeColor.PINK to Items.PINK_CONCRETE, + DyeColor.GRAY to Items.GRAY_CONCRETE, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_CONCRETE, + DyeColor.CYAN to Items.CYAN_CONCRETE, + DyeColor.PURPLE to Items.PURPLE_CONCRETE, + DyeColor.BLUE to Items.BLUE_CONCRETE, + DyeColor.BROWN to Items.BROWN_CONCRETE, + DyeColor.GREEN to Items.GREEN_CONCRETE, + DyeColor.RED to Items.RED_CONCRETE, + DyeColor.BLACK to Items.BLACK_CONCRETE, + )) + + generate(consumer, mapOf( + DyeColor.WHITE to Items.WHITE_CONCRETE_POWDER, + DyeColor.ORANGE to Items.ORANGE_CONCRETE_POWDER, + DyeColor.MAGENTA to Items.MAGENTA_CONCRETE_POWDER, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_CONCRETE_POWDER, + DyeColor.YELLOW to Items.YELLOW_CONCRETE_POWDER, + DyeColor.LIME to Items.LIME_CONCRETE_POWDER, + DyeColor.PINK to Items.PINK_CONCRETE_POWDER, + DyeColor.GRAY to Items.GRAY_CONCRETE_POWDER, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_CONCRETE_POWDER, + DyeColor.CYAN to Items.CYAN_CONCRETE_POWDER, + DyeColor.PURPLE to Items.PURPLE_CONCRETE_POWDER, + DyeColor.BLUE to Items.BLUE_CONCRETE_POWDER, + DyeColor.BROWN to Items.BROWN_CONCRETE_POWDER, + DyeColor.GREEN to Items.GREEN_CONCRETE_POWDER, + DyeColor.RED to Items.RED_CONCRETE_POWDER, + DyeColor.BLACK to Items.BLACK_CONCRETE_POWDER, + )) + + generate(consumer, MRegistry.CARGO_CRATES.item, MRegistry.CARGO_CRATES.items) + generate(consumer, MRegistry.TRITANIUM_BLOCK.item, MRegistry.TRITANIUM_BLOCK.items) + generate(consumer, MRegistry.TRITANIUM_STAIRS.item, MRegistry.TRITANIUM_STAIRS.items) + generate(consumer, MRegistry.TRITANIUM_SLAB.item, MRegistry.TRITANIUM_SLAB.items) + generate(consumer, MRegistry.TRITANIUM_WALL.item, MRegistry.TRITANIUM_WALL.items) + + generate(consumer, Items.TERRACOTTA, mapOf( + DyeColor.WHITE to Items.WHITE_TERRACOTTA, + DyeColor.ORANGE to Items.ORANGE_TERRACOTTA, + DyeColor.MAGENTA to Items.MAGENTA_TERRACOTTA, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_TERRACOTTA, + DyeColor.YELLOW to Items.YELLOW_TERRACOTTA, + DyeColor.LIME to Items.LIME_TERRACOTTA, + DyeColor.PINK to Items.PINK_TERRACOTTA, + DyeColor.GRAY to Items.GRAY_TERRACOTTA, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_TERRACOTTA, + DyeColor.CYAN to Items.CYAN_TERRACOTTA, + DyeColor.PURPLE to Items.PURPLE_TERRACOTTA, + DyeColor.BLUE to Items.BLUE_TERRACOTTA, + DyeColor.BROWN to Items.BROWN_TERRACOTTA, + DyeColor.GREEN to Items.GREEN_TERRACOTTA, + DyeColor.RED to Items.RED_TERRACOTTA, + DyeColor.BLACK to Items.BLACK_TERRACOTTA, + ), cleaning = false) + + generate(consumer, Items.SHULKER_BOX, mapOf( + DyeColor.WHITE to Items.WHITE_SHULKER_BOX, + DyeColor.ORANGE to Items.ORANGE_SHULKER_BOX, + DyeColor.MAGENTA to Items.MAGENTA_SHULKER_BOX, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_SHULKER_BOX, + DyeColor.YELLOW to Items.YELLOW_SHULKER_BOX, + DyeColor.LIME to Items.LIME_SHULKER_BOX, + DyeColor.PINK to Items.PINK_SHULKER_BOX, + DyeColor.GRAY to Items.GRAY_SHULKER_BOX, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_SHULKER_BOX, + DyeColor.CYAN to Items.CYAN_SHULKER_BOX, + DyeColor.PURPLE to Items.PURPLE_SHULKER_BOX, + DyeColor.BLUE to Items.BLUE_SHULKER_BOX, + DyeColor.BROWN to Items.BROWN_SHULKER_BOX, + DyeColor.GREEN to Items.GREEN_SHULKER_BOX, + DyeColor.RED to Items.RED_SHULKER_BOX, + DyeColor.BLACK to Items.BLACK_SHULKER_BOX, + )) + + generate(consumer, Items.GLASS, mapOf( + DyeColor.WHITE to Items.WHITE_STAINED_GLASS, + DyeColor.ORANGE to Items.ORANGE_STAINED_GLASS, + DyeColor.MAGENTA to Items.MAGENTA_STAINED_GLASS, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_STAINED_GLASS, + DyeColor.YELLOW to Items.YELLOW_STAINED_GLASS, + DyeColor.LIME to Items.LIME_STAINED_GLASS, + DyeColor.PINK to Items.PINK_STAINED_GLASS, + DyeColor.GRAY to Items.GRAY_STAINED_GLASS, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_STAINED_GLASS, + DyeColor.CYAN to Items.CYAN_STAINED_GLASS, + DyeColor.PURPLE to Items.PURPLE_STAINED_GLASS, + DyeColor.BLUE to Items.BLUE_STAINED_GLASS, + DyeColor.BROWN to Items.BROWN_STAINED_GLASS, + DyeColor.GREEN to Items.GREEN_STAINED_GLASS, + DyeColor.RED to Items.RED_STAINED_GLASS, + DyeColor.BLACK to Items.BLACK_STAINED_GLASS, + )) + + generate(consumer, Items.GLASS_PANE, mapOf( + DyeColor.WHITE to Items.WHITE_STAINED_GLASS_PANE, + DyeColor.ORANGE to Items.ORANGE_STAINED_GLASS_PANE, + DyeColor.MAGENTA to Items.MAGENTA_STAINED_GLASS_PANE, + DyeColor.LIGHT_BLUE to Items.LIGHT_BLUE_STAINED_GLASS_PANE, + DyeColor.YELLOW to Items.YELLOW_STAINED_GLASS_PANE, + DyeColor.LIME to Items.LIME_STAINED_GLASS_PANE, + DyeColor.PINK to Items.PINK_STAINED_GLASS_PANE, + DyeColor.GRAY to Items.GRAY_STAINED_GLASS_PANE, + DyeColor.LIGHT_GRAY to Items.LIGHT_GRAY_STAINED_GLASS_PANE, + DyeColor.CYAN to Items.CYAN_STAINED_GLASS_PANE, + DyeColor.PURPLE to Items.PURPLE_STAINED_GLASS_PANE, + DyeColor.BLUE to Items.BLUE_STAINED_GLASS_PANE, + DyeColor.BROWN to Items.BROWN_STAINED_GLASS_PANE, + DyeColor.GREEN to Items.GREEN_STAINED_GLASS_PANE, + DyeColor.RED to Items.RED_STAINED_GLASS_PANE, + DyeColor.BLACK to Items.BLACK_STAINED_GLASS_PANE, + )) + + generate(consumer, MRegistry.INDUSTRIAL_GLASS.item, MRegistry.INDUSTRIAL_GLASS.items) + generate(consumer, MRegistry.INDUSTRIAL_GLASS_PANE.item, MRegistry.INDUSTRIAL_GLASS_PANE.items) + generate(consumer, MRegistry.DECORATIVE_CRATE.item, MRegistry.DECORATIVE_CRATE.items) + generate(consumer, MRegistry.TRITANIUM_PRESSURE_PLATE.item, MRegistry.TRITANIUM_PRESSURE_PLATE.items) + generate(consumer, MItems.TRITANIUM_DOOR[null]!!, MItems.TRITANIUM_DOOR) + generate(consumer, MItems.TRITANIUM_TRAPDOOR[null]!!, MItems.TRITANIUM_TRAPDOOR) + + generate(consumer, MRegistry.VENT.item, MRegistry.VENT.items) + generate(consumer, MRegistry.VENT_ALTERNATIVE.item, MRegistry.VENT_ALTERNATIVE.items) + generate(consumer, MItems.CARGO_CRATE_MINECARTS[null]!!, MItems.CARGO_CRATE_MINECARTS) + + generate(consumer, MRegistry.UNREFINED_FLOOR_TILES.items) + generate(consumer, MRegistry.FLOOR_TILES.items) + generate(consumer, MRegistry.FLOOR_TILES_SLAB.items) + generate(consumer, MRegistry.FLOOR_TILES_STAIRS.items) + + striped(consumer, "full", MRegistry.TRITANIUM_STRIPED_BLOCK.itemsWithColor, MRegistry.TRITANIUM_BLOCK.items) + striped(consumer, "stairs", MRegistry.TRITANIUM_STRIPED_STAIRS.itemsWithColor, MRegistry.TRITANIUM_STAIRS.items) + striped(consumer, "walls", MRegistry.TRITANIUM_STRIPED_WALL.itemsWithColor, MRegistry.TRITANIUM_WALL.items) + striped(consumer, "slabs", MRegistry.TRITANIUM_STRIPED_SLAB.itemsWithColor, MRegistry.TRITANIUM_SLAB.items) +} diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt index c266a923c..26236c319 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/tags/Tags.kt @@ -145,6 +145,7 @@ fun addTags(tagsProvider: TagsProvider) { tagsProvider.requiresPickaxe(MBlocks.TRITANIUM_DOOR.values, Tiers.IRON) tagsProvider.requiresPickaxe(MBlocks.TRITANIUM_TRAPDOOR.values, Tiers.IRON) + tagsProvider.requiresPickaxe(MBlocks.PAINTER, Tiers.STONE) tagsProvider.requiresPickaxe(listOf( MBlocks.ANDROID_STATION, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PainterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PainterBlockEntity.kt new file mode 100644 index 000000000..46e00a325 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PainterBlockEntity.kt @@ -0,0 +1,197 @@ +package ru.dbotthepony.mc.otm.block.entity.tech + +import com.google.common.collect.ImmutableList +import net.minecraft.core.BlockPos +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.IntTag +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.Player +import net.minecraft.world.inventory.AbstractContainerMenu +import net.minecraft.world.item.DyeColor +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity +import ru.dbotthepony.mc.otm.container.HandlerFilter +import ru.dbotthepony.mc.otm.container.MatteryContainer +import ru.dbotthepony.mc.otm.core.immutableList +import ru.dbotthepony.mc.otm.core.immutableMap +import ru.dbotthepony.mc.otm.core.nbt.mapPresent +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.menu.tech.PainterMenu +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import java.util.* + +class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.PAINTER, blockPos, blockState) { + override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { + return PainterMenu(containerID, inventory, this) + } + + val dyeInput = MatteryContainer(this::setChangedLight, 1) + private val dyeStored = EnumMap(DyeColor::class.java) + val dyeStoredView: Map = Collections.unmodifiableMap(dyeStored) + + init { + addDroppableContainer(dyeInput) + savetables.stateful(dyeInput, INVENTORY_KEY) + } + + fun takeDyes(dyes: Map) { + for ((dye, amount) in dyes) { + for (i in 0 until amount) { + mixer(dye).mix(dyeStored) + + if (dyeStored(dye) > 0) { + dyeStored[dye] = dyeStored(dye) - 1 + } + } + } + + setChangedLight() + } + + val config = ConfigurableItemHandler(input = dyeInput.handler(object : HandlerFilter { + override fun canInsert(slot: Int, stack: ItemStack): Boolean { + val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return false + return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE + } + + override fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int { + if (!stack.equals(existing, false)) + return super.modifyInsertCount(slot, stack, existing, simulate) + + val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return 0 + return stack.count.coerceAtMost((MAX_STORAGE - dyeStored(dye)) / HUE_PER_ITEM - existing.count) + } + + override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { + return false + } + })) + + fun dyeStored(dye: DyeColor): Int { + return dyeStored[dye]!! + } + + init { + for (dye in DyeColor.entries) { + dyeStored[dye] = 0 + } + } + + override fun saveShared(nbt: CompoundTag) { + super.saveShared(nbt) + + nbt["dyes"] = CompoundTag().also { + for ((k, v) in dyeStored) { + it[k.getName()] = v + } + } + } + + override fun load(nbt: CompoundTag) { + super.load(nbt) + + for (dye in DyeColor.entries) { + dyeStored[dye] = 0 + } + + nbt.mapPresent("dyes") { it: CompoundTag -> + for (k in it.allKeys) { + dyeStored[DyeColor.entries.firstOrNull { it.getName() == k } ?: continue] = it.getInt(k) + } + } + } + + override fun tick() { + super.tick() + + val i = dyeInput.iterator() + + for (item in i) { + val dye = DyeColor.entries.firstOrNull { item.`is`(it.tag) } ?: continue + val stored = dyeStored(dye) + + if (stored + HUE_PER_ITEM <= MAX_STORAGE) { + item.shrink(1) + i.setChanged() + dyeStored[dye] = stored + HUE_PER_ITEM + } + } + } + + class Mixer(val color: DyeColor, vararg mixing: ImmutableList) : Map.Entry { + val mixing: ImmutableList> = ImmutableList.copyOf(mixing) + + override val key: DyeColor + get() = color + override val value: Mixer + get() = this + + private fun mix(input: MutableMap, seen: MutableSet, stack: MutableSet) { + if (input[color]!! > 0 || color in seen || color in stack) return + stack.add(color) + + for (ingredients in mixing) { + val copy = EnumMap(input) + ingredients.forEach { mixer(it).mix(copy, seen, stack) } + + if (ingredients.all { copy[it]!! > 0 }) { + ingredients.forEach { copy[it] = copy[it]!! - 1 } + copy[color] = ingredients.size + + input.putAll(copy) + stack.remove(color) + return + } + } + + stack.remove(color) + seen.add(color) + } + + fun mix(input: MutableMap) { + mix(input, EnumSet.noneOf(DyeColor::class.java), EnumSet.noneOf(DyeColor::class.java)) + } + + fun isAvailable(input: Map): Boolean { + if (input[color]!! > 0) return true + return EnumMap(input).also(::mix)[color]!! > 0 + } + } + + companion object { + fun mixer(color: DyeColor): Mixer { + return MIXING[color]!! + } + + val MIXING = immutableMap { + put(Mixer(DyeColor.WHITE, immutableList(DyeColor.RED, DyeColor.GREEN, DyeColor.BLUE))) + put(Mixer(DyeColor.LIGHT_GRAY, immutableList(DyeColor.BLACK, DyeColor.WHITE, DyeColor.WHITE))) + put(Mixer(DyeColor.GRAY, immutableList(DyeColor.BLACK, DyeColor.WHITE))) + put(Mixer(DyeColor.ORANGE, immutableList(DyeColor.YELLOW, DyeColor.RED))) + put(Mixer(DyeColor.LIME, immutableList(DyeColor.GREEN, DyeColor.WHITE))) + put(Mixer(DyeColor.LIGHT_BLUE, immutableList(DyeColor.BLUE, DyeColor.WHITE))) + put(Mixer(DyeColor.CYAN, immutableList(DyeColor.BLUE, DyeColor.GREEN))) + put(Mixer(DyeColor.PURPLE, immutableList(DyeColor.BLUE, DyeColor.RED))) + + put(Mixer( + DyeColor.MAGENTA, + immutableList(DyeColor.PURPLE, DyeColor.PINK), + immutableList(DyeColor.RED, DyeColor.RED, DyeColor.BLUE, DyeColor.WHITE), + immutableList(DyeColor.PINK, DyeColor.RED, DyeColor.BLUE) + )) + + put(Mixer(DyeColor.PINK, immutableList(DyeColor.RED, DyeColor.WHITE))) + + put(Mixer(DyeColor.RED, immutableList(DyeColor.MAGENTA, DyeColor.YELLOW))) + put(Mixer(DyeColor.GREEN, immutableList(DyeColor.CYAN, DyeColor.YELLOW))) + put(Mixer(DyeColor.BLUE, immutableList(DyeColor.CYAN, DyeColor.MAGENTA))) + put(Mixer(DyeColor.BLACK, immutableList(DyeColor.CYAN, DyeColor.MAGENTA, DyeColor.YELLOW))) + put(Mixer(DyeColor.YELLOW, immutableList(DyeColor.RED, DyeColor.GREEN))) + put(Mixer(DyeColor.BROWN, immutableList(DyeColor.MAGENTA, DyeColor.YELLOW, DyeColor.BLACK))) + } + + const val MAX_STORAGE = 256 + const val HUE_PER_ITEM = 32 + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/PainterBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/PainterBlock.kt new file mode 100644 index 000000000..391f7504e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/PainterBlock.kt @@ -0,0 +1,22 @@ +package ru.dbotthepony.mc.otm.block.tech + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +import ru.dbotthepony.mc.otm.block.entity.tech.PainterBlockEntity + +class PainterBlock : RotatableMatteryBlock(), EntityBlock { + override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity { + return PainterBlockEntity(p_153215_, p_153216_) + } + + override fun getTicker(p_153212_: Level, p_153213_: BlockState, p_153214_: BlockEntityType): BlockEntityTicker? { + if (p_153212_.isClientSide) return null + return BlockEntityTicker { p_155253_, p_155254_, p_155255_, p_155256_ -> if (p_155256_ is PainterBlockEntity) p_155256_.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt index 0f3da165c..eb41d2bac 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt @@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.client.render import net.minecraft.client.gui.GuiGraphics import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.core.math.RGBAColor +import ru.dbotthepony.mc.otm.systemTime +import kotlin.math.sin interface IGUIRenderable { /** @@ -76,14 +78,14 @@ interface IGUIRenderable { } /** - * Locks argument values to default ones and aligns render position to center of render rectangle + * Locks argument values to default ones and aligns render position to center of render rectangle (or specified [gravity]) * * e.g. for example, if we want [ItemStackIcon] to always render as 16x16 pixels icon, even if required render * dimensions are bigger (e.g. 18x18), after calling [fix] on [ItemStackIcon] it will always render as 16x16 icon, * positioned on center of render canvas (e.g. rendering on +0+0 with 18x18 size will put icon at +1+1 and render as 16x16; * rendering on +0+0 with 32x32 canvas will put icon at +8+8 and render as 16x16) */ - fun fixed(fixedWidth: Boolean = true, fixedHeight: Boolean = true, fixedWinding: Boolean = true): IGUIRenderable { + fun fixed(fixedWidth: Boolean = true, fixedHeight: Boolean = true, fixedWinding: Boolean = true, gravity: RenderGravity = RenderGravity.CENTER_CENTER): IGUIRenderable { if (!fixedHeight && !fixedWidth && !fixedWinding) return this return object : IGUIRenderable { @@ -97,11 +99,11 @@ interface IGUIRenderable { var realY = y if (fixedWidth && width > this.width) { - realX += (width - this.width) / 2f + realX += gravity.repositionX(width, this.width) } if (fixedHeight && height > this.height) { - realY += (height - this.height) / 2f + realY += gravity.repositionY(height, this.height) } this@IGUIRenderable.render(guiGraphics, realX, realY, if (fixedWidth) this.width else width, if (fixedHeight) this.height else height, if (fixedWinding) this.winding else winding, color) @@ -122,22 +124,22 @@ interface IGUIRenderable { data class ItemStackIcon(private val itemStack: ItemStack, override val width: Float = 16f, override val height: Float = 16f) : IGUIRenderable { override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) { - if (x % 1f == 0f && y % 1f == 0f && width == 16f && height == 16f) { - guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt()) - clearDepth(guiGraphics.pose(), x, y, width, height) - } else { - val pose = guiGraphics.pose() + val pose = guiGraphics.pose() - pose.pushPose() - pose.translate(x % 1f, y % 1f, 0f) + pose.pushPose() + pose.translate(x, y, 0f) - if (width != 16f || height != 16f) - pose.scale(width / 16f, height / 16f, 1f) + pose.scale(width / 16f, height / 16f, 1f) - guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt()) - pose.popPose() + guiGraphics.renderFakeItem(itemStack, 0, 0) + pose.popPose() - clearDepth(pose, x, y, width, height) - } + clearDepth(pose, x, y, width, height) + } +} + +data class FlatRectangleIcon(override val width: Float = 1f, override val height: Float = 1f, val color: RGBAColor) : IGUIRenderable { + override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) { + guiGraphics.renderRect(x, y, width, height, color = this.color) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index d7d9673ae..e8207d83c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -1608,7 +1608,7 @@ open class EditablePanel @JvmOverloads constructor( } protected open fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { - return true + return false } fun mouseScrolled(x: Double, y: Double, scroll: Double): Boolean { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/ScrollableCanvasPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/ScrollableCanvasPanel.kt new file mode 100644 index 000000000..4a9f266a0 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/util/ScrollableCanvasPanel.kt @@ -0,0 +1,72 @@ +package ru.dbotthepony.mc.otm.client.screen.panels.util + +import net.minecraft.client.gui.screens.Screen +import ru.dbotthepony.mc.otm.client.screen.panels.Dock +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel + +class ScrollableCanvasPanel( + screen: S, + parent: EditablePanel<*>, + x: Float = 0f, + y: Float = 0f, + width: Float = 40f, + height: Float = 40f, + slimScrollbar: Boolean = false +) : EditablePanel(screen, parent, x, y, width, height) { + val canvas = object : EditablePanel(screen, this@ScrollableCanvasPanel) { + init { + dock = Dock.FILL + dockRight = 1f + scissor = true + } + + override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { + return scrollbar.mouseScrolledInner(x, y, scroll) + } + + override fun performLayout() { + super.performLayout() + + var y = 0f + var maxHeight = 0f + var x = 0f + + for (child in visibleChildren.filter { it.dock == Dock.NONE }) { + if (x > 0f && x + child.width > this.width) { + y += maxHeight + x = 0f + maxHeight = 0f + } + + child.x = child.dockLeft + x + child.y = child.dockTop + y + x += child.width + child.dockMargin.horizontal + maxHeight = maxHeight.coerceAtLeast(child.height + child.dockMargin.vertical) + } + + updateBounds() + + if (getMaxScroll(scrollbar) <= scrollbar.scroll) { + scrollbar.scroll = getMaxScroll(scrollbar) + } + } + } + + val scrollbar = AnalogScrollBarPanel(screen, this, maxScroll = ::getMaxScroll, smoothScrollCallback = ::onScrollUpdate, isSlim = slimScrollbar) + + init { + scrollbar.dock = Dock.RIGHT + } + + override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { + return scrollbar.mouseScrolledInner(x, y, scroll) + } + + private fun getMaxScroll(it: AnalogScrollBarPanel<*>): Float { + return (canvas.childrenRectHeight - canvas.height).coerceAtLeast(0f) + } + + private fun onScrollUpdate(it: AnalogScrollBarPanel<*>, old: Float, new: Float) { + this.canvas.yOffset = -new + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt new file mode 100644 index 000000000..9882c3e27 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/PainterScreen.kt @@ -0,0 +1,152 @@ +package ru.dbotthepony.mc.otm.client.screen.tech + +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.network.chat.Component +import net.minecraft.util.RandomSource +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.item.DyeColor +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraftforge.common.Tags +import net.minecraftforge.registries.ForgeRegistries +import ru.dbotthepony.mc.otm.block.entity.tech.PainterBlockEntity +import ru.dbotthepony.mc.otm.client.render.FlatRectangleIcon +import ru.dbotthepony.mc.otm.client.render.ItemStackIcon +import ru.dbotthepony.mc.otm.client.render.renderRect +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen +import ru.dbotthepony.mc.otm.client.screen.panels.Dock +import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode +import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel +import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls +import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel +import ru.dbotthepony.mc.otm.client.screen.panels.button.RectangleButtonPanel +import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel +import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel +import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollableCanvasPanel +import ru.dbotthepony.mc.otm.core.TextComponent +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.math.RGBAColor +import ru.dbotthepony.mc.otm.menu.tech.PainterMenu +import ru.dbotthepony.mc.otm.registry.MItems + +class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) : MatteryScreen(menu, inventory, title) { + inner class Bar(parent: EditablePanel<*>, val dye: DyeColor) : EditablePanel(this@PainterScreen, parent, width = 5f) { + init { + dock = Dock.RIGHT + dockLeft = 1f + tooltips.add(TranslatableComponent("item.minecraft.${dye.getName()}_dye")) + tooltips.add(TextComponent("")) + tooltips.add(TextComponent("")) + } + + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + graphics.renderRect(0f, 0f, width, height, color = RGBAColor.DARK_GRAY) + + val color = RGBAColor.rgb(dye.textColor) + graphics.renderRect(0f, 0f, width, height, color = color.copy(alpha = 0.4f)) + + val multiplier = menu.dyeStoredDirect[dye]!!.toFloat() / PainterBlockEntity.MAX_STORAGE.toFloat() + graphics.renderRect(0f, height * (1f - multiplier), width, height * multiplier, color = color) + + tooltips[tooltips.size - 1] = TextComponent("${menu.dyeStoredDirect[dye]} (${(multiplier * 100f).toInt()}%)") + } + } + + override fun makeMainFrame(): FramePanel> { + val frame = FramePanel(this, 200f, 120f, title) + + val strip = EditablePanel(this, frame, width = AbstractSlotPanel.SIZE) + strip.dock = Dock.LEFT + strip.dockRight = 4f + + SlotPanel(this, strip, menu.inputSlot).also { + it.dock = Dock.TOP + it.dockTop = 4f + } + + EditablePanel(this, frame, width = 6f * DyeColor.entries.size / 2f).also { + it.dock = Dock.RIGHT + it.dockLeft = 4f + + EditablePanel(this, it, height = 46f).also { + it.dock = Dock.TOP + + for (i in 0 until DyeColor.entries.size / 2) { + Bar(it, DyeColor.entries[i]) + } + } + + EditablePanel(this, it, height = 46f).also { + it.dock = Dock.BOTTOM + + for (i in DyeColor.entries.size / 2 until DyeColor.entries.size) { + Bar(it, DyeColor.entries[i]) + } + } + } + + SlotPanel(this, strip, menu.outputSlot).also { + it.dock = Dock.BOTTOM + it.dockBottom = 4f + } + + SlotPanel(this, strip, menu.dyeSlot).also { + it.dock = Dock.FILL + it.dockResize = DockResizeMode.NONE + it.slotBackgroundEmpty = + ItemStackIcon(ItemStack(ForgeRegistries.ITEMS.tags()!!.getTag(Tags.Items.DYES).getRandomElement(RandomSource.create()).orElse(Items.AIR)), 16f, 16f) + .fixed() + .composeBefore(FlatRectangleIcon(16f, 16f, RGBAColor.rgb(0x8b8b8b).copy(alpha = 0.6f))) + .fixed() + } + + val canvas = ScrollableCanvasPanel(this, frame) + canvas.dock = Dock.FILL + + val buttons = ArrayList>() + + menu.listeners.addListener { + buttons.forEach { it.remove() } + buttons.clear() + + for (recipe in menu.possibleRecipes) { + object : LargeRectangleButtonPanel(this@PainterScreen, canvas.canvas, skinElement = ItemStackIcon(recipe.output, 14f, 14f).fixed()) { + init { + buttons.add(this) + dockRight = 1f + dockBottom = 1f + } + + override var isDisabled: Boolean + get() = !recipe.canCraft(menu.dyeStoredDirect) + set(value) {} + + override fun innerRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { + val list = getTooltipFromItem(minecraft!!, recipe.output) + + recipe.dyes.forEach { + val (dye, amount) = it + + if (amount == 1) + list.add(TranslatableComponent("otm.gui.needs", TranslatableComponent("item.minecraft.${dye.getName()}_dye"))) + else if (amount > 1) + list.add(TranslatableComponent("otm.gui.needs_x", TranslatableComponent("item.minecraft.${dye.getName()}_dye"), amount)) + } + + graphics.renderComponentTooltip(font, list, mouseX.toInt(), mouseY.toInt()) + return true + } + + override fun onClick(mouseButton: Int) { + menu.selectRecipe.accept(recipe.id) + } + } + } + } + + DeviceControls(this, frame, itemConfig = menu.itemConfig) + + return frame + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/IGUIRenderable2IDrawable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/IGUIRenderable2IDrawable.kt new file mode 100644 index 000000000..7b168ca96 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/IGUIRenderable2IDrawable.kt @@ -0,0 +1,19 @@ +package ru.dbotthepony.mc.otm.compat.jei + +import mezz.jei.api.gui.drawable.IDrawable +import net.minecraft.client.gui.GuiGraphics +import ru.dbotthepony.mc.otm.client.render.IGUIRenderable + +class IGUIRenderable2IDrawable(val parent: IGUIRenderable) : IDrawable { + override fun getWidth(): Int { + return parent.width.toInt() + } + + override fun getHeight(): Int { + return parent.height.toInt() + } + + override fun draw(guiGraphics: GuiGraphics, xOffset: Int, yOffset: Int) { + parent.render(guiGraphics, xOffset.toFloat(), yOffset.toFloat()) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt index 3c73c955c..bec5cb7fb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt @@ -70,17 +70,20 @@ class JEIPlugin : IModPlugin { registration.addRecipeCatalyst(ItemStack(MItems.POWERED_SMOKER), RecipeTypes.SMOKING) registration.addRecipeCatalyst(ItemStack(MItems.ExopackUpgrades.CRAFTING_UPGRADE), RecipeTypes.CRAFTING) registration.addRecipeCatalyst(ItemStack(MItems.PLATE_PRESS), PlatePressRecipeCategory.recipeType) + registration.addRecipeCatalyst(ItemStack(MItems.PAINTER), PainterRecipeCategory.recipeType) } override fun registerCategories(registration: IRecipeCategoryRegistration) { helpers = registration.jeiHelpers registration.addRecipeCategories(PlatePressRecipeCategory) + registration.addRecipeCategories(PainterRecipeCategory) } override fun registerRecipes(registration: IRecipeRegistration) { val level = minecraft.level ?: throw NullPointerException("No ClientLevel. OLOLOLOLOLOLO") registration.addRecipes(PlatePressRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PLATE_PRESS).filter { !it.isIncomplete }) + registration.addRecipes(PainterRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PAINTER).filter { !it.isIncomplete }) } override fun registerRecipeTransferHandlers(registration: IRecipeTransferRegistration) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt new file mode 100644 index 000000000..f5d937bf8 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/PainterRecipeCategory.kt @@ -0,0 +1,82 @@ +package ru.dbotthepony.mc.otm.compat.jei + +import mezz.jei.api.constants.VanillaTypes +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder +import mezz.jei.api.gui.drawable.IDrawable +import mezz.jei.api.recipe.IFocusGroup +import mezz.jei.api.recipe.RecipeIngredientRole +import mezz.jei.api.recipe.RecipeType +import mezz.jei.api.recipe.category.IRecipeCategory +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.client.render.ItemStackIcon +import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel +import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel +import ru.dbotthepony.mc.otm.recipe.PainterRecipe +import ru.dbotthepony.mc.otm.registry.MItems +import ru.dbotthepony.mc.otm.registry.MNames + +object PainterRecipeCategory : IRecipeCategory, IDrawable { + private val type = RecipeType(ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.PAINTER), PainterRecipe::class.java) + + override fun getRecipeType(): RecipeType { + return type + } + + override fun getTitle(): Component { + return MItems.PAINTER.description + } + + override fun getBackground(): IDrawable { + return this + } + + override fun getWidth(): Int { + return 120 + } + + override fun getHeight(): Int { + return 56 + } + + override fun getIcon(): IDrawable { + return IGUIRenderable2IDrawable(ItemStackIcon(ItemStack(MItems.PAINTER))) + } + + override fun setRecipe(builder: IRecipeLayoutBuilder, recipe: PainterRecipe, focuses: IFocusGroup) { + var x = 0 + var y = 0 + + for ((dye, count) in recipe.dyes) { + builder.addSlot(RecipeIngredientRole.INPUT, 1 + x * 18, 1 + y * 18) + .addIngredients(VanillaTypes.ITEM_STACK, Ingredient.of(dye.tag).items.map { it.copyWithCount(count) }) + + y++ + + if (y >= 3) { + x++ + y = 0 + } + } + + builder.addSlot(RecipeIngredientRole.INPUT, 50, 22).addIngredients(recipe.input) + builder.addSlot(RecipeIngredientRole.OUTPUT, 100, 22).addIngredients(VanillaTypes.ITEM_STACK, listOf(recipe.output)) + } + + override fun draw(guiGraphics: GuiGraphics, xOffset: Int, yOffset: Int) { + for (x in 0 .. 1) { + for (y in 0 .. 2) { + AbstractSlotPanel.SLOT_BACKGROUND.render(guiGraphics, xOffset.toFloat() + x * 18f, yOffset.toFloat() + y * 18f) + } + } + + AbstractSlotPanel.SLOT_BACKGROUND.render(guiGraphics, xOffset.toFloat() + 49f, yOffset.toFloat() + 21f) + AbstractSlotPanel.SLOT_BACKGROUND.render(guiGraphics, xOffset.toFloat() + 99f, yOffset.toFloat() + 21f) + + ProgressGaugePanel.GAUGE_BACKGROUND.render(guiGraphics, xOffset.toFloat() + 73f, yOffset.toFloat() + 22f) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/SkinDrawable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/SkinDrawable.kt deleted file mode 100644 index 6ac46e51f..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/SkinDrawable.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.dbotthepony.mc.otm.compat.jei - -import mezz.jei.api.gui.drawable.IDrawable -import net.minecraft.client.gui.GuiGraphics -import ru.dbotthepony.mc.otm.client.render.sprites.MatterySprite - -class SkinDrawable(val element: MatterySprite) : IDrawable { - override fun getWidth(): Int { - return element.width.toInt() - } - - override fun getHeight(): Int { - return element.height.toInt() - } - - override fun draw(graphics: GuiGraphics, xOffset: Int, yOffset: Int) { - element.render(graphics, xOffset.toFloat(), yOffset.toFloat()) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/StretchingDrawable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/StretchingDrawable.kt deleted file mode 100644 index 72c2c96eb..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/StretchingDrawable.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.dbotthepony.mc.otm.compat.jei - -import mezz.jei.api.gui.drawable.IDrawable -import net.minecraft.client.gui.GuiGraphics -import ru.dbotthepony.mc.otm.client.render.sprites.StretchingRectangleElement - -class StretchingDrawable(val element: StretchingRectangleElement, val rectWidth: Int, val rectHeight: Int) : IDrawable { - override fun getWidth(): Int { - return rectWidth - } - - override fun getHeight(): Int { - return rectHeight - } - - override fun draw(graphics: GuiGraphics, xOffset: Int, yOffset: Int) { - element.render(graphics, xOffset.toFloat(), yOffset.toFloat(), rectWidth.toFloat(), rectHeight.toFloat()) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt index 6a3a4f457..c4fd2a636 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt @@ -16,20 +16,27 @@ class ContainerHandler @JvmOverloads internal constructor( return stack filter.preInsert(slot, stack, simulate) + val localStack = container[slot] + var amount = filter.modifyInsertCount(slot, stack, localStack, simulate) + + if (amount <= 0) + return stack if (localStack.isEmpty) { + amount = stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)).coerceAtMost(amount) + if (!simulate) { - container.setItem(slot, stack.copyWithCount(stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)))) + container.setItem(slot, stack.copyWithCount(amount)) } - if (stack.count <= container.getMaxStackSize(slot, stack)) { + if (stack.count <= amount) { return ItemStack.EMPTY } else { - return stack.copyWithCount(container.getMaxStackSize(slot, stack)) + return stack.copyWithCount(stack.count - amount) } } else if (localStack.isStackable && container.getMaxStackSize(slot, localStack) > localStack.count && ItemStack.isSameItemSameTags(localStack, stack)) { - val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count) + val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count.coerceAtMost(amount)) val diff = newCount - localStack.count if (diff != 0) { @@ -51,12 +58,16 @@ class ContainerHandler @JvmOverloads internal constructor( if (amount <= 0 || container.isSlotForbiddenForAutomation(slot)) return ItemStack.EMPTY - filter.preExtract(slot, amount, simulate) - val localStack = container.getItem(slot) if (localStack.isEmpty) return ItemStack.EMPTY + + @Suppress("name_shadowing") + val amount = filter.modifyExtractCount(slot, amount, simulate) + if (amount <= 0) return ItemStack.EMPTY if (!filter.canExtract(slot, amount, localStack)) return ItemStack.EMPTY + filter.preExtract(slot, amount, simulate) + val minimal = amount.coerceAtMost(localStack.count) val copy = localStack.copy() copy.count = minimal diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/HandlerFilter.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/HandlerFilter.kt index ce8051302..c540621f0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/HandlerFilter.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/HandlerFilter.kt @@ -19,6 +19,14 @@ interface HandlerFilter { fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {} fun preExtract(slot: Int, amount: Int, simulate: Boolean) {} + fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int { + return stack.count + } + + fun modifyExtractCount(slot: Int, amount: Int, simulate: Boolean): Int { + return amount + } + fun and(other: HandlerFilter): HandlerFilter { return object : HandlerFilter { override fun canInsert(slot: Int, stack: ItemStack): Boolean { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 239d59990..af897288a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -175,6 +175,13 @@ inline fun immutableList(initializer: Consumer.() -> Unit): Immutab return builder.build() } +fun immutableList(a: V, vararg values: V): ImmutableList { + val builder = ImmutableList.Builder() + builder.add(a) + builder.addAll(values.iterator()) + return builder.build() +} + fun IForgeRegistry.getID(value: T): Int { return (this as ForgeRegistry).getID(value) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt index 246401c95..ac4fbba42 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/FriendlyStreams.kt @@ -5,6 +5,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtIo import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.level.material.Fluid @@ -327,3 +328,12 @@ fun OutputStream.writeBinaryString(input: String) { writeVarIntLE(bytes.size) write(bytes) } + +fun InputStream.readResourceLocation(sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): ResourceLocation { + return ResourceLocation(readBinaryString(sizeLimit), readBinaryString(sizeLimit)) +} + +fun OutputStream.writeResourceLocation(value: ResourceLocation) { + writeBinaryString(value.namespace) + writeBinaryString(value.path) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt index 5b15f435d..55148826b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/StreamCodecs.kt @@ -122,6 +122,7 @@ val UUIDValueCodec = StreamCodec({ s, a -> a.accountBytes(8L); UUID(s.readLong() val VarIntValueCodec = StreamCodec(DataInputStream::readVarIntLE, DataOutputStream::writeVarIntLE) { a, b -> a == b } val VarLongValueCodec = StreamCodec(DataInputStream::readVarLongLE, DataOutputStream::writeVarLongLE) { a, b -> a == b } val BinaryStringCodec = StreamCodec(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString) +val ResourceLocationValueCodec = StreamCodec(DataInputStream::readResourceLocation, DataOutputStream::writeResourceLocation) val RGBCodec: StreamCodec = StreamCodec( { s, a -> a.accountBytes(12L); RGBAColor(s.readFloat(), s.readFloat(), s.readFloat()) }, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt new file mode 100644 index 000000000..55466ffd1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/PainterMenu.kt @@ -0,0 +1,119 @@ +package ru.dbotthepony.mc.otm.menu.tech + +import net.minecraft.world.SimpleContainer +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.DyeColor +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.block.entity.tech.PainterBlockEntity +import ru.dbotthepony.mc.otm.container.MatteryContainer +import ru.dbotthepony.mc.otm.core.ISubscriptable +import ru.dbotthepony.mc.otm.core.addAll +import ru.dbotthepony.mc.otm.core.collect.SupplierMap +import ru.dbotthepony.mc.otm.core.collect.filter +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.map +import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator +import ru.dbotthepony.mc.otm.core.util.ResourceLocationValueCodec +import ru.dbotthepony.mc.otm.menu.MatteryMenu +import ru.dbotthepony.mc.otm.menu.MatterySlot +import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput +import ru.dbotthepony.mc.otm.recipe.PainterRecipe +import ru.dbotthepony.mc.otm.registry.MMenus +import ru.dbotthepony.mc.otm.registry.MRecipes +import java.util.* +import java.util.function.IntConsumer +import kotlin.collections.ArrayList + +class PainterMenu( + containerId: Int, inventory: Inventory, tile: PainterBlockEntity? = null +) : MatteryMenu(MMenus.PAINTER, containerId, inventory, tile) { + val dyeStored = DyeColor.entries.associateWith { dye -> + mSynchronizer.ComputedIntField({ tile?.dyeStored(dye) ?: 0 }).also { it.addListener(IntConsumer { rescan() }) } + } + + val dyeStoredDirect = SupplierMap(dyeStored) + val itemConfig = ItemConfigPlayerInput(this, tile?.config) + + val inputContainer = MatteryContainer(::rescan, 1) + val outputContainer = MatteryContainer(1) + private var lastRecipe: PainterRecipe? = null + var selectedRecipe by mSynchronizer.Field(null, ResourceLocationValueCodec.nullable).also { it.addListener { rescan() } } + + val selectRecipe = PlayerInput(ResourceLocationValueCodec) { + selectedRecipe = it + } + + val inputSlot = object : MatterySlot(inputContainer, 0) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return super.mayPlace(itemStack) && inventory.player.level().recipeManager.byType(MRecipes.PAINTER).values.any { it.input.test(itemStack) } + } + } + + val outputSlot = object : MatterySlot(outputContainer, 0) { + override fun tryRemove(p_150642_: Int, p_150643_: Int, p_150644_: Player): Optional { + rescan() + return super.tryRemove(p_150642_, p_150643_, p_150644_) + } + + override fun onTake(p_150645_: Player, p_150646_: ItemStack) { + if (p_150646_.isNotEmpty) { + lastRecipe?.dyes?.let { tile?.takeDyes(it) } + inputContainer.removeItem(0, 1) + } + + super.onTake(p_150645_, p_150646_) + } + + override fun mayPlace(itemStack: ItemStack): Boolean { + return false + } + } + + val dyeSlot = object : MatterySlot(tile?.dyeInput ?: SimpleContainer(1), 0) { + override fun mayPlace(itemStack: ItemStack): Boolean { + return super.mayPlace(itemStack) && (DyeColor.getColor(itemStack)?.let { dyeStoredDirect[it]!! + PainterBlockEntity.HUE_PER_ITEM <= PainterBlockEntity.MAX_STORAGE } ?: false) + } + } + + init { + addSlot(outputSlot) + addSlot(dyeSlot) + + addStorageSlot(dyeSlot) + addStorageSlot(inputSlot) + mapQuickMoveToInventory(outputSlot) + mapQuickMoveToInventory(dyeSlot) + + addInventorySlots() + } + + override fun removed(p_38940_: Player) { + super.removed(p_38940_) + clearContainer(p_38940_, inputContainer) + } + + val listeners = ISubscriptable.Impl() + val possibleRecipes = ArrayList() + + private fun rescan() { + possibleRecipes.clear() + possibleRecipes.addAll(inventory.player.level().recipeManager.byType(MRecipes.PAINTER).values.iterator().filter { it.input.test(inputContainer[0]) }) + possibleRecipes.sortWith(CreativeMenuItemComparator.map { it.output.item }) + listeners.accept(Unit) + if (tile !is PainterBlockEntity) return + + if (inputContainer.isEmpty || selectedRecipe == null) { + outputContainer.clearContent() + } else { + val recipe = inventory.player.level().recipeManager.byType(MRecipes.PAINTER)[selectedRecipe] + + if (recipe == null || !recipe.canCraft(dyeStoredDirect) || !recipe.matches(inputContainer, inventory.player.level())) { + outputContainer.clearContent() + } else { + outputContainer[0] = recipe.assemble(inputContainer, inventory.player.level().registryAccess()) + lastRecipe = recipe + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt new file mode 100644 index 000000000..158cab1df --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/PainterRecipe.kt @@ -0,0 +1,135 @@ +package ru.dbotthepony.mc.otm.recipe + +import com.google.common.collect.ImmutableSet +import com.mojang.datafixers.util.Either +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.core.NonNullList +import net.minecraft.core.RegistryAccess +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.Container +import net.minecraft.world.item.DyeColor +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.Recipe +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.item.crafting.RecipeType +import net.minecraft.world.level.Level +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.block.entity.tech.PainterBlockEntity +import ru.dbotthepony.mc.otm.container.get +import ru.dbotthepony.mc.otm.core.isActuallyEmpty +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.core.tagNotNull +import ru.dbotthepony.mc.otm.data.Codec2RecipeSerializer +import ru.dbotthepony.mc.otm.data.CodecList +import ru.dbotthepony.mc.otm.data.IngredientCodec +import ru.dbotthepony.mc.otm.data.PredicatedCodecList +import ru.dbotthepony.mc.otm.data.minRange +import ru.dbotthepony.mc.otm.registry.MRecipes +import java.util.EnumMap +import java.util.function.Predicate + +class PainterRecipe( + private val id: ResourceLocation, + val input: Ingredient, + val output: ItemStack, + val dyes: Map +) : Recipe { + constructor( + id: ResourceLocation, + input: Ingredient, + output: ItemStack, + dyes: Set + ) : this(id, input, output, dyes.associateWith { 1 }) + + fun canCraft(storedDyes: Map): Boolean { + if (isIncomplete) return false + if (dyes.isEmpty() || dyes.values.none { it > 0 }) return true + val copy = EnumMap(storedDyes) + + for ((dye, amount) in dyes) { + for (i in 0 until amount) { + PainterBlockEntity.mixer(dye).mix(copy) + if (copy[dye]!! <= 0) return false + copy[dye] = copy[dye]!! - 1 + } + } + + return true + } + + override fun matches(p_44002_: Container, p_44003_: Level): Boolean { + if (isIncomplete) return false + return input.test(p_44002_[0]) + } + + override fun isIncomplete(): Boolean { + return input.isActuallyEmpty || output.isEmpty + } + + override fun getIngredients(): NonNullList { + return NonNullList.of(Ingredient.EMPTY, input) + } + + override fun assemble(p_44001_: Container, p_267165_: RegistryAccess): ItemStack { + return output.copy().also { o -> + p_44001_[0].tag?.let { + if (o.tag == null) { + o.tag = it.copy() + } else { + for (k in it.allKeys) + if (k !in o.tagNotNull) + o.tagNotNull[k] = it[k]!!.copy() + } + } + } + } + + override fun canCraftInDimensions(p_43999_: Int, p_44000_: Int): Boolean { + return true + } + + override fun isSpecial(): Boolean { + return true + } + + override fun getResultItem(p_267052_: RegistryAccess): ItemStack { + return output + } + + override fun getId(): ResourceLocation { + return id + } + + override fun getSerializer(): RecipeSerializer<*> { + return SERIALIZER + } + + override fun getType(): RecipeType<*> { + return MRecipes.PAINTER + } + + fun toFinished(): FinishedRecipe { + return SERIALIZER.toFinished(this) + } + + companion object { + val SERIALIZER = Codec2RecipeSerializer( + PainterRecipe(ResourceLocation(OverdriveThatMatters.MOD_ID, "empty"), Ingredient.EMPTY, ItemStack.EMPTY, setOf()) + ) { context -> + RecordCodecBuilder.create { + it.group( + IngredientCodec.fieldOf("input").forGetter(PainterRecipe::input), + ItemStack.CODEC.fieldOf("output").forGetter(PainterRecipe::output), + PredicatedCodecList>( + DyeColor.CODEC.xmap({ mapOf(it to 1) }, { it.keys.first() }) to Predicate { it.keys.size == 1 && it.values.first() == 1 }, + Codec.list(DyeColor.CODEC).xmap({ it.associateWith { 1 } }, { ArrayList(it.keys) }) to Predicate { it.values.all { it == 1 } }, + Codec.unboundedMap(DyeColor.CODEC, Codec.INT.minRange(1)) to Predicate { true } + ).fieldOf("dyes").forGetter(PainterRecipe::dyes), + ).apply(it) { a, b, c -> PainterRecipe(context.invoke(), a, b, c) } + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt index 46cfaf0c5..4dfd27172 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -1,11 +1,14 @@ package ru.dbotthepony.mc.otm.registry import net.minecraft.client.renderer.blockentity.BlockEntityRenderers +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraftforge.eventbus.api.IEventBus import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent import net.minecraftforge.registries.DeferredRegister import net.minecraftforge.registries.ForgeRegistries +import net.minecraftforge.registries.RegistryObject import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.block.entity.* import ru.dbotthepony.mc.otm.block.entity.tech.* @@ -25,43 +28,49 @@ import ru.dbotthepony.mc.otm.block.entity.tech.EnergyServoBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity import ru.dbotthepony.mc.otm.client.render.blockentity.* +import java.util.function.Supplier @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") // Type<*> is unused in BlockEntityType.Builder object MBlockEntities { private val registry = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, OverdriveThatMatters.MOD_ID) - val ANDROID_STATION: BlockEntityType by registry.register(MNames.ANDROID_STATION) { BlockEntityType.Builder.of(::AndroidStationBlockEntity, MBlocks.ANDROID_STATION).build(null) } - val BATTERY_BANK: BlockEntityType by registry.register(MNames.BATTERY_BANK) { BlockEntityType.Builder.of(::BatteryBankBlockEntity, MBlocks.BATTERY_BANK).build(null) } - val MATTER_DECOMPOSER: BlockEntityType by registry.register(MNames.MATTER_DECOMPOSER) { BlockEntityType.Builder.of(::MatterDecomposerBlockEntity, MBlocks.MATTER_DECOMPOSER).build(null) } - val MATTER_CAPACITOR_BANK: BlockEntityType by registry.register(MNames.MATTER_CAPACITOR_BANK) { BlockEntityType.Builder.of(::MatterCapacitorBankBlockEntity, MBlocks.MATTER_CAPACITOR_BANK).build(null) } - val MATTER_CABLE: BlockEntityType by registry.register(MNames.MATTER_CABLE) { BlockEntityType.Builder.of(::MatterCableBlockEntity, MBlocks.MATTER_CABLE).build(null) } - val STORAGE_CABLE: BlockEntityType by registry.register(MNames.STORAGE_CABLE) { BlockEntityType.Builder.of(::StorageCableBlockEntity, MBlocks.STORAGE_CABLE).build(null) } - val PATTERN_STORAGE: BlockEntityType by registry.register(MNames.PATTERN_STORAGE) { BlockEntityType.Builder.of(::PatternStorageBlockEntity, MBlocks.PATTERN_STORAGE).build(null) } - val MATTER_SCANNER: BlockEntityType by registry.register(MNames.MATTER_SCANNER) { BlockEntityType.Builder.of(::MatterScannerBlockEntity, MBlocks.MATTER_SCANNER).build(null) } - val MATTER_PANEL: BlockEntityType by registry.register(MNames.MATTER_PANEL) { BlockEntityType.Builder.of(::MatterPanelBlockEntity, MBlocks.MATTER_PANEL).build(null) } - val MATTER_REPLICATOR: BlockEntityType by registry.register(MNames.MATTER_REPLICATOR) { BlockEntityType.Builder.of(::MatterReplicatorBlockEntity, MBlocks.MATTER_REPLICATOR).build(null) } - val MATTER_BOTTLER: BlockEntityType by registry.register(MNames.MATTER_BOTTLER) { BlockEntityType.Builder.of(::MatterBottlerBlockEntity, MBlocks.MATTER_BOTTLER).build(null) } - val DRIVE_VIEWER: BlockEntityType by registry.register(MNames.DRIVE_VIEWER) { BlockEntityType.Builder.of(::DriveViewerBlockEntity, MBlocks.DRIVE_VIEWER).build(null) } - val BLACK_HOLE: BlockEntityType by registry.register(MNames.BLACK_HOLE) { BlockEntityType.Builder.of(::BlackHoleBlockEntity, MBlocks.BLACK_HOLE).build(null) } - val CARGO_CRATE: BlockEntityType by registry.register(MNames.CARGO_CRATE) { BlockEntityType.Builder.of(::CargoCrateBlockEntity, *MRegistry.CARGO_CRATES.blocks.values.toTypedArray()).build(null) } - val DRIVE_RACK: BlockEntityType by registry.register(MNames.DRIVE_RACK) { BlockEntityType.Builder.of(::DriveRackBlockEntity, MBlocks.DRIVE_RACK).build(null) } - val ITEM_MONITOR: BlockEntityType by registry.register(MNames.ITEM_MONITOR) { BlockEntityType.Builder.of(::ItemMonitorBlockEntity, MBlocks.ITEM_MONITOR).build(null) } - val ENERGY_COUNTER: BlockEntityType by registry.register(MNames.ENERGY_COUNTER) { BlockEntityType.Builder.of(::EnergyCounterBlockEntity, MBlocks.ENERGY_COUNTER).build(null) } - val CHEMICAL_GENERATOR: BlockEntityType by registry.register(MNames.CHEMICAL_GENERATOR) { BlockEntityType.Builder.of(::ChemicalGeneratorBlockEntity, MBlocks.CHEMICAL_GENERATOR).build(null) } - val PLATE_PRESS: BlockEntityType by registry.register(MNames.PLATE_PRESS) { BlockEntityType.Builder.of(::PlatePressBlockEntity, MBlocks.PLATE_PRESS).build(null) } - val TWIN_PLATE_PRESS: BlockEntityType by registry.register(MNames.TWIN_PLATE_PRESS) { BlockEntityType.Builder.of({ a, b -> PlatePressBlockEntity(a, b, true) }, MBlocks.TWIN_PLATE_PRESS).build(null) } - val GRAVITATION_STABILIZER: BlockEntityType by registry.register(MNames.GRAVITATION_STABILIZER) { BlockEntityType.Builder.of(::GravitationStabilizerBlockEntity, MBlocks.GRAVITATION_STABILIZER).build(null) } - val MATTER_RECYCLER: BlockEntityType by registry.register(MNames.MATTER_RECYCLER) { BlockEntityType.Builder.of(::MatterRecyclerBlockEntity, MBlocks.MATTER_RECYCLER).build(null) } - val ENERGY_SERVO: BlockEntityType by registry.register(MNames.ENERGY_SERVO) { BlockEntityType.Builder.of(::EnergyServoBlockEntity, MBlocks.ENERGY_SERVO).build(null) } - val COBBLESTONE_GENERATOR: BlockEntityType by registry.register(MNames.COBBLESTONE_GENERATOR) { BlockEntityType.Builder.of(::CobblerBlockEntity, MBlocks.COBBLESTONE_GENERATOR).build(null) } - val ESSENCE_STORAGE: BlockEntityType by registry.register(MNames.ESSENCE_STORAGE) { BlockEntityType.Builder.of(::EssenceStorageBlockEntity, MBlocks.ESSENCE_STORAGE).build(null) } - val MATTER_RECONSTRUCTOR: BlockEntityType by registry.register(MNames.MATTER_RECONSTRUCTOR) { BlockEntityType.Builder.of(::MatterReconstructorBlockEntity, MBlocks.MATTER_RECONSTRUCTOR).build(null) } - val FLUID_TANK: BlockEntityType by registry.register(MNames.FLUID_TANK) { BlockEntityType.Builder.of(::FluidTankBlockEntity, MBlocks.FLUID_TANK).build(null) } - val ANDROID_CHARGER: BlockEntityType by registry.register(MNames.ANDROID_CHARGER) { BlockEntityType.Builder.of(::AndroidChargerBlockEntity, MBlocks.ANDROID_CHARGER).build(null) } - val ANDROID_CHARGER_MIDDLE: BlockEntityType by registry.register(MNames.ANDROID_CHARGER + "_middle") { BlockEntityType.Builder.of(::AndroidChargerMiddleBlockEntity, MBlocks.ANDROID_CHARGER).build(null) } - val ANDROID_CHARGER_TOP: BlockEntityType by registry.register(MNames.ANDROID_CHARGER + "_top") { BlockEntityType.Builder.of(::AndroidChargerTopBlockEntity, MBlocks.ANDROID_CHARGER).build(null) } - val INFINITE_WATER_SOURCE: BlockEntityType by registry.register(MNames.INFINITE_WATER_SOURCE) { BlockEntityType.Builder.of(::InfiniteWaterSourceBlockEntity, MBlocks.INFINITE_WATER_SOURCE).build(null) } - val DEV_CHEST: BlockEntityType by registry.register(MNames.DEV_CHEST) { BlockEntityType.Builder.of(::DevChestBlockEntity, MBlocks.DEV_CHEST).build(null) } + private fun register(name: String, factory: BlockEntityType.BlockEntitySupplier, vararg blocks: Supplier): RegistryObject> { + return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) } + } + + val ANDROID_STATION by register(MNames.ANDROID_STATION, ::AndroidStationBlockEntity, MBlocks::ANDROID_STATION) + val BATTERY_BANK by register(MNames.BATTERY_BANK, ::BatteryBankBlockEntity, MBlocks::BATTERY_BANK) + val MATTER_DECOMPOSER by register(MNames.MATTER_DECOMPOSER, ::MatterDecomposerBlockEntity, MBlocks::MATTER_DECOMPOSER) + val MATTER_CAPACITOR_BANK by register(MNames.MATTER_CAPACITOR_BANK, ::MatterCapacitorBankBlockEntity, MBlocks::MATTER_CAPACITOR_BANK) + val MATTER_CABLE by register(MNames.MATTER_CABLE, ::MatterCableBlockEntity, MBlocks::MATTER_CABLE) + val STORAGE_CABLE by register(MNames.STORAGE_CABLE, ::StorageCableBlockEntity, MBlocks::STORAGE_CABLE) + val PATTERN_STORAGE by register(MNames.PATTERN_STORAGE, ::PatternStorageBlockEntity, MBlocks::PATTERN_STORAGE) + val MATTER_SCANNER by register(MNames.MATTER_SCANNER, ::MatterScannerBlockEntity, MBlocks::MATTER_SCANNER) + val MATTER_PANEL by register(MNames.MATTER_PANEL, ::MatterPanelBlockEntity, MBlocks::MATTER_PANEL) + val MATTER_REPLICATOR by register(MNames.MATTER_REPLICATOR, ::MatterReplicatorBlockEntity, MBlocks::MATTER_REPLICATOR) + val MATTER_BOTTLER by register(MNames.MATTER_BOTTLER, ::MatterBottlerBlockEntity, MBlocks::MATTER_BOTTLER) + val DRIVE_VIEWER by register(MNames.DRIVE_VIEWER, ::DriveViewerBlockEntity, MBlocks::DRIVE_VIEWER) + val BLACK_HOLE by register(MNames.BLACK_HOLE, ::BlackHoleBlockEntity, MBlocks::BLACK_HOLE) + val CARGO_CRATE by registry.register(MNames.CARGO_CRATE) { BlockEntityType.Builder.of(::CargoCrateBlockEntity, *MRegistry.CARGO_CRATES.blocks.values.toTypedArray()).build(null) } + val DRIVE_RACK by register(MNames.DRIVE_RACK, ::DriveRackBlockEntity, MBlocks::DRIVE_RACK) + val ITEM_MONITOR by register(MNames.ITEM_MONITOR, ::ItemMonitorBlockEntity, MBlocks::ITEM_MONITOR) + val ENERGY_COUNTER by register(MNames.ENERGY_COUNTER, ::EnergyCounterBlockEntity, MBlocks::ENERGY_COUNTER) + val CHEMICAL_GENERATOR by register(MNames.CHEMICAL_GENERATOR, ::ChemicalGeneratorBlockEntity, MBlocks::CHEMICAL_GENERATOR) + val PLATE_PRESS by register(MNames.PLATE_PRESS, ::PlatePressBlockEntity, MBlocks::PLATE_PRESS) + val TWIN_PLATE_PRESS by register(MNames.TWIN_PLATE_PRESS, { a, b -> PlatePressBlockEntity(a, b, true) }, MBlocks::TWIN_PLATE_PRESS) + val GRAVITATION_STABILIZER by register(MNames.GRAVITATION_STABILIZER, ::GravitationStabilizerBlockEntity, MBlocks::GRAVITATION_STABILIZER) + val MATTER_RECYCLER by register(MNames.MATTER_RECYCLER, ::MatterRecyclerBlockEntity, MBlocks::MATTER_RECYCLER) + val ENERGY_SERVO by register(MNames.ENERGY_SERVO, ::EnergyServoBlockEntity, MBlocks::ENERGY_SERVO) + val COBBLESTONE_GENERATOR by register(MNames.COBBLESTONE_GENERATOR, ::CobblerBlockEntity, MBlocks::COBBLESTONE_GENERATOR) + val ESSENCE_STORAGE by register(MNames.ESSENCE_STORAGE, ::EssenceStorageBlockEntity, MBlocks::ESSENCE_STORAGE) + val MATTER_RECONSTRUCTOR by register(MNames.MATTER_RECONSTRUCTOR, ::MatterReconstructorBlockEntity, MBlocks::MATTER_RECONSTRUCTOR) + val FLUID_TANK by register(MNames.FLUID_TANK, ::FluidTankBlockEntity, MBlocks::FLUID_TANK) + val ANDROID_CHARGER by register(MNames.ANDROID_CHARGER, ::AndroidChargerBlockEntity, MBlocks::ANDROID_CHARGER) + val ANDROID_CHARGER_MIDDLE by register(MNames.ANDROID_CHARGER + "_middle", ::AndroidChargerMiddleBlockEntity, MBlocks::ANDROID_CHARGER) + val ANDROID_CHARGER_TOP by register(MNames.ANDROID_CHARGER + "_top", ::AndroidChargerTopBlockEntity, MBlocks::ANDROID_CHARGER) + val INFINITE_WATER_SOURCE by register(MNames.INFINITE_WATER_SOURCE, ::InfiniteWaterSourceBlockEntity, MBlocks::INFINITE_WATER_SOURCE) + val DEV_CHEST by register(MNames.DEV_CHEST, ::DevChestBlockEntity, MBlocks::DEV_CHEST) + val PAINTER by register(MNames.PAINTER, ::PainterBlockEntity, MBlocks::PAINTER) val POWERED_FURNACE: BlockEntityType by registry.register(MNames.POWERED_FURNACE) { BlockEntityType.Builder.of({ a, b -> MBlocks.POWERED_FURNACE.newBlockEntity(a, b) }, MBlocks.POWERED_FURNACE).build(null) } val POWERED_BLAST_FURNACE: BlockEntityType by registry.register(MNames.POWERED_BLAST_FURNACE) { BlockEntityType.Builder.of({ a, b -> MBlocks.POWERED_BLAST_FURNACE.newBlockEntity(a, b) }, MBlocks.POWERED_BLAST_FURNACE).build(null) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index 9aea56fdd..34658df9d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -63,6 +63,7 @@ import ru.dbotthepony.mc.otm.block.storage.StoragePowerSupplierBlock import ru.dbotthepony.mc.otm.block.tech.AndroidChargerBlock import ru.dbotthepony.mc.otm.block.tech.CobblerBlock import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock +import ru.dbotthepony.mc.otm.block.tech.PainterBlock import ru.dbotthepony.mc.otm.block.tech.PoweredFurnaceBlock import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.core.TranslatableComponent @@ -100,6 +101,7 @@ object MBlocks { val INFINITE_WATER_SOURCE: Block by registry.register(MNames.INFINITE_WATER_SOURCE) { InfiniteWaterSourceBlock() } val ESSENCE_STORAGE: EssenceStorageBlock by registry.register(MNames.ESSENCE_STORAGE) { EssenceStorageBlock() } val MATTER_RECONSTRUCTOR: MatterReconstructorBlock by registry.register(MNames.MATTER_RECONSTRUCTOR) { MatterReconstructorBlock() } + val PAINTER: PainterBlock by registry.register(MNames.PAINTER) { PainterBlock() } val STORAGE_BUS: Block by registry.register(MNames.STORAGE_BUS) { StorageBusBlock() } val STORAGE_IMPORTER: Block by registry.register(MNames.STORAGE_IMPORTER) { StorageImporterBlock() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index a7b653932..8c004d6c5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -151,6 +151,8 @@ object MItems { } } + val PAINTER: BlockItem by registry.register(MNames.PAINTER) { BlockItem(MBlocks.PAINTER, DEFAULT_PROPERTIES) } + val MACHINES = SupplierList( ::ANDROID_STATION, ::ANDROID_CHARGER, ::BATTERY_BANK, ::MATTER_DECOMPOSER, ::MATTER_CAPACITOR_BANK, ::MATTER_CABLE, ::PATTERN_STORAGE, ::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR, @@ -158,7 +160,7 @@ object MItems { ::POWERED_SMOKER, ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER, ::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER, - ::ENERGY_SERVO, + ::ENERGY_SERVO, ::PAINTER, ::PHANTOM_ATTRACTOR, ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::INFINITE_WATER_SOURCE, ::ESSENCE_STORAGE, ::MATTER_RECONSTRUCTOR ) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt index e6dc21401..fbc7042df 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MMenus.kt @@ -36,6 +36,7 @@ import ru.dbotthepony.mc.otm.client.screen.tech.CobblerScreen import ru.dbotthepony.mc.otm.client.screen.tech.EnergyCounterScreen import ru.dbotthepony.mc.otm.client.screen.tech.EnergyServoScreen import ru.dbotthepony.mc.otm.client.screen.tech.EssenceStorageScreen +import ru.dbotthepony.mc.otm.client.screen.tech.PainterScreen import ru.dbotthepony.mc.otm.client.screen.tech.PlatePressScreen import ru.dbotthepony.mc.otm.client.screen.tech.PoweredFurnaceScreen import ru.dbotthepony.mc.otm.client.screen.tech.TwinPlatePressScreen @@ -66,6 +67,7 @@ import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu +import ru.dbotthepony.mc.otm.menu.tech.PainterMenu import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu import ru.dbotthepony.mc.otm.menu.tech.TwinPlatePressMenu @@ -100,6 +102,7 @@ object MMenus { val ESSENCE_STORAGE: MenuType by registry.register(MNames.ESSENCE_STORAGE) { MenuType(::EssenceStorageMenu, FeatureFlags.VANILLA_SET) } val ITEM_REPAIER: MenuType by registry.register(MNames.MATTER_RECONSTRUCTOR) { MenuType(::MatterReconstructorMenu, FeatureFlags.VANILLA_SET) } val FLUID_TANK: MenuType by registry.register(MNames.FLUID_TANK) { MenuType(::FluidTankMenu, FeatureFlags.VANILLA_SET) } + val PAINTER: MenuType by registry.register(MNames.PAINTER) { MenuType(::PainterMenu, FeatureFlags.VANILLA_SET) } val STORAGE_BUS: MenuType by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) } val STORAGE_IMPORTER_EXPORTER: MenuType by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) } @@ -142,6 +145,7 @@ object MMenus { MenuScreens.register(ITEM_REPAIER, ::MatterReconstructorScreen) MenuScreens.register(FLUID_TANK, ::FluidTankScreen) MenuScreens.register(POWERED_FURNACE, ::PoweredFurnaceScreen) + MenuScreens.register(PAINTER, ::PainterScreen) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index 6d80d71bc..cc48bc828 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -15,6 +15,7 @@ object MNames { const val ANDROID_CHARGER = "android_charger" const val INFINITE_WATER_SOURCE = "infinite_water_source" const val DEV_CHEST = "dev_chest" + const val PAINTER = "painter" // blocks const val ANDROID_STATION = "android_station" diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt index 216531d73..3894c5b2e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRecipes.kt @@ -10,6 +10,7 @@ import net.minecraftforge.registries.RegistryObject import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe +import ru.dbotthepony.mc.otm.recipe.PainterRecipe import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe @@ -36,11 +37,13 @@ object MRecipes { } val PLATE_PRESS by register("plate_press") + val PAINTER by register("painter") init { serializers.register("plate_press") { PlatePressRecipe.SERIALIZER } serializers.register("energy_container") { EnergyContainerRecipe.Companion } serializers.register("upgrade") { UpgradeRecipe.Companion } serializers.register("hammer_priming") { ExplosiveHammerPrimingRecipe.Companion } + serializers.register("painter") { PainterRecipe.SERIALIZER } } }