Plate press recipes experience points

Fixes #139
This commit is contained in:
DBotThePony 2023-02-08 23:15:11 +07:00
parent 7b6cce48b9
commit e238ceceaf
Signed by: DBot
GPG Key ID: DCC23B5715498507
13 changed files with 286 additions and 28 deletions

View File

@ -10,6 +10,8 @@ import net.minecraft.data.recipes.RecipeBuilder
import net.minecraft.data.recipes.RecipeProvider import net.minecraft.data.recipes.RecipeProvider
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey import net.minecraft.tags.TagKey
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.level.ItemLike import net.minecraft.world.level.ItemLike
@ -77,14 +79,15 @@ class MatteryRecipeProvider(generatorIn: DataGenerator) : RecipeProvider(generat
} }
} }
fun plate(id: String, count: Int = 1, workTicks: Int = 200) { fun plate(id: String, count: Int = 1, workTicks: Int = 200, experience: FloatProvider = ConstantFloat.ZERO) {
exec { _, consumer -> exec { _, consumer ->
consumer.accept(PlatePressShallowFinishedRecipe( consumer.accept(PlatePressShallowFinishedRecipe(
ResourceLocation(DataGen.MOD_ID, "plates/$id"), ResourceLocation(DataGen.MOD_ID, "plates/$id"),
ResourceLocation("forge", "ingots/$id"), ResourceLocation("forge", "ingots/$id"),
ResourceLocation("forge", "plates/$id"), ResourceLocation("forge", "plates/$id"),
count, count,
workTicks workTicks,
experience = experience
)) ))
} }
} }

View File

@ -2,14 +2,18 @@ package ru.dbotthepony.mc.otm.datagen.recipes
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import com.mojang.serialization.JsonOps
import net.minecraft.data.recipes.FinishedRecipe import net.minecraft.data.recipes.FinishedRecipe
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.item.crafting.RecipeSerializer import net.minecraft.world.item.crafting.RecipeSerializer
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe
import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.data.getOrNull
class PlatePressFinishedRecipe(private val recipe: PlatePressRecipe) : FinishedRecipe { class PlatePressFinishedRecipe(private val recipe: PlatePressRecipe) : FinishedRecipe {
override fun serializeRecipeData(it: JsonObject) { override fun serializeRecipeData(it: JsonObject) {
@ -21,6 +25,7 @@ class PlatePressFinishedRecipe(private val recipe: PlatePressRecipe) : FinishedR
} }
it["work_time"] = JsonPrimitive(recipe.workTime) it["work_time"] = JsonPrimitive(recipe.workTime)
it["experience"] = FloatProvider.CODEC.toJsonStrict(recipe.experience)
} }
override fun getId(): ResourceLocation { override fun getId(): ResourceLocation {
@ -45,7 +50,8 @@ class PlatePressShallowFinishedRecipe(
private val input: ResourceLocation, private val input: ResourceLocation,
private val output: ResourceLocation, private val output: ResourceLocation,
private val count: Int = 1, private val count: Int = 1,
private val workTime: Int = 200 private val workTime: Int = 200,
private val experience: FloatProvider = ConstantFloat.ZERO,
) : FinishedRecipe { ) : FinishedRecipe {
override fun serializeRecipeData(it: JsonObject) { override fun serializeRecipeData(it: JsonObject) {
it["input"] = JsonObject().also { it["input"] = JsonObject().also {
@ -60,6 +66,7 @@ class PlatePressShallowFinishedRecipe(
} }
it["work_time"] = JsonPrimitive(workTime) it["work_time"] = JsonPrimitive(workTime)
it["experience"] = FloatProvider.CODEC.toJsonStrict(experience)
} }
override fun getId(): ResourceLocation { override fun getId(): ResourceLocation {

View File

@ -1,19 +1,21 @@
package ru.dbotthepony.mc.otm.datagen.recipes package ru.dbotthepony.mc.otm.datagen.recipes
import net.minecraft.util.valueproviders.ConstantFloat
fun addPlatePressRecipes(provider: MatteryRecipeProvider) { fun addPlatePressRecipes(provider: MatteryRecipeProvider) {
val baselineMetals = arrayOf("iron", "silver", "bronze", "lead", "constantan", "brass") val baselineMetals = arrayOf("iron" to 0.2f, "silver" to 0.3f, "bronze" to 0.3f, "lead" to 0.3f, "constantan" to 0.4f, "brass" to 0.3f)
val softMetals = arrayOf("gold", "aluminum", "aluminium", "copper", "electrum", "zinc") val softMetals = arrayOf("gold" to 0.4f, "aluminum" to 0.3f, "aluminium" to 0.3f, "copper" to 0.2f, "electrum" to 0.4f, "zinc" to 0.3f)
val hardMetals = arrayOf("tritanium", "steel", "tungsten", "uranium") val hardMetals = arrayOf("tritanium" to 0.5f, "steel" to 0.5f, "tungsten" to 0.55f, "uranium" to 0.5f)
for (thing in baselineMetals) { for ((thing, exp) in baselineMetals) {
provider.plate(thing) provider.plate(thing, experience = ConstantFloat.of(exp), workTicks = 160)
} }
for (thing in softMetals) { for ((thing, exp) in softMetals) {
provider.plate(thing, workTicks = 140) provider.plate(thing, workTicks = 120, experience = ConstantFloat.of(exp))
} }
for (thing in hardMetals) { for ((thing, exp) in hardMetals) {
provider.plate(thing, workTicks = 300) provider.plate(thing, workTicks = 240, experience = ConstantFloat.of(exp))
} }
} }

View File

@ -5,6 +5,7 @@ package ru.dbotthepony.mc.otm
import net.minecraft.client.server.IntegratedServer import net.minecraft.client.server.IntegratedServer
import net.minecraft.server.MinecraftServer import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraftforge.api.distmarker.Dist import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.event.TickEvent import net.minecraftforge.event.TickEvent
@ -195,6 +196,8 @@ fun tickWhileServer(condition: () -> Boolean, ticker: () -> Unit) {
} }
fun Level.once(ticker: ITickable) { fun Level.once(ticker: ITickable) {
if (this.isClientSide) return
if (!SERVER_IS_LIVE) { if (!SERVER_IS_LIVE) {
LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping"))
return return
@ -204,6 +207,8 @@ fun Level.once(ticker: ITickable) {
} }
fun Level.oncePre(ticker: ITickable) { fun Level.oncePre(ticker: ITickable) {
if (this.isClientSide) return
if (!SERVER_IS_LIVE) { if (!SERVER_IS_LIVE) {
LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping"))
return return
@ -213,6 +218,8 @@ fun Level.oncePre(ticker: ITickable) {
} }
fun Level.addTicker(ticker: IConditionalTickable) { fun Level.addTicker(ticker: IConditionalTickable) {
if (this.isClientSide) return
if (!SERVER_IS_LIVE) { if (!SERVER_IS_LIVE) {
LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping"))
return return
@ -222,6 +229,8 @@ fun Level.addTicker(ticker: IConditionalTickable) {
} }
fun Level.addTickerPre(ticker: IConditionalTickable) { fun Level.addTickerPre(ticker: IConditionalTickable) {
if (this.isClientSide) return
if (!SERVER_IS_LIVE) { if (!SERVER_IS_LIVE) {
LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping")) LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping"))
return return
@ -231,18 +240,22 @@ fun Level.addTickerPre(ticker: IConditionalTickable) {
} }
fun Level.until(ticker: () -> Boolean) { fun Level.until(ticker: () -> Boolean) {
if (this.isClientSide) return
addTicker(IConditionalTickable.wrap(ticker)) addTicker(IConditionalTickable.wrap(ticker))
} }
fun Level.untilPre(ticker: () -> Boolean) { fun Level.untilPre(ticker: () -> Boolean) {
if (this.isClientSide) return
addTickerPre(IConditionalTickable.wrap(ticker)) addTickerPre(IConditionalTickable.wrap(ticker))
} }
fun Level.`while`(condition: () -> Boolean, ticker: () -> Unit) { fun Level.`while`(condition: () -> Boolean, ticker: () -> Unit) {
if (this.isClientSide) return
addTicker(IConditionalTickable.wrap(condition, ticker)) addTicker(IConditionalTickable.wrap(condition, ticker))
} }
fun Level.whilePre(condition: () -> Boolean, ticker: () -> Unit) { fun Level.whilePre(condition: () -> Boolean, ticker: () -> Unit) {
if (this.isClientSide) return
addTickerPre(IConditionalTickable.wrap(condition, ticker)) addTickerPre(IConditionalTickable.wrap(condition, ticker))
} }

View File

@ -32,29 +32,34 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
open class Job { open class Job {
open val ticks: Double open val ticks: Double
open val powerUsage: Decimal open val powerUsage: Decimal
open val experience: Float
constructor( constructor(
ticks: Double, ticks: Double,
powerUsage: Decimal = Decimal.ZERO powerUsage: Decimal = Decimal.ZERO,
experience: Float = 0f,
) { ) {
this.ticks = ticks this.ticks = ticks
this.powerUsage = powerUsage this.powerUsage = powerUsage
this.experience = experience
} }
constructor( constructor(
tag: CompoundTag tag: CompoundTag
) : this(tag.getDouble(TICKS_KEY), tag.getDecimal(POWER_KEY)) ) : this(tag.getDouble(TICKS_KEY), tag.getDecimal(POWER_KEY), tag.getFloat(EXPERIENCE_KEY))
open fun serializeNBT(): CompoundTag { open fun serializeNBT(): CompoundTag {
return CompoundTag().also { return CompoundTag().also {
it[TICKS_KEY] = ticks it[TICKS_KEY] = ticks
it[POWER_KEY] = powerUsage it[POWER_KEY] = powerUsage
it[EXPERIENCE_KEY] = experience
} }
} }
companion object { companion object {
const val TICKS_KEY = "ticks" const val TICKS_KEY = "ticks"
const val POWER_KEY = "power" const val POWER_KEY = "power"
const val EXPERIENCE_KEY = "power"
} }
} }
@ -65,8 +70,9 @@ abstract class MatteryWorkerBlockEntity<JobType : MatteryWorkerBlockEntity.Job>(
constructor( constructor(
itemStack: ItemStack, itemStack: ItemStack,
ticks: Double, ticks: Double,
power: Decimal = Decimal.ZERO power: Decimal = Decimal.ZERO,
) : super(ticks, power) { experience: Float = 0f,
) : super(ticks, power, experience) {
this.itemStack = itemStack this.itemStack = itemStack
} }

View File

@ -4,7 +4,10 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
@ -26,16 +29,27 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.MRecipes
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.once
import ru.dbotthepony.mc.otm.onceServer
class PlatePressBlockEntity( class PlatePressBlockEntity(
p_155229_: BlockPos, p_155229_: BlockPos,
p_155230_: BlockState p_155230_: BlockState
) : MatteryWorkerBlockEntity<MatteryWorkerBlockEntity.ItemJob>(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ) : MatteryWorkerBlockEntity<MatteryWorkerBlockEntity.ItemJob>(MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::ItemJob), IDroppableContainer {
::ItemJob
), IDroppableContainer {
val container = MatteryContainer(this::setChangedLight, 2) val container = MatteryContainer(this::setChangedLight, 2)
override val energy = WorkerEnergyStorage(this::setChangedLight, ServerConfig.PLATE_PRESS) override val energy = WorkerEnergyStorage(this::setChangedLight, ServerConfig.PLATE_PRESS)
var experience = 0.0
fun popExperience(player: ServerPlayer) {
val whole = experience.toInt()
if (whole > 0) {
experience -= whole
ExperienceOrb.award(player.level as ServerLevel, player.position(), whole)
}
}
override val droppableContainer: Container override val droppableContainer: Container
get() = container get() = container
val itemHandler = container.handler(object : MatteryContainerHooks { val itemHandler = container.handler(object : MatteryContainerHooks {
@ -68,11 +82,13 @@ class PlatePressBlockEntity(
override fun saveAdditional(nbt: CompoundTag) { override fun saveAdditional(nbt: CompoundTag) {
super.saveAdditional(nbt) super.saveAdditional(nbt)
nbt[INVENTORY_KEY] = container.serializeNBT() nbt[INVENTORY_KEY] = container.serializeNBT()
nbt["experience"] = experience
} }
override fun load(nbt: CompoundTag) { override fun load(nbt: CompoundTag) {
super.load(nbt) super.load(nbt)
nbt.map(INVENTORY_KEY, container::deserializeNBT) nbt.map(INVENTORY_KEY, container::deserializeNBT)
experience = nbt.getDouble("experience")
} }
override val defaultDisplayName: Component override val defaultDisplayName: Component
@ -89,6 +105,7 @@ class PlatePressBlockEntity(
if (!container.fullyAddItem(job.itemStack, start = SLOT_OUTPUT, end = SLOT_OUTPUT)) if (!container.fullyAddItem(job.itemStack, start = SLOT_OUTPUT, end = SLOT_OUTPUT))
return Status.FAILURE_ITEM return Status.FAILURE_ITEM
experience = (experience + job.experience).coerceAtMost(100.0)
return Status.SUCCESS return Status.SUCCESS
} }
@ -99,7 +116,7 @@ class PlatePressBlockEntity(
val recipe = level?.recipeManager?.getRecipeFor(MRecipes.PLATE_PRESS, container, level!!)?.orElse(null) ?: return null to IdleReason.ITEM val recipe = level?.recipeManager?.getRecipeFor(MRecipes.PLATE_PRESS, container, level!!)?.orElse(null) ?: return null to IdleReason.ITEM
container[SLOT_INPUT].shrink(1) container[SLOT_INPUT].shrink(1)
return ItemJob(recipe.resultItem, recipe.workTime.toDouble(), BASELINE_CONSUMPTION) to null return ItemJob(recipe.resultItem, recipe.workTime.toDouble(), BASELINE_CONSUMPTION, experience = recipe.experience.sample(level!!.random)) to null
} }
companion object { companion object {

View File

@ -73,7 +73,7 @@ class EnergyCounterBlock : MatteryBlock(), EntityBlock {
) { ) {
super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston) super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
if (!level.isClientSide && SERVER_IS_LIVE) { if (SERVER_IS_LIVE) {
level.once { level.once {
(level.getBlockEntity(pos) as? EnergyCounterBlockEntity)?.checkSurroundings() (level.getBlockEntity(pos) as? EnergyCounterBlockEntity)?.checkSurroundings()
} }

View File

@ -71,6 +71,12 @@ object PlatePressRecipeCategory : IRecipeCategory<PlatePressRecipe>, IDrawable {
mouseY: Double mouseY: Double
) { ) {
minecraft.font.drawAligned(stack, TranslatableComponent("otm.gui.recipe.ticks", recipe.workTime), TextAlign.TOP_CENTER, 40f, 30f, RGBAColor.BLACK) minecraft.font.drawAligned(stack, TranslatableComponent("otm.gui.recipe.ticks", recipe.workTime), TextAlign.TOP_CENTER, 40f, 30f, RGBAColor.BLACK)
val average = recipe.experience.toString()
if (average != "0.0") {
minecraft.font.drawAligned(stack, TranslatableComponent("gui.jei.category.smelting.experience", average), TextAlign.TOP_CENTER, 40f, 1f, RGBAColor.BLACK)
}
} }
override fun getWidth(): Int { override fun getWidth(): Int {

View File

@ -1,5 +1,11 @@
package ru.dbotthepony.mc.otm.core package ru.dbotthepony.mc.otm.core
import com.google.gson.JsonElement
import com.google.gson.JsonSyntaxException
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.contents.LiteralContents import net.minecraft.network.chat.contents.LiteralContents
@ -11,6 +17,39 @@ import net.minecraft.world.level.block.Block
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.IForgeRegistry import net.minecraftforge.registries.IForgeRegistry
// because doing it inline is ugly
fun <V : Any> Codec<V>.fromJson(value: JsonElement): V? {
return decode(JsonOps.INSTANCE, value).get().map({ left -> left.first }, { null })
}
fun <V : Any> Codec<V>.fromJsonStrict(value: JsonElement): V {
return decode(JsonOps.INSTANCE, value).get().map({ left -> left.first }, { throw JsonSyntaxException("Error decoding element: ${it.message()}") })
}
fun <V : Any> Codec<V>.toJson(value: V): JsonElement? {
return encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty()).get().map({ it }, { null })
}
fun <V : Any> Codec<V>.toJsonStrict(value: V): JsonElement {
return encode(value, JsonOps.INSTANCE, JsonOps.INSTANCE.empty()).get().map({ it }, { throw RuntimeException("Error encoding element: ${it.message()}") })
}
fun <V : Any> Codec<V>.fromNbt(value: Tag): V? {
return decode(NbtOps.INSTANCE, value).get().map({ left -> left.first }, { null })
}
fun <V : Any> Codec<V>.fromNbtStrict(value: Tag): V {
return decode(NbtOps.INSTANCE, value).get().map({ left -> left.first }, { throw RuntimeException("Error decoding element: ${it.message()}") })
}
fun <V : Any> Codec<V>.toNbt(value: V): Tag? {
return encode(value, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it }, { null })
}
fun <V : Any> Codec<V>.toNbtStrict(value: V): Tag {
return encode(value, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it }, { throw RuntimeException("Error encoding element: ${it.message()}") })
}
// 1.19 being 1.19 // 1.19 being 1.19
fun TranslatableComponent(key: String, vararg values: Any): MutableComponent = MutableComponent.create(TranslatableContents(key, *values)) fun TranslatableComponent(key: String, vararg values: Any): MutableComponent = MutableComponent.create(TranslatableContents(key, *values))
fun TextComponent(value: String): MutableComponent = MutableComponent.create(LiteralContents(value)) fun TextComponent(value: String): MutableComponent = MutableComponent.create(LiteralContents(value))

View File

@ -1,9 +1,19 @@
package ru.dbotthepony.mc.otm.core.util package ru.dbotthepony.mc.otm.core.util
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import io.netty.handler.codec.EncoderException import io.netty.handler.codec.EncoderException
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtIo import net.minecraft.nbt.NbtIo
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
@ -11,6 +21,7 @@ import net.minecraftforge.registries.ForgeRegistry
import java.io.* import java.io.*
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger import java.math.BigInteger
import kotlin.math.absoluteValue
// But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement // But seriously, Mojang, why would you need to derive from ByteBuf directly, when you can implement
// your own InputStream and OutputStream, since ByteBuf is meant to be operated on most time like a stream anyway? // your own InputStream and OutputStream, since ByteBuf is meant to be operated on most time like a stream anyway?
@ -88,3 +99,137 @@ fun InputStream.readBigDecimal(): BigDecimal {
return BigDecimal(BigInteger(bytes), scale) return BigDecimal(BigInteger(bytes), scale)
} }
private const val TYPE_NULL = 0x01
private const val TYPE_DOUBLE = 0x02
private const val TYPE_BOOLEAN = 0x03
private const val TYPE_INT = 0x04
private const val TYPE_STRING = 0x05
private const val TYPE_ARRAY = 0x06
private const val TYPE_OBJECT = 0x07
private fun fixSignedInt(read: Long): Long {
val sign = read and 0x1L
@Suppress("name_shadowing")
val read = read ushr 1
if (sign == 1L) {
return -read - 1L
} else {
return read
}
}
/**
* Writes binary json to stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun OutputStream.writeJson(element: JsonElement) {
if (element is JsonObject) {
write(TYPE_OBJECT)
writeVarIntLE(element.size())
for ((k, v) in element.entrySet()) {
writeBinaryString(k)
writeJson(v)
}
} else if (element is JsonArray) {
write(TYPE_ARRAY)
writeVarIntLE(element.size())
for (v in element) {
writeJson(v)
}
} else if (element is JsonPrimitive) {
if (element.isNumber) {
val num = element.asNumber
if (num is Int || num is Long) {
write(TYPE_INT)
var int = num.toLong()
if (int < 0) {
int = int.absoluteValue.shl(1).or(1)
} else {
int.shl(1)
}
writeVarLongLE(int)
} else if (num is Float || num is Double) {
write(TYPE_DOUBLE)
writeDouble(num.toDouble())
} else {
throw IllegalArgumentException("Unknown number type: ${num::class.qualifiedName}")
}
} else if (element.isString) {
write(TYPE_STRING)
writeBinaryString(element.asString)
} else if (element.isBoolean) {
write(TYPE_BOOLEAN)
write(if (element.asBoolean) 1 else 0)
} else {
write(TYPE_NULL)
}
} else {
throw IllegalArgumentException("Unknown element type: ${element::class.qualifiedName}")
}
}
/**
* Reads binary json from stream in Starbound Object Notation format
*
* just copy pasted this code from my another project because i was lazy
*/
fun InputStream.readJson(): JsonElement {
return when (val id = read()) {
TYPE_NULL -> JsonNull.INSTANCE
TYPE_DOUBLE -> JsonPrimitive(readDouble())
TYPE_BOOLEAN -> JsonPrimitive(read() > 1)
TYPE_INT -> JsonPrimitive(fixSignedInt(readVarLongLE()))
TYPE_STRING -> JsonPrimitive(readBinaryString())
TYPE_ARRAY -> {
val values = readVarIntLE()
if (values == 0) return JsonArray()
if (values < 0) throw JsonSyntaxException("Tried to read json array with $values elements in it")
val build = JsonArray(values)
for (i in 0 until values) build.add(readJson())
return build
}
TYPE_OBJECT -> {
val values = readVarIntLE()
if (values == 0) return JsonObject()
if (values < 0) throw JsonSyntaxException("Tried to read json object with $values elements in it")
val build = JsonObject()
for (i in 0 until values) {
val key: String
try {
key = readBinaryString()
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i", err)
}
try {
build.add(key, readJson())
} catch(err: Throwable) {
throw JsonSyntaxException("Reading json object at $i with name $key", err)
}
}
return build
}
else -> throw JsonParseException("Unknown element type $id")
}
}
fun FriendlyByteBuf.readJson(): JsonElement {
return ByteBufInputStream(this).readJson()
}
fun FriendlyByteBuf.writeJson(value: JsonElement) {
ByteBufOutputStream(this).writeJson(value)
}

View File

@ -26,10 +26,20 @@ open class MatterySlot @JvmOverloads constructor(container: Container, index: In
} }
} }
open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) { open class MachineOutputSlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: () -> Unit = {}) : MatterySlot(container, index, x, y) {
override fun mayPlace(itemStack: ItemStack): Boolean { override fun mayPlace(itemStack: ItemStack): Boolean {
return false return false
} }
override fun onTake(pPlayer: Player, pStack: ItemStack) {
super.onTake(pPlayer, pStack)
this.onTake.invoke()
}
override fun onQuickCraft(pStack: ItemStack, pAmount: Int) {
super.onQuickCraft(pStack, pAmount)
this.onTake.invoke()
}
} }
open class BatterySlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) { open class BatterySlot @JvmOverloads constructor(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.menu.tech package ru.dbotthepony.mc.otm.menu.tech
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity
@ -18,7 +19,7 @@ class PlatePressMenu @JvmOverloads constructor(
val container = tile?.container ?: SimpleContainer(2) val container = tile?.container ?: SimpleContainer(2)
val inputSlot = MatterySlot(container, PlatePressBlockEntity.SLOT_INPUT) val inputSlot = MatterySlot(container, PlatePressBlockEntity.SLOT_INPUT)
val outputSlot = MachineOutputSlot(container, PlatePressBlockEntity.SLOT_OUTPUT) val outputSlot = MachineOutputSlot(container, PlatePressBlockEntity.SLOT_OUTPUT) { tile?.popExperience(ply as ServerPlayer) }
override val storageSlots: List<MatterySlot> = ImmutableList.of(inputSlot, outputSlot) override val storageSlots: List<MatterySlot> = ImmutableList.of(inputSlot, outputSlot)

View File

@ -5,6 +5,8 @@ import com.google.gson.JsonPrimitive
import net.minecraft.core.NonNullList import net.minecraft.core.NonNullList
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.Ingredient
@ -16,16 +18,21 @@ import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity
import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.core.fromJsonStrict
import ru.dbotthepony.mc.otm.core.isActuallyEmpty import ru.dbotthepony.mc.otm.core.isActuallyEmpty
import ru.dbotthepony.mc.otm.registry.MRecipes import ru.dbotthepony.mc.otm.registry.MRecipes
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.core.util.readJson
import ru.dbotthepony.mc.otm.core.util.writeJson
class PlatePressRecipe( class PlatePressRecipe(
private val id: ResourceLocation, private val id: ResourceLocation,
val input: Ingredient, val input: Ingredient,
val output: Ingredient, val output: Ingredient,
val count: Int, val count: Int,
val workTime: Int = 200 val workTime: Int = 200,
val experience: FloatProvider = ConstantFloat.ZERO
) : Recipe<Container> { ) : Recipe<Container> {
override fun matches(container: Container, p_44003_: Level): Boolean { override fun matches(container: Container, p_44003_: Level): Boolean {
if (output.isActuallyEmpty || input.isActuallyEmpty) if (output.isActuallyEmpty || input.isActuallyEmpty)
@ -103,11 +110,12 @@ object PlatePressRecipeFactory : RecipeSerializer<PlatePressRecipe> {
val count = ((obj["result"] as JsonObject)["count"] as? JsonPrimitive)?.let { return@let try {it.asInt} catch(err: Throwable) {throw IllegalStateException("Invalid result.count")} } ?: 1 val count = ((obj["result"] as JsonObject)["count"] as? JsonPrimitive)?.let { return@let try {it.asInt} catch(err: Throwable) {throw IllegalStateException("Invalid result.count")} } ?: 1
return PlatePressRecipe(loc, input, result, count, workTime) val experience = obj["experience"]?.let { FloatProvider.CODEC.fromJsonStrict(it) } ?: ConstantFloat.ZERO
return PlatePressRecipe(loc, input, result, count, workTime, experience)
} }
override fun fromNetwork(loc: ResourceLocation, buff: FriendlyByteBuf): PlatePressRecipe { override fun fromNetwork(loc: ResourceLocation, buff: FriendlyByteBuf): PlatePressRecipe {
return PlatePressRecipe(loc, Ingredient.fromNetwork(buff), Ingredient.fromNetwork(buff), buff.readInt(), buff.readInt()) return PlatePressRecipe(loc, Ingredient.fromNetwork(buff), Ingredient.fromNetwork(buff), buff.readInt(), buff.readInt(), FloatProvider.CODEC.fromJsonStrict(buff.readJson()))
} }
override fun toNetwork(buff: FriendlyByteBuf, recipe: PlatePressRecipe) { override fun toNetwork(buff: FriendlyByteBuf, recipe: PlatePressRecipe) {
@ -115,5 +123,6 @@ object PlatePressRecipeFactory : RecipeSerializer<PlatePressRecipe> {
recipe.output.toNetwork(buff) recipe.output.toNetwork(buff)
buff.writeInt(recipe.count) buff.writeInt(recipe.count)
buff.writeInt(recipe.workTime) buff.writeInt(recipe.workTime)
buff.writeJson(FloatProvider.CODEC.toJsonStrict(recipe.experience))
} }
} }