From e263e29989fda8bbc508aa0b86ceb360de8de9b1 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 1 Jan 2023 18:08:32 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=20=D1=81=D0=B0=D0=BC=D0=BE=D0=BC?= =?UTF-8?q?=20=D0=B4=D0=B5=D0=BB=D0=B5,=20=D0=BC=D1=8B=20=D1=83=D0=B6?= =?UTF-8?q?=D0=B5=20=D0=B8=D0=B7=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=D0=B8=D1=81?= =?UTF-8?q?=D1=8C=20=D0=BE=D1=82=20=D1=81=D1=82=D0=B0=D1=80=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20framegrid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dbotthepony/kstarbound/defs/FrameGrid.kt | 355 ------------------ .../kstarbound/defs/projectile/Configured.kt | 1 - 2 files changed, 356 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/defs/FrameGrid.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/FrameGrid.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/FrameGrid.kt deleted file mode 100644 index c5fc2316..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/FrameGrid.kt +++ /dev/null @@ -1,355 +0,0 @@ -package ru.dbotthepony.kstarbound.defs - -import com.google.common.collect.ImmutableList -import com.google.gson.* -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap -import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap -import org.apache.logging.log4j.LogManager -import ru.dbotthepony.kstarbound.Starbound -import ru.dbotthepony.kvector.vector.nint.Vector2i -import java.io.FileNotFoundException -import kotlin.collections.HashMap - -class MalformedFrameGridException(message: String? = null, cause: Throwable? = null) : Throwable(message, cause) - -data class Frame( - val texture: String, - val texturePosition: Vector2i, - val textureSize: Vector2i, -) { - val textureEndPosition = texturePosition + textureSize - val width get() = textureSize.x - val height get() = textureSize.y - - val aspectRatioWH: Float get() { - if (height == 0) { - return 1f - } - - return width.toFloat() / height.toFloat() - } - - val aspectRatioHW: Float get() { - if (width == 0) { - return 1f - } - - return height.toFloat() / width.toFloat() - } -} - -data class FrameSet( - val texture: String, - val name: String, - val frames: List, -) { - val frameCount get() = frames.size - fun frame(num: Int) = frames[num] -} - -private class FrameSetBuilder(val name: String) { - val frames = Object2ObjectArrayMap() - - fun build(texture: String): FrameSet { - val list = ImmutableList.builder() - val rebuild = Int2ObjectArrayMap() - - for ((k, v) in frames) { - val int = k.toIntOrNull() - - if (int != null) { - rebuild[int] = v - } - } - - if (rebuild.size == 0) { - throw IllegalStateException("Frame Set $name is empty") - } - - for (i in 0 until rebuild.size) { - list.add(rebuild[i] ?: throw IllegalStateException("Frame Set $name has gap at $i")) - } - - return FrameSet( - texture = texture, - name = name, - frames = list.build() - ) - } -} - -interface IFrameGrid { - val texture: String - val size: Vector2i - val dimensions: Vector2i - val frames: List - val frameCount get() = frames.size - val isResolved: Boolean - - val textureSize get() = size * dimensions - - fun resolve(determinedSize: Vector2i) - - operator fun get(index: Int) = frames[index] - - operator fun get(index: String): FrameSet { - for (frame in frames) { - if (index == frame.name) { - return frame - } - } - - throw IndexOutOfBoundsException("No such frame strip with name $index") - } - - companion object { - private fun splitName(textureName: String, input: String, warn: Boolean, lazy: () -> String): Pair { - val split = input.split(':') - val frameName: String - val setName: String - - when (split.size) { - 1 -> { - setName = "root" - frameName = split[0] - } - 2 -> { - setName = split[0] - frameName = split[1] - } - else -> throw IllegalArgumentException("${lazy.invoke()}: Malformed frame name $input") - } - - val frameNumber = frameName.toIntOrNull() - - if (frameNumber == null) { - if (warn) - LOGGER.warn("{}: Frame {} will be discarded after frame grid is built, because it is not an integer", textureName, frameName) - } else { - require(frameNumber >= 0) { "${lazy.invoke()}: Frame number of $frameNumber does not make any sense" } - } - - return setName to frameName - } - - 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) - } - } - } - - /** - * Так как объект довольно сложный для автоматической десериализации через gson - * поэтому вот так - */ - fun fromJson(input: JsonObject, texture: String): IFrameGrid { - val frameGrid: JsonObject - val aliases: JsonObject - val texturePath = "$texture.png".intern() - - if (input["frameGrid"] is JsonObject) { - frameGrid = input["frameGrid"] as JsonObject - } else { - frameGrid = input - } - - if (input["aliases"] is JsonObject) { - aliases = input["aliases"] as JsonObject - } else { - aliases = JsonObject() - } - - val size = Starbound.gson.fromJson(frameGrid["size"], Vector2i::class.java) ?: throw IllegalArgumentException("Size is missing") - val dimensions = Starbound.gson.fromJson(frameGrid["dimensions"], Vector2i::class.java) ?: throw IllegalArgumentException("Dimensions are missing") - - require(size.x > 0) { "Invalid texture width of ${size.x}" } - require(size.y > 0) { "Invalid texture height of ${size.y}" } - - require(dimensions.x > 0) { "Invalid texture frame count of ${dimensions.x}" } - require(dimensions.y > 0) { "Invalid texture stripe count of ${dimensions.y}" } - - val names = frameGrid["names"] as? JsonArray ?: generateFakeNames(dimensions) - - if (names.size() != dimensions.y) { - LOGGER.warn("{} inconsistency: it has Y frame span of {}, but {} name strips are defined", texture, dimensions.y, names.size()) - } - - val frameSets = Object2ObjectArrayMap() - - for (yPosition in 0 until names.size()) { - val list = names[yPosition] as? JsonArray ?: throw IllegalArgumentException("names->$yPosition is not an array") - - if (list.size() != dimensions.x) { - LOGGER.warn("{} inconsistency: it has X frame span of {}, but strip at {} has {} names defined", texture, dimensions.x, yPosition, list.size()) - } - - for (xPosition in 0 until list.size()) { - val fullName = list[xPosition] - - if (fullName is JsonNull) { - continue - } - - fullName as? JsonPrimitive ?: throw IllegalArgumentException("names->$yPosition->$xPosition: Illegal value $fullName") - - val (setName, frameNumber) = splitName(texture, fullName.asString, true) { "names->$yPosition->$xPosition" } - val frameSet = frameSets.computeIfAbsent(setName, ::FrameSetBuilder) - - frameSet.frames[frameNumber] = Frame( - texture = texturePath, - textureSize = size, - texturePosition = Vector2i(x = size.x * xPosition, y = size.y * yPosition)) - } - } - - for ((newName, originalName) in aliases.entrySet()) { - originalName as? JsonPrimitive ?: throw IllegalArgumentException("aliases->$newName: Illegal value $originalName") - - val (oSetName, oFrameNumber) = splitName(texture, originalName.asString, false) { "alias->$newName" } - val (nSetName, nFrameNumber) = splitName(texture, newName, true) { "alias->$newName" } - - val oFrameSet = frameSets.computeIfAbsent(oSetName, ::FrameSetBuilder) - val nFrameSet = frameSets.computeIfAbsent(nSetName, ::FrameSetBuilder) - - nFrameSet.frames[nFrameNumber] = requireNotNull(oFrameSet.frames[oFrameNumber]) { "alias->$newName points to nothing" } - } - - val frameSetList = ImmutableList.builder() - - for (frameSet in frameSets.values) { - frameSetList.add(frameSet.build(texturePath)) - } - - return ResolvedFrameGrid(texturePath, size, dimensions, frameSetList.build()) - } - - fun singleFrame(texturePath: String, size: Vector2i = Vector2i.ZERO): IFrameGrid { - val frame = Frame( - texture = texturePath, - textureSize = size, - texturePosition = Vector2i.ZERO) - - return ResolvedFrameGrid( - texture = texturePath, - size = size, - dimensions = Vector2i.POSITIVE_XY, - frames = listOf(FrameSet( - name = "root", - frames = listOf(frame), - texture = texturePath)) - ) - } - - private val cache = HashMap() - - fun loadCached(path: String, weak: Boolean = false, weakSize: Vector2i = Vector2i.ZERO): IFrameGrid { - if (path[0] != '/') - throw IllegalArgumentException("Path must be absolute") - - val splitPath = path.split('/').toMutableList() - val last = splitPath.last() - splitPath.removeLast() - val splitLast = last.split('.') - - try { - if (splitLast.size == 1) { - // имя уже абсолютное - return cache.computeIfAbsent(path) { - val frames = Starbound.locate("$path.frames", "${splitPath.joinToString("/")}/default.frames").orNull() - - if (weak && frames == null) { - LOGGER.warn("Expected animated texture at {}, but .frames metafile is missing.", path) - - return@computeIfAbsent singleFrame("$path.png", weakSize) - } - - return@computeIfAbsent fromJson((frames?.readJson() ?: throw FileNotFoundException("Unable to find .frames meta for $path")) as JsonObject, path) - } - } - - val newPath = "${splitPath.joinToString("/")}/${splitLast[0]}" - - return cache.computeIfAbsent(newPath) { - val frames = Starbound.locate("$newPath.frames", "${splitPath.joinToString("/")}/default.frames").orNull() - - if (weak && frames == null) { - LOGGER.warn("Expected animated texture at {}, but .frames metafile is missing.", newPath) - - return@computeIfAbsent singleFrame("$newPath.png", weakSize) - } - - return@computeIfAbsent fromJson((frames?.readJson() ?: throw FileNotFoundException("Unable to find .frames meta for $path")) as JsonObject, newPath) - } - } catch (err: Throwable) { - throw MalformedFrameGridException("Reading animated texture definition $path", err) - } - } - - fun loadFrameStrip(path: String, weak: Boolean = false, weakSize: Vector2i = Vector2i.ZERO): FrameSet { - if (path[0] != '/') - throw IllegalArgumentException("Path must be absolute") - - val split = path.split(':') - - if (split.size == 1) { - // мы хотим получить главный кадр, который является анонимным - val load = loadCached(path, weak, weakSize) - check(load.frameCount == 1) { "$path has ${load.frameCount} frame strips, but we want exactly one!" } - return load[0] - } - - val load = loadCached(split[0], weak, weakSize) - return load[split[1]] - } - - private val LOGGER = LogManager.getLogger() - } -} - -data class ResolvedFrameGrid( - override val texture: String, - override val size: Vector2i, - override val dimensions: Vector2i, - - override val frames: List -) : IFrameGrid { - override val isResolved = true - - override fun resolve(determinedSize: Vector2i) { - require(determinedSize.x > 0) { "Invalid image width ${determinedSize.x}" } - require(determinedSize.y > 0) { "Invalid image height ${determinedSize.y}" } - // no-op - } -} - -data class LazyFrameGrid( - override val texture: String, - override val size: Vector2i, - override val dimensions: Vector2i, -) : IFrameGrid { - private var _frames: List? = null - - override val frames: List - get() = _frames ?: throw IllegalStateException("Call resolve() first") - - override var isResolved = false - private set - - override fun resolve(determinedSize: Vector2i) { - if (_frames != null) - return - - check(dimensions == determinedSize) { "$texture was expected to have dimensions of $dimensions, $determinedSize given" } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/projectile/Configured.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/projectile/Configured.kt index fa92604b..2ef23395 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/projectile/Configured.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/projectile/Configured.kt @@ -5,7 +5,6 @@ import org.apache.logging.log4j.LogManager import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.AssembledPrototype import ru.dbotthepony.kstarbound.defs.DamageType -import ru.dbotthepony.kstarbound.defs.FrameSet import ru.dbotthepony.kstarbound.defs.animation.ImageReference import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile