Атлас спрайтов (frames), переделан с нуля
This commit is contained in:
parent
49e9dd2735
commit
8ef4ab0eb1
@ -15,6 +15,7 @@ import ru.dbotthepony.kstarbound.api.NonExistingFile
|
|||||||
import ru.dbotthepony.kstarbound.api.PhysicalFile
|
import ru.dbotthepony.kstarbound.api.PhysicalFile
|
||||||
import ru.dbotthepony.kstarbound.api.explore
|
import ru.dbotthepony.kstarbound.api.explore
|
||||||
import ru.dbotthepony.kstarbound.defs.*
|
import ru.dbotthepony.kstarbound.defs.*
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
||||||
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
|
||||||
@ -32,6 +33,7 @@ import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
|||||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.math.*
|
import ru.dbotthepony.kstarbound.math.*
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.util2d.AABBi
|
import ru.dbotthepony.kvector.util2d.AABBi
|
||||||
@ -135,12 +137,13 @@ object Starbound {
|
|||||||
.registerTypeAdapter(stringTypeAdapter)
|
.registerTypeAdapter(stringTypeAdapter)
|
||||||
|
|
||||||
// math
|
// math
|
||||||
.registerTypeAdapter(AABB::class.java, AABBTypeAdapter)
|
.registerTypeAdapter(AABBTypeAdapter)
|
||||||
.registerTypeAdapter(AABBi::class.java, AABBiTypeAdapter)
|
.registerTypeAdapter(AABBiTypeAdapter)
|
||||||
.registerTypeAdapter(Vector2d::class.java, Vector2dTypeAdapter)
|
.registerTypeAdapter(Vector2dTypeAdapter)
|
||||||
.registerTypeAdapter(Vector2f::class.java, Vector2fTypeAdapter)
|
.registerTypeAdapter(Vector2fTypeAdapter)
|
||||||
.registerTypeAdapter(Vector2i::class.java, Vector2iTypeAdapter)
|
.registerTypeAdapter(Vector2iTypeAdapter)
|
||||||
.registerTypeAdapter(Poly::class.java, PolyTypeAdapter)
|
.registerTypeAdapter(Vector4iTypeAdapter)
|
||||||
|
.registerTypeAdapter(PolyTypeAdapter)
|
||||||
|
|
||||||
.also(ConfigurableProjectile::registerGson)
|
.also(ConfigurableProjectile::registerGson)
|
||||||
.also(SkyParameters::registerGson)
|
.also(SkyParameters::registerGson)
|
||||||
@ -154,6 +157,7 @@ object Starbound {
|
|||||||
.also(LiquidDefinition::registerGson)
|
.also(LiquidDefinition::registerGson)
|
||||||
.also(ItemDefinition::registerGson)
|
.also(ItemDefinition::registerGson)
|
||||||
.also(ItemRarity::registerGson)
|
.also(ItemRarity::registerGson)
|
||||||
|
.also(SpriteReference::registerGson)
|
||||||
|
|
||||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ interface IStarboundFile {
|
|||||||
|
|
||||||
fun orNull(): IStarboundFile? = if (exists) this else null
|
fun orNull(): IStarboundFile? = if (exists) this else null
|
||||||
|
|
||||||
|
operator fun get(name: String): IStarboundFile? {
|
||||||
|
return children?.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
fun computeFullPath(): String {
|
fun computeFullPath(): String {
|
||||||
var path = name
|
var path = name
|
||||||
var parent = parent
|
var parent = parent
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render.entity
|
package ru.dbotthepony.kstarbound.client.render.entity
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46
|
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
@ -11,28 +10,31 @@ import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
|||||||
|
|
||||||
class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
||||||
private val def = entity.def
|
private val def = entity.def
|
||||||
private val texture = def.inventoryIcon?.let(state::loadNamedTextureSafe)
|
private val textures = def.inventoryIcon?.stream()?.map { state.loadNamedTextureSafe(it.image.path) }?.toList() ?: listOf()
|
||||||
|
|
||||||
override fun render(stack: Matrix4fStack) {
|
override fun render(stack: Matrix4fStack) {
|
||||||
if (texture == null)
|
if (textures.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
state.shaderVertexTexture.use()
|
state.shaderVertexTexture.use()
|
||||||
state.shaderVertexTexture.transform.set(stack.last)
|
state.shaderVertexTexture.transform.set(stack.last)
|
||||||
state.activeTexture = 0
|
state.activeTexture = 0
|
||||||
state.shaderVertexTexture["_texture"] = 0
|
state.shaderVertexTexture["_texture"] = 0
|
||||||
texture.bind()
|
|
||||||
|
|
||||||
val builder = state.flat2DTexturedQuads.small
|
for (texture in textures) {
|
||||||
|
texture.bind()
|
||||||
|
|
||||||
builder.begin()
|
val builder = state.flat2DTexturedQuads.small
|
||||||
|
|
||||||
val width = (texture.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
builder.begin()
|
||||||
val height = (texture.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
|
||||||
|
|
||||||
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, QuadTransformers.uv(0f, 1f, 1f, 0f))
|
val width = (texture.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
|
val height = (texture.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
|
|
||||||
builder.upload()
|
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, QuadTransformers.uv(0f, 1f, 1f, 0f))
|
||||||
builder.draw()
|
|
||||||
|
builder.upload()
|
||||||
|
builder.draw()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,233 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.animation
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import com.google.gson.internal.bind.TypeAdapters
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.stream
|
||||||
|
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||||
|
import ru.dbotthepony.kvector.vector.nint.Vector4i
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Атлас спрайтов, собранный вручную артистом
|
||||||
|
*
|
||||||
|
* В файлах игры именуется frames
|
||||||
|
*/
|
||||||
|
class AtlasDefinition private constructor(
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Спрайты данного атласа, включая спрайты под псевдонимами
|
||||||
|
*/
|
||||||
|
val sprites: ImmutableMap<String, Sprite>,
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Первый спрайт, по своему имени (естественная сортировка)
|
||||||
|
*/
|
||||||
|
val first: Sprite = sprites[sprites.keys.stream().sorted().findFirst().orElseThrow { NoSuchElementException("No a single key present in $name") }] ?: throw NoSuchElementException("IMPOSSIBRU in $name")
|
||||||
|
|
||||||
|
operator fun get(name: String): Sprite? {
|
||||||
|
return sprites[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun any(name: String): Sprite {
|
||||||
|
return get(name) ?: first
|
||||||
|
}
|
||||||
|
|
||||||
|
fun any(): Sprite {
|
||||||
|
return get("root") ?: get("0") ?: first
|
||||||
|
}
|
||||||
|
|
||||||
|
class Sprite(
|
||||||
|
/**
|
||||||
|
* Имя данного спрайта, на которое прототипы могут ссылаться
|
||||||
|
*
|
||||||
|
* К примеру, если имя будет указано как
|
||||||
|
*
|
||||||
|
* **`active.2`**
|
||||||
|
*
|
||||||
|
* а сам атлас называется
|
||||||
|
*
|
||||||
|
* **`mything.png`**
|
||||||
|
*
|
||||||
|
* то прототипы могут ссылаться на данный спрайт как
|
||||||
|
*
|
||||||
|
* **`mything.png:active.2`**
|
||||||
|
*/
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Позиция и размеры данного спрайта, в пикселях
|
||||||
|
*/
|
||||||
|
val position: Vector4i,
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Вычисляет uv координаты данного спрайта на заданном полотне
|
||||||
|
*/
|
||||||
|
fun compute(width: Int, height: Int): UVCoordinates {
|
||||||
|
return UVCoordinates(
|
||||||
|
u0 = position.x.toFloat() / width.toFloat(),
|
||||||
|
v0 = position.y.toFloat() / height.toFloat(),
|
||||||
|
u1 = position.z.toFloat() / width.toFloat(),
|
||||||
|
v1 = position.w.toFloat() / height.toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вычисляет uv координаты данного спрайта на заданном полотне
|
||||||
|
*/
|
||||||
|
fun compute(dimensions: Vector2i): UVCoordinates {
|
||||||
|
return compute(dimensions.x, dimensions.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вычисляет uv координаты данного спрайта на заданном полотне
|
||||||
|
*/
|
||||||
|
fun compute(texture: GLTexture2D): UVCoordinates {
|
||||||
|
return compute(texture.width, texture.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "FrameGrid.Sprite[name=$name, position=$position]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val EMPTY = AtlasDefinition("null", ImmutableMap.of("root", Sprite("root", Vector4i(0, 0, 1, 1))))
|
||||||
|
|
||||||
|
private val cache = ConcurrentHashMap<String, AtlasDefinition>()
|
||||||
|
|
||||||
|
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
|
||||||
|
return JsonArray(dimensions.y).also {
|
||||||
|
var stripElem = 0
|
||||||
|
|
||||||
|
for (stripNum in 0 until dimensions.y) {
|
||||||
|
val strip = JsonArray(dimensions.x)
|
||||||
|
|
||||||
|
for (i in 0 until dimensions.x) {
|
||||||
|
strip.add(stripElem.toString())
|
||||||
|
stripElem++
|
||||||
|
}
|
||||||
|
|
||||||
|
it.add(strip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFrames(input: JsonReader, name: String): AtlasDefinition {
|
||||||
|
val read = TypeAdapters.JSON_ELEMENT.read(input)
|
||||||
|
|
||||||
|
if (read !is JsonObject) {
|
||||||
|
throw JsonSyntaxException("Expected to have object as top most element, $read given")
|
||||||
|
}
|
||||||
|
|
||||||
|
val frameGrid = read["frameGrid"]
|
||||||
|
val sprites = HashMap<String, Sprite>()
|
||||||
|
|
||||||
|
if (frameGrid is JsonObject) {
|
||||||
|
val size = Starbound.gson.fromJson(frameGrid["size"] ?: throw JsonSyntaxException("Missing frameGrid.size"), Vector2i::class.java)
|
||||||
|
val dimensions = Starbound.gson.fromJson(frameGrid["dimensions"] ?: throw JsonSyntaxException("Missing frameGrid.dimensions"), Vector2i::class.java)
|
||||||
|
|
||||||
|
require(size.x >= 0) { "Invalid size.x: ${size.x}" }
|
||||||
|
require(size.y >= 0) { "Invalid size.y: ${size.y}" }
|
||||||
|
|
||||||
|
require(dimensions.x >= 0) { "Invalid dimensions.x: ${dimensions.x}" }
|
||||||
|
require(dimensions.y >= 0) { "Invalid dimensions.y: ${dimensions.y}" }
|
||||||
|
|
||||||
|
val names = (frameGrid["names"] as? JsonArray ?: generateFakeNames(dimensions))
|
||||||
|
.stream().map { (it as? JsonArray)?.stream()?.map { if (it == JsonNull.INSTANCE) null else it.asString }?.toList() }.toList()
|
||||||
|
|
||||||
|
val spriteList = ArrayList<Sprite>()
|
||||||
|
|
||||||
|
for ((y, strip) in names.withIndex()) {
|
||||||
|
// разрешаем вставлять null как ленту кадров, что означает что мы должны пропустить её полностью
|
||||||
|
if (strip == null)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for ((x, spriteName) in strip.withIndex()) {
|
||||||
|
// если кадр не имеет имени...
|
||||||
|
if (spriteName == null)
|
||||||
|
continue
|
||||||
|
|
||||||
|
spriteList.add(Sprite(spriteName, Vector4i(x * size.x, y * size.y, (x + 1) * size.x, (y + 1) * size.y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (sprite in spriteList)
|
||||||
|
sprites[sprite.name] = sprite
|
||||||
|
} else if (frameGrid != null) {
|
||||||
|
throw JsonSyntaxException("Unexpected frameGrid element: $frameGrid")
|
||||||
|
} else {
|
||||||
|
val frameList = read["frameList"] ?: throw JsonSyntaxException("Frame grid must have either frameGrid or frameList object defined")
|
||||||
|
|
||||||
|
if (frameList !is JsonObject) {
|
||||||
|
throw JsonSyntaxException("frameList is not an object")
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((spriteName, coords) in frameList.entrySet()) {
|
||||||
|
sprites[spriteName] = Sprite(spriteName, Starbound.gson.fromJson(coords, Vector4i::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val aliases = read["aliases"]
|
||||||
|
|
||||||
|
if (aliases != null) {
|
||||||
|
if (aliases !is JsonObject)
|
||||||
|
throw JsonSyntaxException("aliases expected to be a Json object, $aliases given")
|
||||||
|
|
||||||
|
for ((k, v) in aliases.entrySet())
|
||||||
|
sprites[k] = sprites[v.asString] ?: throw JsonSyntaxException("$k want to refer to sprite $v, but it does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
return AtlasDefinition(name, ImmutableMap.copyOf(sprites))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recursiveGet(name: String, folder: String): AtlasDefinition? {
|
||||||
|
var current = folder
|
||||||
|
|
||||||
|
while (current != "/" && current != "") {
|
||||||
|
val get = cache.computeIfAbsent("$current/$name") {
|
||||||
|
val file = Starbound.locate("$it.frames")
|
||||||
|
|
||||||
|
if (file.exists) {
|
||||||
|
try {
|
||||||
|
return@computeIfAbsent parseFrames(JsonReader(file.reader()), "$it.frames")
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
throw JsonSyntaxException("Reading frame grid $it.frames")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@computeIfAbsent EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get !== EMPTY) {
|
||||||
|
return get
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.substringBeforeLast('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(path: String): AtlasDefinition {
|
||||||
|
require(path[0] == '/') { "$path is not an absolute path" }
|
||||||
|
val folder = path.substringBeforeLast('/').lowercase()
|
||||||
|
val filename = path.substringAfterLast('/').substringBefore('.').lowercase()
|
||||||
|
|
||||||
|
val direct = recursiveGet(filename, folder)
|
||||||
|
if (direct != null) return direct
|
||||||
|
|
||||||
|
val default = recursiveGet("default", folder)
|
||||||
|
if (default != null) return default
|
||||||
|
|
||||||
|
return EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.animation
|
||||||
|
|
||||||
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
|
|
||||||
|
interface IUVCoordinates : IStruct4f {
|
||||||
|
val u0: Float
|
||||||
|
val v0: Float
|
||||||
|
val u1: Float
|
||||||
|
val v1: Float
|
||||||
|
|
||||||
|
override fun component1(): Float {
|
||||||
|
return u0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun component2(): Float {
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun component3(): Float {
|
||||||
|
return u1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun component4(): Float {
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.animation
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
|
|
||||||
|
data class SpriteReference(
|
||||||
|
val path: String,
|
||||||
|
val sprite: AtlasDefinition.Sprite
|
||||||
|
) {
|
||||||
|
companion object : TypeAdapter<SpriteReference>() {
|
||||||
|
fun parse(input: String): SpriteReference {
|
||||||
|
val grid = AtlasDefinition.get(input.substringBefore(':'))
|
||||||
|
|
||||||
|
return when (input.count { it == ':' }) {
|
||||||
|
0 -> SpriteReference(input, grid.any())
|
||||||
|
1 -> SpriteReference(input.substringBefore(':'), grid.get(input.substringAfter(':')) ?: throw NoSuchElementException("No such sprite with name ${input.substringAfter(':')} present in frame grid ${grid.name} (atlas ${input.substringBefore(':')})"))
|
||||||
|
else -> throw IllegalArgumentException("Invalid sprite reference: $input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(out: JsonWriter, value: SpriteReference) {
|
||||||
|
out.value(value.path + ":" + value.sprite.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(`in`: JsonReader): SpriteReference {
|
||||||
|
return parse(Starbound.readingFolderTransformer(`in`.nextString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
|
gsonBuilder.registerTypeAdapter(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.animation
|
||||||
|
|
||||||
|
data class UVCoordinates(
|
||||||
|
override val u0: Float,
|
||||||
|
override val v0: Float,
|
||||||
|
override val u1: Float,
|
||||||
|
override val v1: Float,
|
||||||
|
) : IUVCoordinates
|
@ -1,12 +1,10 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.item
|
package ru.dbotthepony.kstarbound.defs.item
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.TypeAdapter
|
|
||||||
import com.google.gson.stream.JsonReader
|
|
||||||
import com.google.gson.stream.JsonToken
|
|
||||||
import com.google.gson.stream.JsonWriter
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.io.json.KConcreteTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.KConcreteTypeAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.ListAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.asJsonObject
|
import ru.dbotthepony.kstarbound.io.json.asJsonObject
|
||||||
import ru.dbotthepony.kstarbound.io.json.asList
|
import ru.dbotthepony.kstarbound.io.json.asList
|
||||||
import ru.dbotthepony.kstarbound.io.json.ifString
|
import ru.dbotthepony.kstarbound.io.json.ifString
|
||||||
@ -37,7 +35,7 @@ data class ItemDefinition(
|
|||||||
/**
|
/**
|
||||||
* Иконка в инвентаре, относительный и абсолютный пути
|
* Иконка в инвентаре, относительный и абсолютный пути
|
||||||
*/
|
*/
|
||||||
val inventoryIcon: String? = null,
|
val inventoryIcon: List<InventoryIcon>? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Описание предмета
|
* Описание предмета
|
||||||
@ -260,13 +258,21 @@ data class ItemDefinition(
|
|||||||
val amount: Double = 0.0,
|
val amount: Double = 0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class InventoryIcon(
|
||||||
|
val image: SpriteReference
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
val INVENTORY_ICON_ADAPTER = KConcreteTypeAdapter.Builder(InventoryIcon::class)
|
||||||
|
.auto(InventoryIcon::image)
|
||||||
|
.build()
|
||||||
|
|
||||||
val ADAPTER = KConcreteTypeAdapter.Builder(ItemDefinition::class)
|
val ADAPTER = KConcreteTypeAdapter.Builder(ItemDefinition::class)
|
||||||
.auto(ItemDefinition::itemName)
|
.auto(ItemDefinition::itemName)
|
||||||
.auto(ItemDefinition::price)
|
.auto(ItemDefinition::price)
|
||||||
.auto(ItemDefinition::rarity)
|
.auto(ItemDefinition::rarity)
|
||||||
.auto(ItemDefinition::category)
|
.auto(ItemDefinition::category)
|
||||||
.auto(ItemDefinition::inventoryIcon, Starbound::readingFolderTransformerNullable)
|
.add(ItemDefinition::inventoryIcon, ListAdapter(INVENTORY_ICON_ADAPTER).ifString { listOf(InventoryIcon(SpriteReference.parse(Starbound.readingFolderTransformer(it)))) }.nullSafe())
|
||||||
.auto(ItemDefinition::description)
|
.auto(ItemDefinition::description)
|
||||||
.auto(ItemDefinition::shortdescription)
|
.auto(ItemDefinition::shortdescription)
|
||||||
|
|
||||||
@ -342,6 +348,7 @@ data class ItemDefinition(
|
|||||||
gsonBuilder.registerTypeAdapter(FOSSIL_ADAPTER)
|
gsonBuilder.registerTypeAdapter(FOSSIL_ADAPTER)
|
||||||
gsonBuilder.registerTypeAdapter(ARMOR_FRAMES_ADAPTER)
|
gsonBuilder.registerTypeAdapter(ARMOR_FRAMES_ADAPTER)
|
||||||
gsonBuilder.registerTypeAdapter(STATUS_EFFECT_ADAPTER)
|
gsonBuilder.registerTypeAdapter(STATUS_EFFECT_ADAPTER)
|
||||||
|
gsonBuilder.registerTypeAdapter(INVENTORY_ICON_ADAPTER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.io.json
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectSpliterator
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectSpliterators
|
||||||
|
import java.util.stream.Stream
|
||||||
|
import java.util.stream.StreamSupport
|
||||||
|
|
||||||
|
class JsonArraySpliterator(private val obj: JsonArray, offset: Int = 0, private val maxPos: Int = obj.size()) : ObjectSpliterators.AbstractIndexBasedSpliterator<JsonElement>(offset) {
|
||||||
|
init {
|
||||||
|
require(offset >= 0) { "Invalid offset $offset" }
|
||||||
|
require(offset + maxPos <= obj.size()) { "$offset -> $maxPos while having only size of ${obj.size()}!" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(location: Int): JsonElement {
|
||||||
|
return obj[location]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMaxPos(): Int {
|
||||||
|
return maxPos
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun makeForSplit(pos: Int, maxPos: Int): ObjectSpliterator<JsonElement> {
|
||||||
|
return JsonArraySpliterator(obj, pos, maxPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonArray.elementSpliterator() = JsonArraySpliterator(this)
|
||||||
|
fun JsonArray.stream(): Stream<out JsonElement> = StreamSupport.stream(elementSpliterator(), false)
|
@ -6,6 +6,31 @@ import com.google.gson.stream.JsonWriter
|
|||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||||
|
import ru.dbotthepony.kvector.vector.nint.Vector4i
|
||||||
|
|
||||||
|
object Vector4iTypeAdapter : TypeAdapter<Vector4i>() {
|
||||||
|
override fun write(out: JsonWriter, value: Vector4i) {
|
||||||
|
`out`.beginArray()
|
||||||
|
`out`.value(value.x)
|
||||||
|
`out`.value(value.y)
|
||||||
|
`out`.value(value.z)
|
||||||
|
`out`.value(value.w)
|
||||||
|
`out`.endArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(`in`: JsonReader): Vector4i {
|
||||||
|
`in`.beginArray()
|
||||||
|
|
||||||
|
val x = `in`.nextInt()
|
||||||
|
val y = `in`.nextInt()
|
||||||
|
val z = `in`.nextInt()
|
||||||
|
val w = `in`.nextInt()
|
||||||
|
|
||||||
|
`in`.endArray()
|
||||||
|
|
||||||
|
return Vector4i(x, y, z, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object Vector2iTypeAdapter : TypeAdapter<Vector2i>() {
|
object Vector2iTypeAdapter : TypeAdapter<Vector2i>() {
|
||||||
override fun write(out: JsonWriter, value: Vector2i) {
|
override fun write(out: JsonWriter, value: Vector2i) {
|
||||||
|
Loading…
Reference in New Issue
Block a user