Briefcase grill

This commit is contained in:
DBotThePony 2025-01-05 23:50:43 +07:00
parent a09795ad74
commit 7c35c284cd
Signed by: DBot
GPG Key ID: DCC23B5715498507
26 changed files with 660 additions and 48 deletions

View File

@ -44,6 +44,8 @@ private fun decoratives(provider: MatteryLanguageProvider) {
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description0", "High blast resistance door with redstone latch...")
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description1", "...feeling safe now?")
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description2", "This one is painted $name")
add(MBlocks.GRILL[color]!!, "$name Briefcase Grill")
}
add(MRegistry.TRITANIUM_PRESSURE_PLATE.block, "Tritanium Pressure Plate")
@ -65,6 +67,8 @@ private fun decoratives(provider: MatteryLanguageProvider) {
add(MItems.CARGO_CRATE_MINECARTS[null]!!, "Minecart with Cargo Crate")
add(MEntityTypes.CARGO_CRATE_MINECARTS[null]!!, "Minecart with Cargo Crate")
add(MBlocks.GRILL[null]!!, "Briefcase Grill")
add(MRegistry.CARGO_CRATES.block, "Cargo Crate")
add(MRegistry.COMPUTER_TERMINAL.block, "Computer Terminal")
add(MRegistry.STAR_CHAIR.block, "Star Chair")
@ -372,7 +376,10 @@ private fun misc(provider: MatteryLanguageProvider) {
gui("power.burn_time", "Burn time left: %s ticks")
gui("progress_widget", "Progress: %s%%")
gui("progress_widget_stuck", "The machine can not work, check configuration")
gui("progress_widget_ticks", "Progress: %d / %d (%s%%)")
gui("fuel_widget", "Fuel: %s%%")
gui("fuel_widget_ticks", "Fuel: %d / %d (%s%%)")
gui("progress_stuck", "The machine can not work, check configuration")
gui("total_raw", "Total:")
gui("total", "Total: %s")

View File

@ -56,6 +56,8 @@ private fun decoratives(provider: MatteryLanguageProvider) {
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description0", HIGH_BLAST_RESISTANCE_DOOR)
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description1", FEELING_SAFE_NOW)
add(MBlocks.TRITANIUM_TRAPDOOR[color]!!, "description2", "Данный вариант выкрашен в $name")
add(MBlocks.GRILL[color]!!, "$nameAdj мангал-дипломат")
}
add(MRegistry.TRITANIUM_PRESSURE_PLATE.block, "Тритановая нажимная пластина")
@ -65,6 +67,8 @@ private fun decoratives(provider: MatteryLanguageProvider) {
misc("computer_terminal_tooltip", "Может быть использован как кнопка, с оговоркой что он посылает сигнал блоку сзади, а не под ним")
misc("decorative", "Элемент декора")
add(MBlocks.GRILL[null]!!, "Мангал-дипломат")
add(MItems.CARGO_CRATE_MINECARTS[null]!!, "Вагонетка с грузовым ящиком")
add(MEntityTypes.CARGO_CRATE_MINECARTS[null]!!, "Вагонетка с грузовым ящиком")

View File

@ -181,6 +181,7 @@ fun addLootTables(lootTables: LootTables) {
lootTables.tile(MBlocks.FLUID_TANK)
lootTables.tile(MBlocks.PAINTER)
lootTables.tile(MBlocks.MATTER_ENTANGLER)
lootTables.tile(MBlocks.GRILL.values)
lootTables.tile(MBlocks.ENERGY_SERVO.values)
lootTables.tile(MBlocks.ENERGY_COUNTER.values)

View File

@ -11,6 +11,7 @@ import net.minecraft.world.item.crafting.Ingredient
import net.neoforged.neoforge.common.Tags
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.config.CablesConfig
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.registry.MItemTags
import ru.dbotthepony.mc.otm.registry.MItems
@ -493,4 +494,32 @@ fun addCraftingTableRecipes(consumer: RecipeOutput) {
.row(Tags.Items.INGOTS_GOLD, Tags.Items.INGOTS_GOLD, Tags.Items.INGOTS_GOLD)
.row(MItemTags.REINFORCED_TRITANIUM_PLATES, Items.BLUE_ICE, MItemTags.REINFORCED_TRITANIUM_PLATES)
.build(consumer)
val ironRod = ItemTags.create(ResourceLocation("c", "rods/iron"))
for ((color, item) in MItems.GRILL) {
MatteryRecipe(item, category = RecipeCategory.DECORATIONS)
.rowB(color?.tag)
.row(Tags.Items.INGOTS_IRON, Tags.Items.INGOTS_IRON, Tags.Items.INGOTS_IRON)
.rowAC(MItemTags.TRITANIUM_INGOTS, MItemTags.TRITANIUM_INGOTS)
.build(consumer)
MatteryRecipe(item, category = RecipeCategory.DECORATIONS)
.rowB(color?.tag)
.row(ironRod, ironRod, ironRod)
.rowAC(MItemTags.TRITANIUM_INGOTS, MItemTags.TRITANIUM_INGOTS)
.build(consumer, "grill_alt_a/${color?.name?.lowercase() ?: "default"}")
MatteryRecipe(item, category = RecipeCategory.DECORATIONS)
.rowB(color?.tag)
.row(ironRod, ironRod, ironRod)
.rowB(MItemTags.TRITANIUM_PLATES)
.build(consumer, "grill_alt_b/${color?.name?.lowercase() ?: "default"}")
MatteryRecipe(item, category = RecipeCategory.DECORATIONS)
.rowB(color?.tag)
.row(Tags.Items.INGOTS_IRON, Tags.Items.INGOTS_IRON, Tags.Items.INGOTS_IRON)
.rowB(MItemTags.TRITANIUM_PLATES)
.build(consumer, "grill_alt_c/${color?.name?.lowercase() ?: "default"}")
}
}

View File

@ -413,4 +413,6 @@ fun addPainterRecipes(consumer: RecipeOutput) {
mapOf(color to 1)
))
}
generate(consumer, MItems.GRILL[null]!!, MItems.GRILL)
}

View File

@ -227,6 +227,7 @@ fun addTags(tagsProvider: TagsProvider) {
tagsProvider.requiresPickaxe(MBlocks.TRITANIUM_TRAPDOOR.values, Tiers.IRON)
tagsProvider.requiresPickaxe(MBlocks.PAINTER, Tiers.STONE)
tagsProvider.requiresPickaxe(MBlocks.ENERGY_CABLES.values, Tiers.STONE)
tagsProvider.requiresPickaxe(MBlocks.GRILL.values, Tiers.STONE)
tagsProvider.requiresPickaxe(listOf<Block>(
*MBlocks.ANDROID_STATION.values.toTypedArray(),

View File

@ -7,9 +7,13 @@ import net.minecraft.ChatFormatting
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.component.DataComponents
import net.minecraft.core.particles.DustParticleOptions
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.util.RandomSource
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
@ -28,9 +32,11 @@ import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.TooltipList
import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -41,10 +47,14 @@ import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.once
import java.util.concurrent.Callable
import java.util.function.Function
import java.util.function.Supplier
import kotlin.jvm.optionals.getOrNull
private val LOGGER = LogManager.getLogger()
fun Block.getShapeForEachState(properties: List<Property<*>>, fn: (BlockState) -> VoxelShape): Map<BlockState, VoxelShape> {
val builder = Object2ObjectArrayMap<BlockState, Supplier<VoxelShape>>()
@ -81,6 +91,29 @@ fun interface INeighbourChangeListener {
)
}
interface IBlockWithCustomName {
var customDisplayName: Component?
fun saveCustomDisplayName(nbt: CompoundTag, registry: HolderLookup.Provider) {
if (customDisplayName != null)
ComponentSerialization
.CODEC
.encodeStart(registry.createSerializationContext(NbtOps.INSTANCE), customDisplayName)
.resultOrPartial { LOGGER.error("Unable to serialize custom name: {}", it) }
.ifPresent { nbt["Name"] = it }
}
fun loadCustomDisplayName(nbt: CompoundTag, registry: HolderLookup.Provider) {
if ("Name" in nbt) {
customDisplayName = ComponentSerialization
.CODEC
.decode(registry.createSerializationContext(NbtOps.INSTANCE), nbt["Name"]!!)
.resultOrPartial { LOGGER.error("Unable to deserialize custom name: {}", it) }
.getOrNull()?.first
}
}
}
open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(properties), INeighbourChangeListener {
val tooltips = TooltipList()
@ -94,7 +127,7 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
if (this is EntityBlock && itemStack.has(DataComponents.CUSTOM_NAME) && !level.isClientSide) {
val tile = level.getBlockEntity(blockPos)
if (tile is MatteryDeviceBlockEntity) {
if (tile is IBlockWithCustomName) {
try {
tile.customDisplayName = itemStack.get(DataComponents.CUSTOM_NAME)
} catch(_: Exception) {

View File

@ -0,0 +1,106 @@
package ru.dbotthepony.mc.otm.block.decorative
import net.minecraft.core.BlockPos
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.DyeColor
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.material.MapColor
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.entity.decorative.GrillBlockEntity
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import kotlin.math.absoluteValue
class GrillBlock(val color: DyeColor?) : RotatableMatteryBlock(Properties.of().mapColor(color?.mapColor ?: MapColor.METAL).destroyTime(0.75f).explosionResistance(10.0f)), EntityBlock {
override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity {
return GrillBlockEntity(p_153215_, p_153216_)
}
init {
tooltips.colored(color)
}
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 { _, _, _, t -> if (t is GrillBlockEntity) t.tick() }
}
enum class State : StringRepresentable {
IDLE,
FUELED,
GRILLING;
private val str = name.lowercase()
override fun getSerializedName(): String {
return str
}
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(STATE_PROPERTY)
}
override fun animateTick(blockState: BlockState, level: Level, blockPos: BlockPos, random: RandomSource) {
val state = blockState[STATE_PROPERTY]
if (state === State.IDLE)
return
val (x, y, z) = blockPos
if (random.nextInt(15) == 0) {
val xd = x + 0.5 + random.nextGaussian() * 0.1
val yd = y + 1.1
val zd = z + 0.5 + random.nextGaussian() * 0.1
level.addParticle(ParticleTypes.LAVA, xd, yd, zd, random.nextDouble() * 0.5, 0.005, random.nextDouble() * 0.5)
}
if (random.nextInt(6) == 0) {
level.playLocalSound(
x + 0.5,
y + 0.5,
z + 0.5,
SoundEvents.CAMPFIRE_CRACKLE,
SoundSource.BLOCKS,
0.5f + random.nextFloat(),
random.nextFloat() * 0.7f + 0.6f,
false
)
}
if (state === State.GRILLING && random.nextInt(4) == 0) {
val xd = x + 0.5 + random.nextGaussian() * 0.25
val yd = y + 1.1
val zd = z + 0.5 + random.nextGaussian() * 0.25
level.addParticle(ParticleTypes.CAMPFIRE_COSY_SMOKE, xd, yd, zd, 0.0, random.nextGaussian().absoluteValue * 0.04 + 0.02, 0.0)
}
}
companion object {
val STATE_PROPERTY: EnumProperty<State> = EnumProperty.create("state", State::class.java)
}
}

View File

@ -22,6 +22,7 @@ import net.neoforged.neoforge.items.IItemHandler
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.block.IBlockWithCustomName
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
@ -44,9 +45,8 @@ import kotlin.jvm.optionals.getOrNull
* Device block entity base, implementing [MenuProvider] and [IRedstoneControlled], and also tracks custom display name
*/
@Suppress("LiftReturnOrAssignment")
abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState)
: MatteryBlockEntity(blockEntityType, blockPos, blockState), MenuProvider, IRedstoneControlled {
var customDisplayName: Component? = null
abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(blockEntityType, blockPos, blockState), MenuProvider, IRedstoneControlled, IBlockWithCustomName {
final override var customDisplayName: Component? = null
override val redstoneControl: AbstractRedstoneControl = RedstoneControl { new, old ->
markDirtyFast()
@ -72,25 +72,12 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
override fun saveShared(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.saveShared(nbt, registry)
if (customDisplayName != null)
ComponentSerialization
.CODEC
.encodeStart(registry.createSerializationContext(NbtOps.INSTANCE), customDisplayName)
.resultOrPartial { LOGGER.error("Unable to serialize custom name: {}", it) }
.ifPresent { nbt["Name"] = it }
saveCustomDisplayName(nbt, registry)
}
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.loadAdditional(nbt, registry)
if ("Name" in nbt) {
customDisplayName = ComponentSerialization
.CODEC
.decode(registry.createSerializationContext(NbtOps.INSTANCE), nbt["Name"]!!)
.resultOrPartial { LOGGER.error("Unable to deserialize custom name: {}", it) }
.getOrNull()?.first
}
loadCustomDisplayName(nbt, registry)
}
override fun setLevel(level: Level) {

View File

@ -0,0 +1,212 @@
package ru.dbotthepony.mc.otm.block.entity.decorative
import com.mojang.serialization.Codec
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Containers
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.item.crafting.SingleRecipeInput
import net.minecraft.world.item.crafting.SmokingRecipe
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.block.IBlockWithCustomName
import ru.dbotthepony.mc.otm.block.decorative.GrillBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.data.minRange
import ru.dbotthepony.mc.otm.menu.decorative.GrillMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks
import kotlin.jvm.optionals.getOrNull
class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.GRILL, blockPos, blockState), MenuProvider, IBlockWithCustomName {
val fuelSlot = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, 1) {
override fun getMaxStackSize(): Int {
return 4
}
}
val inputSlots = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, SLOTS) {
override fun getMaxStackSize(): Int {
return 1
}
private fun clearSlot(slot: Int) {
inputProgress[slot] = 0
outputs[slot] = null
activeSlots.rem(slot)
}
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
if (new.isEmpty || new.count > 1) {
clearSlot(slot)
} else {
val level = level
if (level == null) {
clearSlot(slot)
return
}
val input = SingleRecipeInput(new)
val result = level.recipeManager
.byType(RecipeType.SMOKING)
.firstOrNull { it.value.matches(input, level) }
if (result == null) {
clearSlot(slot)
} else {
if (outputs[slot] != result.value)
inputProgress[slot] = 0
outputs[slot] = result.value
activeSlots.add(slot)
}
}
}
}
override var customDisplayName: Component? = null
override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu {
return GrillMenu(p_39954_, p_39955_, this)
}
override fun getDisplayName(): Component {
return customDisplayName ?: level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos")
}
private val outputs = arrayOfNulls<SmokingRecipe>(SLOTS)
private val activeSlots = IntArrayList()
private val inputProgress = IntArray(SLOTS)
var fuelTicks = 0
private set
var fuelTotalTicks = 0
private set
fun totalTicksRequired(slot: Int): Int {
return outputs[slot]?.cookingTime?.times(3) ?: 0
}
fun ticksPassed(slot: Int): Int {
return inputProgress[slot]
}
override fun saveShared(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.saveShared(nbt, registry)
saveCustomDisplayName(nbt, registry)
}
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
super.loadAdditional(nbt, registry)
loadCustomDisplayName(nbt, registry)
}
init {
savetablesLevel.int(::fuelTicks)
savetablesLevel.int(::fuelTotalTicks)
savetables.stateful(::inputSlots)
savetables.stateful(::fuelSlot)
// TODO: could have been done as list (and get more space efficient storage), but we use an array, oh well
for (i in inputProgress.indices) {
savetables.codec(Delegate.Of({ inputProgress[i] }, { inputProgress[i] = it }), progressCodec, "inputProgress${i}")
}
// addDroppableContainer(inputSlots)
// addDroppableContainer(fuelSlot)
}
private val state by countingLazy(blockStateChangesCounter) {
this.blockState[GrillBlock.STATE_PROPERTY]
}
override fun tick() {
super.tick()
if (fuelTicks <= 0 && activeSlots.isNotEmpty()) {
val fuel = fuelSlot[0].getBurnTime(null) * 8
if (fuel > 0) {
fuelTicks = fuel
fuelTotalTicks = fuel
val split = fuelSlot[0].split(1)
val remaining = split.craftingRemainingItem
if (remaining.isEmpty) {
fuelSlot.setChanged(0)
} else if (fuelSlot[0].isNotEmpty) {
val level = level as ServerLevel
val (x, y, z) = blockPos.center
Containers.dropItemStack(level, x, y, z, remaining)
} else {
fuelSlot[0] = remaining
}
}
}
if (fuelTicks > 0) {
if (activeSlots.isNotEmpty()) {
fuelTicks -= 5
for (slot in activeSlots.toIntArray()) {
val recipe = outputs[slot]!!
if (recipe.cookingTime * 3 <= ++inputProgress[slot]) {
inputSlots[slot] = recipe.assemble(SingleRecipeInput(inputSlots[slot]), level!!.registryAccess())
}
}
if (state !== GrillBlock.State.GRILLING) {
level!!.setBlock(blockPos, blockState.set(GrillBlock.STATE_PROPERTY, GrillBlock.State.GRILLING), Block.UPDATE_ALL)
}
} else {
fuelTicks--
if (state !== GrillBlock.State.FUELED) {
level!!.setBlock(blockPos, blockState.set(GrillBlock.STATE_PROPERTY, GrillBlock.State.FUELED), Block.UPDATE_ALL)
}
}
} else {
fuelTotalTicks = 0
if (state !== GrillBlock.State.IDLE) {
level!!.setBlock(blockPos, blockState.set(GrillBlock.STATE_PROPERTY, GrillBlock.State.IDLE), Block.UPDATE_ALL)
}
}
}
companion object {
const val SLOTS = 6
private val progressCodec = Codec.INT.minRange(0)
}
}

View File

@ -18,6 +18,7 @@ object WidgetLocation {
val CHECKBOX = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/checkbox.png"), 30f, 60f)
val RADIO = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/radio.png"), 30f, 60f)
val PROGRESS_ARROWS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/progress_arrows.png"), 22f, 31f)
val FUEL = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/fuel.png"), 14f, 28f)
val HORIZONTAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/horizontal_gauges.png"), 96f, 108f)
val VERTICAL_GAUGES = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/vertical_gauges.png"), 90f, 48f)
val REDSTONE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/redstone.png"), 54f, 18f)

View File

@ -0,0 +1,60 @@
package ru.dbotthepony.mc.otm.client.screen.decorative
import mezz.jei.api.constants.RecipeTypes
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
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.DockProperty
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.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.decorative.GrillMenu
class GrillScreen(menu: GrillMenu, inventory: Inventory, title: Component) : MatteryScreen<GrillMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = super.makeMainFrame()!!
val topStrip = EditablePanel(this, frame, height = AbstractSlotPanel.SIZE)
topStrip.dock = Dock.TOP
topStrip.dockBottom = 4f
val middleStrip = EditablePanel(this, frame, height = AbstractSlotPanel.SIZE)
middleStrip.dock = Dock.TOP
for (slot in menu.inputSlots)
SlotPanel(this, topStrip, slot).also { it.dock = Dock.LEFT }
for (slot in menu.progress) {
val panel = ProgressGaugePanel(this, middleStrip, slot)
panel.type = ProgressGaugePanel.Type.FUEL
panel.behaviorType = ProgressGaugePanel.Type.PROGRESS
panel.dock = Dock.LEFT
panel.dockResize = DockResizeMode.NONE
panel.dockMargin = DockProperty(left = 2f, right = 2f)
panel.setRecipeType { listOf(RecipeTypes.SMOKING) }
}
ProgressGaugePanel(this, middleStrip, menu.fuel).also {
it.dock = Dock.LEFT
it.dockLeft = 2f
it.dockResize = DockResizeMode.NONE
it.flop = true
it.behaviorType = ProgressGaugePanel.Type.FUEL
it.setRecipeType { listOf(RecipeTypes.FUELING) }
}
SlotPanel(this, middleStrip, menu.fuelSlot[0]).also {
it.dock = Dock.LEFT
it.dockLeft = 2f
it.dockResize = DockResizeMode.NONE
}
frame.sizeToContents()
return frame
}
}

View File

@ -14,43 +14,74 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.AbstractButtonPanel
import ru.dbotthepony.mc.otm.compat.jei.JEIPlugin
import ru.dbotthepony.mc.otm.compat.jei.isJeiLoaded
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.IProgressGaugeWidget
import java.util.function.Supplier
import kotlin.math.roundToInt
open class ProgressGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
val widget: ProgressGaugeWidget,
val widget: IProgressGaugeWidget,
x: Float = 0f,
y: Float = 0f
): AbstractButtonPanel<S>(screen, parent, x, y, width = GAUGE_BACKGROUND.width, height = GAUGE_BACKGROUND.height) {
): AbstractButtonPanel<S>(screen, parent, x, y) {
init {
scissor = true
}
var flop = false
var type = Type.PROGRESS
set(value) {
field = value
width = value.width
height = value.height
}
var behaviorType: Type? = null
init {
type = Type.PROGRESS
}
private var recipeTypeSupplier: Supplier<List<RecipeType<*>>>? = null
protected open fun makeTooltip(): MutableList<Component> {
val tooltip: MutableList<Component>
enum class Type(val width: Float, val height: Float) {
PROGRESS(GAUGE_BACKGROUND.width, GAUGE_BACKGROUND.height),
FUEL(FUEL_BACKGROUND.width, FUEL_BACKGROUND.height);
if (widget.isStuck) {
tooltip = mutableListOf(
val localizedString = "otm.gui.${name.lowercase()}_widget"
}
protected open fun makeTooltip(): MutableList<Component> {
val tooltip = ArrayList<Component>()
if (widget is IProgressGaugeWidget.WithTicks) {
tooltip.add(
TranslatableComponent(
"otm.gui.progress_widget",
String.format("%.2f", widget.percentage * 100f)
),
TranslatableComponent("otm.gui.progress_widget_stuck").withStyle(ChatFormatting.DARK_RED)
)
} else {
tooltip = mutableListOf(
TranslatableComponent(
"otm.gui.progress_widget",
String.format("%.2f", widget.percentage * 100f)
"${(behaviorType ?: type).localizedString}_ticks",
widget.workTicks,
widget.totalTicks,
String.format("%.2f", widget.progress * 100f)
)
)
} else {
tooltip.add(
TranslatableComponent(
(behaviorType ?: type).localizedString,
String.format("%.2f", widget.progress * 100f)
)
)
}
if (widget.isStuck) {
tooltip.add(TranslatableComponent("otm.gui.progress_stuck").withStyle(ChatFormatting.DARK_RED))
}
if (tooltips.isNotEmpty()) {
tooltip.add(TextComponent(""))
tooltip.addAll(tooltips)
}
return tooltip
@ -61,14 +92,24 @@ open class ProgressGaugePanel<out S : Screen>(
RenderSystem.setShaderColor(0.75f, 0.4f, 0.4f, 1f)
}
if (flop) {
GAUGE_BACKGROUND.render(graphics, canvasHeight = height, canvasWidth = this.width, winding = UVWindingOrder.U1_V0_U0_V1)
val width = (this.width * widget.percentage).roundToInt().toFloat()
GAUGE_FOREGROUND.renderPartial(graphics, x = this.width - width, height = height, width = width, winding = UVWindingOrder.U1_V0_U0_V1)
} else {
GAUGE_BACKGROUND.render(graphics, canvasHeight = height, canvasWidth = this.width)
val width = (this.width * widget.percentage).roundToInt().toFloat()
GAUGE_FOREGROUND.renderPartial(graphics, height = height, width = width)
when (type) {
Type.PROGRESS -> {
if (flop) {
GAUGE_BACKGROUND.render(graphics, canvasHeight = height, canvasWidth = this.width, winding = UVWindingOrder.U1_V0_U0_V1)
val width = (this.width * widget.progress).roundToInt().toFloat()
GAUGE_FOREGROUND.renderPartial(graphics, x = this.width - width, height = height, width = width, winding = UVWindingOrder.U1_V0_U0_V1)
} else {
GAUGE_BACKGROUND.render(graphics, canvasHeight = height, canvasWidth = this.width)
val width = (this.width * widget.progress).roundToInt().toFloat()
GAUGE_FOREGROUND.renderPartial(graphics, height = height, width = width)
}
}
Type.FUEL -> {
FUEL_BACKGROUND.render(graphics, canvasHeight = height, canvasWidth = this.width)
val height = (this.height * widget.progress).roundToInt().toFloat()
FUEL_FOREGROUND.renderPartial(graphics, height = height, y = this.height - height, topDown = false, width = width)
}
}
if (widget.isStuck && tickCount % 40 <= 20) {
@ -110,5 +151,7 @@ open class ProgressGaugePanel<out S : Screen>(
companion object {
val GAUGE_BACKGROUND = WidgetLocation.PROGRESS_ARROWS.sprite(y = 0f, width = 22f, height = 15f)
val GAUGE_FOREGROUND = WidgetLocation.PROGRESS_ARROWS.sprite(y = 15f, width = 22f, height = 15f)
val FUEL_BACKGROUND = WidgetLocation.FUEL.sprite(y = 0f, width = 14f, height = 14f)
val FUEL_FOREGROUND = WidgetLocation.FUEL.sprite(y = 14f, width = 14f, height = 14f)
}
}

View File

@ -70,6 +70,9 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
}
}
/**
* Iterates either non-empty slots of container or all slots of container
*/
fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot> {
if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { containerSlot(it) }
@ -230,4 +233,22 @@ interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
return list
}
fun shrink(slot: Int, amount: Int): Boolean {
if (slot < 0 || slot > size())
return false
val item = this[slot]
if (item.isEmpty)
return false
if (item.count <= amount) {
this[slot] = ItemStack.EMPTY
} else {
item.shrink(amount)
setChanged(slot)
}
return true
}
}

View File

@ -139,6 +139,12 @@ open class BatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0)
}
}
open class ChemicalFuelSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return super.mayPlace(itemStack) && itemStack.getBurnTime(null) > 0
}
}
open class ChargeSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return super.mayPlace(itemStack) && (itemStack.energy?.canReceive() ?: false)

View File

@ -0,0 +1,68 @@
package ru.dbotthepony.mc.otm.menu.decorative
import com.google.common.collect.ImmutableList
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.item.crafting.SingleRecipeInput
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.block.entity.decorative.GrillBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.menu.ChemicalFuelSlot
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.OutputSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.menu.widget.IProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
import java.util.function.Supplier
class GrillMenu(
containerId: Int,
inventory: Inventory,
tile: GrillBlockEntity? = null
) : MatteryMenu(MMenus.GRILL, containerId, inventory, tile) {
val fuelSlot = makeSlots(tile?.fuelSlot ?: object : MatteryContainer(1) {
override fun getMaxStackSize(): Int {
return 4
}
}, ::ChemicalFuelSlot)
val inputSlots = makeSlots<Container, MatterySlot>(tile?.inputSlots ?: object : MatteryContainer(GrillBlockEntity.SLOTS) {
override fun getMaxStackSize(): Int {
return 1
}
}) { c, i ->
object : MatterySlot(c, i) {
override fun mayPlace(itemStack: ItemStack): Boolean {
val input = SingleRecipeInput(itemStack)
return super.mayPlace(itemStack) && player.level().recipeManager.byType(RecipeType.SMOKING).any { it.value.matches(input, player.level()) }
}
}
}
val progress: ImmutableList<IProgressGaugeWidget.WithTicks> = immutableList(GrillBlockEntity.SLOTS) {
object : IProgressGaugeWidget.WithTicks {
override val isStuck: Boolean
get() = totalTicks < workTicks
override val workTicks: Int by mSynchronizer.computedInt(Supplier { tile?.ticksPassed(it) ?: 0 })
override val totalTicks: Int by mSynchronizer.computedInt(Supplier { tile?.totalTicksRequired(it) ?: 0 })
}
}
val fuel = object : IProgressGaugeWidget.WithTicks {
override val isStuck: Boolean
get() = false
override val workTicks: Int by mSynchronizer.computedInt(Supplier { tile?.fuelTicks ?: 0 })
override val totalTicks: Int by mSynchronizer.computedInt(Supplier { tile?.fuelTotalTicks ?: 0 })
}
init {
addInventorySlots()
addStorageSlot(fuelSlot)
addStorageSlot(inputSlots)
}
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.menu.widget
import kotlin.math.min
interface IProgressGaugeWidget {
val isStuck: Boolean
val progress: Float
interface WithTicks : IProgressGaugeWidget {
val workTicks: Int
val totalTicks: Int
override val progress: Float
get() = if (totalTicks <= 0 || workTicks <= 0) 0f else min(1f, workTicks.toFloat() / totalTicks.toFloat())
}
}

View File

@ -9,14 +9,14 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.function.BooleanSupplier
@Suppress("unused")
class ProgressGaugeWidget(synchronizer: SynchableGroup) {
class ProgressGaugeWidget(synchronizer: SynchableGroup) : IProgressGaugeWidget {
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
var progressSupplier: FloatSupplier = FloatSupplier { 0f }
var stuckSupplier: BooleanSupplier = BooleanSupplier { false }
val percentage by synchronizer.computedFloat(delegate = { progressSupplier.getAsFloat() })
val isStuck by synchronizer.computedBoolean(delegate = BooleanSupplier { stuckSupplier.asBoolean })
override val progress by synchronizer.computedFloat(delegate = { progressSupplier.getAsFloat() })
override val isStuck by synchronizer.computedBoolean(delegate = BooleanSupplier { stuckSupplier.asBoolean })
constructor(
menu: MatteryMenu,

View File

@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.block.entity.cable.SimpleEnergyCableBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.GrillBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.InfiniteWaterSourceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.PainterBlockEntity
@ -106,6 +107,8 @@ object MBlockEntities {
val STORAGE_EXPORTER by register(MNames.STORAGE_EXPORTER, ::StorageExporterBlockEntity, MBlocks::STORAGE_EXPORTER)
val STORAGE_POWER_SUPPLIER by register(MNames.STORAGE_POWER_SUPPLIER, ::StoragePowerSupplierBlockEntity, MBlocks.STORAGE_POWER_SUPPLIER)
val GRILL by register(MNames.GRILL, ::GrillBlockEntity, MBlocks.GRILL)
val HOLO_SIGN: BlockEntityType<HoloSignBlockEntity> by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) }
fun register(bus: IEventBus) {

View File

@ -35,6 +35,7 @@ import ru.dbotthepony.mc.otm.block.addSimpleDescription
import ru.dbotthepony.mc.otm.block.decorative.DevChestBlock
import ru.dbotthepony.mc.otm.block.decorative.EngineBlock
import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock
import ru.dbotthepony.mc.otm.block.decorative.GrillBlock
import ru.dbotthepony.mc.otm.block.decorative.HoloSignBlock
import ru.dbotthepony.mc.otm.block.decorative.InfiniteWaterSourceBlock
import ru.dbotthepony.mc.otm.block.decorative.LaboratoryLamp
@ -166,6 +167,8 @@ object MBlocks {
val STORAGE_CABLE: Block by registry.register(MNames.STORAGE_CABLE, ::StorageCableBlock)
val STORAGE_POWER_SUPPLIER = registry.coloredWithBase(MNames.STORAGE_POWER_SUPPLIER, ::StoragePowerSupplierBlock)
val GRILL = registry.coloredWithBase(MNames.GRILL, ::GrillBlock)
val BLACK_HOLE: Block by registry.register(MNames.BLACK_HOLE) { BlackHoleBlock() }
val GRAVITATION_STABILIZER: Block by registry.register(MNames.GRAVITATION_STABILIZER) { BlockGravitationStabilizer() }
val GRAVITATION_STABILIZER_LENS: Block by registry.register(MNames.GRAVITATION_STABILIZER_LENS) { BlockGravitationStabilizerLens() }

View File

@ -147,6 +147,8 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
accept(MRegistry.TRITANIUM_PRESSURE_PLATE.item)
accept(MItems.TRITANIUM_ANVIL[0])
accept(MItems.GRILL.values)
// accept(MItems.MATTER_DUST)
accept(MItems.TRITANIUM_ORE)

View File

@ -121,6 +121,8 @@ object MItems {
val STORAGE_CABLE: BlockItem by registry.register(MNames.STORAGE_CABLE) { BlockItem(MBlocks.STORAGE_CABLE, DEFAULT_PROPERTIES) }
val STORAGE_POWER_SUPPLIER = register(MNames.STORAGE_POWER_SUPPLIER, MBlocks.STORAGE_POWER_SUPPLIER)
val GRILL = registry.coloredWithBase(MNames.GRILL) { BlockItem(MBlocks.GRILL[it]!!, DEFAULT_PROPERTIES) }
val GRAVITATION_STABILIZER: BlockItem by registry.register(MNames.GRAVITATION_STABILIZER) { BlockItem(MBlocks.GRAVITATION_STABILIZER, DEFAULT_PROPERTIES) }
val PHANTOM_ATTRACTOR: DoubleHighBlockItem by registry.register(MNames.PHANTOM_ATTRACTOR) { DoubleHighBlockItem(MBlocks.PHANTOM_ATTRACTOR, DEFAULT_PROPERTIES) }

View File

@ -8,6 +8,7 @@ import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.block.entity.tech.AndroidChargerBlockEntity
import ru.dbotthepony.mc.otm.client.screen.decorative.CargoCrateScreen
import ru.dbotthepony.mc.otm.client.screen.decorative.FluidTankScreen
import ru.dbotthepony.mc.otm.client.screen.decorative.GrillScreen
import ru.dbotthepony.mc.otm.client.screen.decorative.HoloSignScreen
import ru.dbotthepony.mc.otm.client.screen.decorative.MinecartCargoCrateScreen
import ru.dbotthepony.mc.otm.client.screen.decorative.PainterScreen
@ -42,6 +43,7 @@ import ru.dbotthepony.mc.otm.client.screen.tech.ItemHatchScreen
import ru.dbotthepony.mc.otm.client.screen.tech.MatterHatchScreen
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu
import ru.dbotthepony.mc.otm.menu.decorative.GrillMenu
import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu
import ru.dbotthepony.mc.otm.menu.decorative.MinecartCargoCrateMenu
import ru.dbotthepony.mc.otm.menu.decorative.PainterMenu
@ -91,6 +93,7 @@ object MMenus {
val MATTER_BOTTLER by registry.register(MNames.MATTER_BOTTLER) { MenuType(::MatterBottlerMenu, FeatureFlags.VANILLA_SET) }
val DRIVE_VIEWER by registry.register(MNames.DRIVE_VIEWER) { MenuType(::DriveViewerMenu, FeatureFlags.VANILLA_SET) }
val CARGO_CRATE by registry.register(MNames.CARGO_CRATE) { MenuType(::CargoCrateMenu, FeatureFlags.VANILLA_SET) }
val GRILL by registry.register(MNames.GRILL) { MenuType(::GrillMenu, FeatureFlags.VANILLA_SET) }
val MINECART_CARGO_CRATE by registry.register(MNames.MINECART_CARGO_CRATE) { MenuType(::MinecartCargoCrateMenu, FeatureFlags.VANILLA_SET) }
val DRIVE_RACK by registry.register(MNames.DRIVE_RACK) { MenuType(::DriveRackMenu, FeatureFlags.VANILLA_SET) }
val ITEM_MONITOR by registry.register(MNames.ITEM_MONITOR) { MenuType(::ItemMonitorMenu, FeatureFlags.VANILLA_SET) }
@ -140,6 +143,7 @@ object MMenus {
event.register(MATTER_BOTTLER, ::MatterBottlerScreen)
event.register(DRIVE_VIEWER, ::DriveViewerScreen)
event.register(CARGO_CRATE, ::CargoCrateScreen)
event.register(GRILL, ::GrillScreen)
event.register(MINECART_CARGO_CRATE, ::MinecartCargoCrateScreen)
event.register(DRIVE_RACK, ::DriveRackScreen)
event.register(ITEM_MONITOR, ::ItemMonitorScreen)

View File

@ -58,6 +58,7 @@ object MNames {
const val STORAGE_CABLE = "storage_cable" // нужен рецепт
const val STORAGE_POWER_SUPPLIER = "storage_power_supplier" // нужен рецепт
const val GRILL = "grill" // нужен рецепт
const val DEBUG_EXPLOSION_SMALL = "debug_explosion_small"
const val DEBUG_SPHERE_POINTS = "debug_sphere_points"

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B