218 lines
5.1 KiB
Kotlin
218 lines
5.1 KiB
Kotlin
package ru.dbotthepony.kstarbound.item
|
|
|
|
import com.google.gson.JsonNull
|
|
import com.google.gson.JsonObject
|
|
import com.google.gson.JsonPrimitive
|
|
import com.google.gson.TypeAdapter
|
|
import com.google.gson.internal.bind.TypeAdapters
|
|
import com.google.gson.stream.JsonReader
|
|
import com.google.gson.stream.JsonWriter
|
|
import org.classdump.luna.Table
|
|
import org.classdump.luna.TableFactory
|
|
import ru.dbotthepony.kstarbound.Registry
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
|
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
|
|
import ru.dbotthepony.kommons.gson.consumeNull
|
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
import ru.dbotthepony.kommons.io.writeVarLong
|
|
import ru.dbotthepony.kstarbound.Registries
|
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
|
import ru.dbotthepony.kstarbound.lua.from
|
|
import java.io.DataOutputStream
|
|
import java.util.concurrent.atomic.AtomicLong
|
|
|
|
/**
|
|
* Base class for instanced items in game
|
|
*/
|
|
open class ItemStack {
|
|
constructor() {
|
|
this.config = Registries.items.emptyRef
|
|
this.parameters = JsonObject()
|
|
}
|
|
|
|
constructor(descriptor: ItemDescriptor) {
|
|
this.config = Registries.items.ref(descriptor.name)
|
|
this.count = descriptor.count
|
|
this.parameters = descriptor.parameters.deepCopy()
|
|
}
|
|
|
|
/**
|
|
* unique number utilized to determine whenever stack has changed
|
|
*/
|
|
var changeset: Long = CHANGESET.incrementAndGet()
|
|
private set
|
|
|
|
/**
|
|
* it uses global atomic long to guarantee stacks having different
|
|
* changesets throughout entire lifetime of game
|
|
*/
|
|
protected fun bumpVersion() {
|
|
changeset = CHANGESET.incrementAndGet()
|
|
}
|
|
|
|
var count: Long = 0L
|
|
set(value) {
|
|
field = value.coerceAtLeast(0L)
|
|
}
|
|
|
|
val config: Registry.Ref<IItemDefinition>
|
|
val parameters: JsonObject
|
|
|
|
val isEmpty: Boolean
|
|
get() = count <= 0 || config.isEmpty
|
|
|
|
val isNotEmpty: Boolean
|
|
get() = count > 0 && config.isPresent
|
|
|
|
val maxStackSize: Long
|
|
get() = config.value?.maxStack ?: 0L
|
|
|
|
fun grow(amount: Long) {
|
|
count += amount
|
|
}
|
|
|
|
fun shrink(amount: Long) {
|
|
count -= amount
|
|
}
|
|
|
|
fun createDescriptor(): ItemDescriptor {
|
|
if (isEmpty)
|
|
return ItemDescriptor.EMPTY
|
|
|
|
return ItemDescriptor(config.key.left(), count, parameters.deepCopy())
|
|
}
|
|
|
|
// faster than creating an item descriptor and writing it (because it avoids copying and allocation)
|
|
fun write(stream: DataOutputStream) {
|
|
if (isEmpty) {
|
|
stream.writeBinaryString("")
|
|
stream.writeVarLong(0L)
|
|
stream.writeJsonElement(JsonNull.INSTANCE)
|
|
} else {
|
|
stream.writeBinaryString(config.key.left())
|
|
stream.writeVarLong(count)
|
|
stream.writeJsonElement(parameters)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Возвращает null если этот предмет пуст
|
|
*/
|
|
fun conciseToNull(): ItemStack? {
|
|
if (isEmpty) {
|
|
return null
|
|
} else {
|
|
return this
|
|
}
|
|
}
|
|
|
|
fun mergeFrom(other: ItemStack, simulate: Boolean) {
|
|
if (isStackable(other)) {
|
|
val newCount = (count + other.count).coerceAtMost(maxStackSize)
|
|
val diff = newCount - count
|
|
other.count -= diff
|
|
|
|
if (!simulate)
|
|
count = newCount
|
|
}
|
|
}
|
|
|
|
fun lenientEquals(other: Any?): Boolean {
|
|
if (other !is ItemStack)
|
|
return false
|
|
|
|
if (isEmpty)
|
|
return other.isEmpty
|
|
|
|
return other.count == count && other.config == config
|
|
}
|
|
|
|
fun isStackable(other: ItemStack): Boolean {
|
|
if (isEmpty || other.isEmpty)
|
|
return false
|
|
|
|
return count != 0L && other.count != 0L && maxStackSize < count && other.config == config && other.parameters == parameters
|
|
}
|
|
|
|
override fun equals(other: Any?): Boolean {
|
|
if (other !is ItemStack)
|
|
return false
|
|
|
|
if (isEmpty)
|
|
return other.isEmpty
|
|
|
|
return other.count == count && other.config == config && other.parameters == parameters
|
|
}
|
|
|
|
override fun hashCode(): Int {
|
|
return config.hashCode()
|
|
}
|
|
|
|
override fun toString(): String {
|
|
if (isEmpty)
|
|
return "ItemStack.EMPTY"
|
|
|
|
return "ItemDescriptor[${config.value?.itemName}, count = $count, params = $parameters]"
|
|
}
|
|
|
|
fun copy(): ItemStack {
|
|
if (isEmpty)
|
|
return this
|
|
|
|
return ItemStack(ItemDescriptor(config, count, parameters.deepCopy()))
|
|
}
|
|
|
|
fun toJson(): JsonObject? {
|
|
if (isEmpty)
|
|
return null
|
|
|
|
return JsonObject().also {
|
|
it.add("name", JsonPrimitive(config.key.left()))
|
|
it.add("count", JsonPrimitive(count))
|
|
it.add("parameters", parameters.deepCopy())
|
|
}
|
|
}
|
|
|
|
fun toTable(allocator: TableFactory): Table? {
|
|
if (isEmpty) {
|
|
return null
|
|
}
|
|
|
|
return allocator.newTable(0, 3).also {
|
|
it.rawset("name", config.key.left())
|
|
it.rawset("count", count)
|
|
it.rawset("parameters", allocator.from(parameters))
|
|
}
|
|
}
|
|
|
|
class Adapter(val starbound: Starbound) : TypeAdapter<ItemStack>() {
|
|
override fun write(out: JsonWriter, value: ItemStack?) {
|
|
val json = value?.toJson()
|
|
|
|
if (json == null)
|
|
out.nullValue()
|
|
else
|
|
TypeAdapters.JSON_ELEMENT.write(out, json)
|
|
}
|
|
|
|
override fun read(`in`: JsonReader): ItemStack {
|
|
if (`in`.consumeNull())
|
|
return EMPTY
|
|
|
|
return starbound.item(TypeAdapters.JSON_ELEMENT.read(`in`))
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private val CHANGESET = AtomicLong()
|
|
|
|
@JvmField
|
|
val EMPTY = ItemStack()
|
|
|
|
fun create(descriptor: ItemDescriptor): ItemStack {
|
|
return EMPTY
|
|
}
|
|
}
|
|
}
|