Painter table

This commit is contained in:
DBotThePony 2023-08-15 20:04:27 +07:00
parent 615912de8b
commit 61b83d5f5a
Signed by: DBot
GPG Key ID: DCC23B5715498507
31 changed files with 1233 additions and 95 deletions

View File

@ -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)

View File

@ -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)")

View File

@ -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)")

View File

@ -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)

View File

@ -433,4 +433,11 @@ fun addCraftingTableRecipes(consumer: Consumer<FinishedRecipe>) {
.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)
}

View File

@ -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<FinishedRecipe>, items: Map<out DyeColor?, Item>, 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<FinishedRecipe>, default: Item, items: Map<out DyeColor?, Item>, 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<FinishedRecipe>, to: Item, from: Map<out DyeColor?, Item>) {
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<FinishedRecipe>, name: String, items: List<Pair<Item, Pair<DyeColor, DyeColor>>>, base: Map<DyeColor, Item>) {
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<FinishedRecipe>) {
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)
}

View File

@ -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,

View File

@ -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, Int>(DyeColor::class.java)
val dyeStoredView: Map<DyeColor, Int> = Collections.unmodifiableMap(dyeStored)
init {
addDroppableContainer(dyeInput)
savetables.stateful(dyeInput, INVENTORY_KEY)
}
fun takeDyes(dyes: Map<DyeColor, Int>) {
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<DyeColor>) : Map.Entry<DyeColor, Mixer> {
val mixing: ImmutableList<ImmutableList<DyeColor>> = ImmutableList.copyOf(mixing)
override val key: DyeColor
get() = color
override val value: Mixer
get() = this
private fun mix(input: MutableMap<DyeColor, Int>, seen: MutableSet<DyeColor>, stack: MutableSet<DyeColor>) {
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<DyeColor, Int>) {
mix(input, EnumSet.noneOf(DyeColor::class.java), EnumSet.noneOf(DyeColor::class.java))
}
fun isAvailable(input: Map<DyeColor, Int>): 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
}
}

View File

@ -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 <T : BlockEntity?> getTicker(p_153212_: Level, p_153213_: BlockState, p_153214_: BlockEntityType<T>): BlockEntityTicker<T>? {
if (p_153212_.isClientSide) return null
return BlockEntityTicker { p_155253_, p_155254_, p_155255_, p_155256_ -> if (p_155256_ is PainterBlockEntity) p_155256_.tick() }
}
}

View File

@ -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()
pose.pushPose()
pose.translate(x % 1f, y % 1f, 0f)
pose.translate(x, y, 0f)
if (width != 16f || height != 16f)
pose.scale(width / 16f, height / 16f, 1f)
guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt())
guiGraphics.renderFakeItem(itemStack, 0, 0)
pose.popPose()
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)
}
}

View File

@ -1608,7 +1608,7 @@ open class EditablePanel<out S : Screen> @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 {

View File

@ -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<out S : Screen>(
screen: S,
parent: EditablePanel<*>,
x: Float = 0f,
y: Float = 0f,
width: Float = 40f,
height: Float = 40f,
slimScrollbar: Boolean = false
) : EditablePanel<S>(screen, parent, x, y, width, height) {
val canvas = object : EditablePanel<S>(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
}
}

View File

@ -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<PainterMenu>(menu, inventory, title) {
inner class Bar(parent: EditablePanel<*>, val dye: DyeColor) : EditablePanel<PainterScreen>(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<MatteryScreen<*>> {
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<RectangleButtonPanel<PainterScreen>>()
menu.listeners.addListener {
buttons.forEach { it.remove() }
buttons.clear()
for (recipe in menu.possibleRecipes) {
object : LargeRectangleButtonPanel<PainterScreen>(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
}
}

View File

@ -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())
}
}

View File

@ -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) {

View File

@ -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<PainterRecipe>, IDrawable {
private val type = RecipeType(ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.PAINTER), PainterRecipe::class.java)
override fun getRecipeType(): RecipeType<PainterRecipe> {
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)
}
}

View File

@ -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())
}
}

View File

@ -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())
}
}

View File

@ -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

View File

@ -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 {

View File

@ -175,6 +175,13 @@ inline fun <V : Any> immutableList(initializer: Consumer<V>.() -> Unit): Immutab
return builder.build()
}
fun <V : Any> immutableList(a: V, vararg values: V): ImmutableList<V> {
val builder = ImmutableList.Builder<V>()
builder.add(a)
builder.addAll(values.iterator())
return builder.build()
}
fun <T> IForgeRegistry<T>.getID(value: T): Int {
return (this as ForgeRegistry<T>).getID(value)
}

View File

@ -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)
}

View File

@ -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<RGBAColor> = StreamCodec(
{ s, a -> a.accountBytes(12L); RGBAColor(s.readFloat(), s.readFloat(), s.readFloat()) },

View File

@ -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<ItemStack> {
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<Unit>()
val possibleRecipes = ArrayList<PainterRecipe>()
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
}
}
}
}

View File

@ -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<DyeColor, Int>
) : Recipe<Container> {
constructor(
id: ResourceLocation,
input: Ingredient,
output: ItemStack,
dyes: Set<DyeColor>
) : this(id, input, output, dyes.associateWith { 1 })
fun canCraft(storedDyes: Map<DyeColor, Int>): 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<Ingredient> {
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>(
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<Map<DyeColor, Int>>(
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) }
}
}
}
}

View File

@ -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<AndroidStationBlockEntity> by registry.register(MNames.ANDROID_STATION) { BlockEntityType.Builder.of(::AndroidStationBlockEntity, MBlocks.ANDROID_STATION).build(null) }
val BATTERY_BANK: BlockEntityType<BatteryBankBlockEntity> by registry.register(MNames.BATTERY_BANK) { BlockEntityType.Builder.of(::BatteryBankBlockEntity, MBlocks.BATTERY_BANK).build(null) }
val MATTER_DECOMPOSER: BlockEntityType<MatterDecomposerBlockEntity> by registry.register(MNames.MATTER_DECOMPOSER) { BlockEntityType.Builder.of(::MatterDecomposerBlockEntity, MBlocks.MATTER_DECOMPOSER).build(null) }
val MATTER_CAPACITOR_BANK: BlockEntityType<MatterCapacitorBankBlockEntity> by registry.register(MNames.MATTER_CAPACITOR_BANK) { BlockEntityType.Builder.of(::MatterCapacitorBankBlockEntity, MBlocks.MATTER_CAPACITOR_BANK).build(null) }
val MATTER_CABLE: BlockEntityType<MatterCableBlockEntity> by registry.register(MNames.MATTER_CABLE) { BlockEntityType.Builder.of(::MatterCableBlockEntity, MBlocks.MATTER_CABLE).build(null) }
val STORAGE_CABLE: BlockEntityType<StorageCableBlockEntity> by registry.register(MNames.STORAGE_CABLE) { BlockEntityType.Builder.of(::StorageCableBlockEntity, MBlocks.STORAGE_CABLE).build(null) }
val PATTERN_STORAGE: BlockEntityType<PatternStorageBlockEntity> by registry.register(MNames.PATTERN_STORAGE) { BlockEntityType.Builder.of(::PatternStorageBlockEntity, MBlocks.PATTERN_STORAGE).build(null) }
val MATTER_SCANNER: BlockEntityType<MatterScannerBlockEntity> by registry.register(MNames.MATTER_SCANNER) { BlockEntityType.Builder.of(::MatterScannerBlockEntity, MBlocks.MATTER_SCANNER).build(null) }
val MATTER_PANEL: BlockEntityType<MatterPanelBlockEntity> by registry.register(MNames.MATTER_PANEL) { BlockEntityType.Builder.of(::MatterPanelBlockEntity, MBlocks.MATTER_PANEL).build(null) }
val MATTER_REPLICATOR: BlockEntityType<MatterReplicatorBlockEntity> by registry.register(MNames.MATTER_REPLICATOR) { BlockEntityType.Builder.of(::MatterReplicatorBlockEntity, MBlocks.MATTER_REPLICATOR).build(null) }
val MATTER_BOTTLER: BlockEntityType<MatterBottlerBlockEntity> by registry.register(MNames.MATTER_BOTTLER) { BlockEntityType.Builder.of(::MatterBottlerBlockEntity, MBlocks.MATTER_BOTTLER).build(null) }
val DRIVE_VIEWER: BlockEntityType<DriveViewerBlockEntity> by registry.register(MNames.DRIVE_VIEWER) { BlockEntityType.Builder.of(::DriveViewerBlockEntity, MBlocks.DRIVE_VIEWER).build(null) }
val BLACK_HOLE: BlockEntityType<BlackHoleBlockEntity> by registry.register(MNames.BLACK_HOLE) { BlockEntityType.Builder.of(::BlackHoleBlockEntity, MBlocks.BLACK_HOLE).build(null) }
val CARGO_CRATE: BlockEntityType<CargoCrateBlockEntity> by registry.register(MNames.CARGO_CRATE) { BlockEntityType.Builder.of(::CargoCrateBlockEntity, *MRegistry.CARGO_CRATES.blocks.values.toTypedArray()).build(null) }
val DRIVE_RACK: BlockEntityType<DriveRackBlockEntity> by registry.register(MNames.DRIVE_RACK) { BlockEntityType.Builder.of(::DriveRackBlockEntity, MBlocks.DRIVE_RACK).build(null) }
val ITEM_MONITOR: BlockEntityType<ItemMonitorBlockEntity> by registry.register(MNames.ITEM_MONITOR) { BlockEntityType.Builder.of(::ItemMonitorBlockEntity, MBlocks.ITEM_MONITOR).build(null) }
val ENERGY_COUNTER: BlockEntityType<EnergyCounterBlockEntity> by registry.register(MNames.ENERGY_COUNTER) { BlockEntityType.Builder.of(::EnergyCounterBlockEntity, MBlocks.ENERGY_COUNTER).build(null) }
val CHEMICAL_GENERATOR: BlockEntityType<ChemicalGeneratorBlockEntity> by registry.register(MNames.CHEMICAL_GENERATOR) { BlockEntityType.Builder.of(::ChemicalGeneratorBlockEntity, MBlocks.CHEMICAL_GENERATOR).build(null) }
val PLATE_PRESS: BlockEntityType<PlatePressBlockEntity> by registry.register(MNames.PLATE_PRESS) { BlockEntityType.Builder.of(::PlatePressBlockEntity, MBlocks.PLATE_PRESS).build(null) }
val TWIN_PLATE_PRESS: BlockEntityType<PlatePressBlockEntity> 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<GravitationStabilizerBlockEntity> by registry.register(MNames.GRAVITATION_STABILIZER) { BlockEntityType.Builder.of(::GravitationStabilizerBlockEntity, MBlocks.GRAVITATION_STABILIZER).build(null) }
val MATTER_RECYCLER: BlockEntityType<MatterRecyclerBlockEntity> by registry.register(MNames.MATTER_RECYCLER) { BlockEntityType.Builder.of(::MatterRecyclerBlockEntity, MBlocks.MATTER_RECYCLER).build(null) }
val ENERGY_SERVO: BlockEntityType<EnergyServoBlockEntity> by registry.register(MNames.ENERGY_SERVO) { BlockEntityType.Builder.of(::EnergyServoBlockEntity, MBlocks.ENERGY_SERVO).build(null) }
val COBBLESTONE_GENERATOR: BlockEntityType<CobblerBlockEntity> by registry.register(MNames.COBBLESTONE_GENERATOR) { BlockEntityType.Builder.of(::CobblerBlockEntity, MBlocks.COBBLESTONE_GENERATOR).build(null) }
val ESSENCE_STORAGE: BlockEntityType<EssenceStorageBlockEntity> by registry.register(MNames.ESSENCE_STORAGE) { BlockEntityType.Builder.of(::EssenceStorageBlockEntity, MBlocks.ESSENCE_STORAGE).build(null) }
val MATTER_RECONSTRUCTOR: BlockEntityType<MatterReconstructorBlockEntity> by registry.register(MNames.MATTER_RECONSTRUCTOR) { BlockEntityType.Builder.of(::MatterReconstructorBlockEntity, MBlocks.MATTER_RECONSTRUCTOR).build(null) }
val FLUID_TANK: BlockEntityType<FluidTankBlockEntity> by registry.register(MNames.FLUID_TANK) { BlockEntityType.Builder.of(::FluidTankBlockEntity, MBlocks.FLUID_TANK).build(null) }
val ANDROID_CHARGER: BlockEntityType<AndroidChargerBlockEntity> by registry.register(MNames.ANDROID_CHARGER) { BlockEntityType.Builder.of(::AndroidChargerBlockEntity, MBlocks.ANDROID_CHARGER).build(null) }
val ANDROID_CHARGER_MIDDLE: BlockEntityType<AndroidChargerMiddleBlockEntity> by registry.register(MNames.ANDROID_CHARGER + "_middle") { BlockEntityType.Builder.of(::AndroidChargerMiddleBlockEntity, MBlocks.ANDROID_CHARGER).build(null) }
val ANDROID_CHARGER_TOP: BlockEntityType<AndroidChargerTopBlockEntity> by registry.register(MNames.ANDROID_CHARGER + "_top") { BlockEntityType.Builder.of(::AndroidChargerTopBlockEntity, MBlocks.ANDROID_CHARGER).build(null) }
val INFINITE_WATER_SOURCE: BlockEntityType<InfiniteWaterSourceBlockEntity> by registry.register(MNames.INFINITE_WATER_SOURCE) { BlockEntityType.Builder.of(::InfiniteWaterSourceBlockEntity, MBlocks.INFINITE_WATER_SOURCE).build(null) }
val DEV_CHEST: BlockEntityType<DevChestBlockEntity> by registry.register(MNames.DEV_CHEST) { BlockEntityType.Builder.of(::DevChestBlockEntity, MBlocks.DEV_CHEST).build(null) }
private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, vararg blocks: Supplier<Block>): RegistryObject<BlockEntityType<T>> {
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<PoweredFurnaceBlockEntity> 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<PoweredFurnaceBlockEntity> 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) }

View File

@ -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() }

View File

@ -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
)

View File

@ -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<EssenceStorageMenu> by registry.register(MNames.ESSENCE_STORAGE) { MenuType(::EssenceStorageMenu, FeatureFlags.VANILLA_SET) }
val ITEM_REPAIER: MenuType<MatterReconstructorMenu> by registry.register(MNames.MATTER_RECONSTRUCTOR) { MenuType(::MatterReconstructorMenu, FeatureFlags.VANILLA_SET) }
val FLUID_TANK: MenuType<FluidTankMenu> by registry.register(MNames.FLUID_TANK) { MenuType(::FluidTankMenu, FeatureFlags.VANILLA_SET) }
val PAINTER: MenuType<PainterMenu> by registry.register(MNames.PAINTER) { MenuType(::PainterMenu, FeatureFlags.VANILLA_SET) }
val STORAGE_BUS: MenuType<StorageBusMenu> by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) }
val STORAGE_IMPORTER_EXPORTER: MenuType<StorageImporterExporterMenu> 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)
}
}
}

View File

@ -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"

View File

@ -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<PlatePressRecipe>("plate_press")
val PAINTER by register<PainterRecipe>("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 }
}
}