Starbound теперь более не синглтон, а настоящий класс
удалил кучу устаревших классов ибо они совсем не имеют смысла
This commit is contained in:
parent
210d065f79
commit
15abdba2c5
7
src/main/kotlin/ru/dbotthepony/kstarbound/Constants.kt
Normal file
7
src/main/kotlin/ru/dbotthepony/kstarbound/Constants.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
const val METRES_IN_STARBOUND_UNIT = 0.5
|
||||
const val METRES_IN_STARBOUND_UNITf = 0.5f
|
||||
|
||||
const val PIXELS_IN_STARBOUND_UNIT = 8.0
|
||||
const val PIXELS_IN_STARBOUND_UNITf = 8.0f
|
@ -1,13 +1,10 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.gson.GsonBuilder
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.Version
|
||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.io.*
|
||||
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
|
||||
import ru.dbotthepony.kstarbound.io.BTreeDB
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
||||
@ -16,11 +13,11 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.util.zip.Inflater
|
||||
import kotlin.random.Random
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
fun main() {
|
||||
val starbound = Starbound()
|
||||
LOGGER.info("Running LWJGL ${Version.getVersion()}")
|
||||
|
||||
//Thread.sleep(6_000L)
|
||||
@ -28,10 +25,10 @@ fun main() {
|
||||
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
|
||||
//val db = BTreeDB(File("world.world"))
|
||||
|
||||
val client = StarboundClient()
|
||||
val client = StarboundClient(starbound)
|
||||
|
||||
//Starbound.addFilePath(File("./unpacked_assets/"))
|
||||
Starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak"))
|
||||
starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak"))
|
||||
|
||||
/*for (folder in File("J:\\Steam\\steamapps\\workshop\\content\\211820").list()!!) {
|
||||
val f = File("J:\\Steam\\steamapps\\workshop\\content\\211820\\$folder\\contents.pak")
|
||||
@ -43,17 +40,17 @@ fun main() {
|
||||
|
||||
//Starbound.addPakPath(File("packed.pak"))
|
||||
|
||||
Starbound.initializeGame { finished, replaceStatus, status ->
|
||||
starbound.initializeGame { finished, replaceStatus, status ->
|
||||
client.putDebugLog(status, replaceStatus)
|
||||
}
|
||||
|
||||
client.onTermination {
|
||||
Starbound.terminateLoading = true
|
||||
starbound.terminateLoading = true
|
||||
}
|
||||
|
||||
val ent = PlayerEntity(client.world!!)
|
||||
|
||||
Starbound.onInitialize {
|
||||
starbound.onInitialize {
|
||||
var find = 0L
|
||||
var set = 0L
|
||||
var parse = 0L
|
||||
@ -86,7 +83,7 @@ fun main() {
|
||||
for (y in 0 .. 31) {
|
||||
for (x in 0 .. 31) {
|
||||
val materialID = reader.readUnsignedShort()
|
||||
val getMat = Starbound.TILE_BY_ID[materialID]
|
||||
val getMat = starbound.tilesByID[materialID]
|
||||
|
||||
if (getMat != null) {
|
||||
chunk.foreground[x, y].material = getMat
|
||||
@ -99,7 +96,7 @@ fun main() {
|
||||
val colorVariant = reader.readUnsignedByte()
|
||||
|
||||
val modifier = reader.readUnsignedShort()
|
||||
val getModifier = Starbound.TILE_MODIFIER_BY_ID[modifier]
|
||||
val getModifier = starbound.tileModifiersByID[modifier]
|
||||
|
||||
chunk.foreground[x, y].color = colorVariant
|
||||
chunk.foreground[x, y].setHueShift(colorShift)
|
||||
@ -113,7 +110,7 @@ fun main() {
|
||||
chunk.foreground[x, y].setModifierHueShift(modifierHueShift)
|
||||
|
||||
val materialID2 = reader.readUnsignedShort()
|
||||
val getMat2 = Starbound.TILE_BY_ID[materialID2]
|
||||
val getMat2 = starbound.tilesByID[materialID2]
|
||||
|
||||
if (getMat2 != null) {
|
||||
chunk.background[x, y].material = getMat2
|
||||
@ -127,7 +124,7 @@ fun main() {
|
||||
val colorVariant2 = reader.readUnsignedByte()
|
||||
|
||||
val modifier2 = reader.readUnsignedShort()
|
||||
val getModifier2 = Starbound.TILE_MODIFIER_BY_ID[modifier2]
|
||||
val getModifier2 = starbound.tileModifiersByID[modifier2]
|
||||
|
||||
if (getModifier2 != null && getMat2 != null) {
|
||||
chunk.background[x, y].modifier = getModifier2
|
||||
@ -151,7 +148,7 @@ fun main() {
|
||||
val indestructible = reader.readBoolean()
|
||||
val unknown = reader.readUnsignedByte()
|
||||
|
||||
val getLiquid = Starbound.LIQUID_BY_ID[liquid]
|
||||
val getLiquid = starbound.liquidByID[liquid]
|
||||
|
||||
if (getLiquid != null) {
|
||||
val state = chunk.setLiquid(x, y, getLiquid)!!
|
||||
@ -177,7 +174,7 @@ fun main() {
|
||||
|
||||
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
||||
|
||||
val item = Starbound.ITEM.values.random()
|
||||
val item = starbound.items.values.random()
|
||||
val rand = java.util.Random()
|
||||
|
||||
for (i in 0 .. 10) {
|
||||
@ -275,7 +272,7 @@ fun main() {
|
||||
}
|
||||
|
||||
while (client.renderFrame()) {
|
||||
Starbound.pollCallbacks()
|
||||
starbound.pollCallbacks()
|
||||
|
||||
//ent.think(client.frameRenderTime)
|
||||
//client.camera.pos.x = ent.position.x.toFloat()
|
||||
|
176
src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt
Normal file
176
src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt
Normal file
@ -0,0 +1,176 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.IntCollection
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator
|
||||
import it.unimi.dsi.fastutil.ints.IntSet
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectCollection
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.util.*
|
||||
|
||||
class ObjectRegistry<T>(val name: String, val key: (T) -> String, val intKey: ((T) -> Int)? = null) {
|
||||
private val objects = Object2ObjectOpenHashMap<String, T>()
|
||||
private val intObjects = Int2ObjectOpenHashMap<T>()
|
||||
|
||||
private val origins = Object2ObjectOpenHashMap<String, Any>()
|
||||
private val intOrigins = Int2ObjectOpenHashMap<Any>()
|
||||
|
||||
val view: Map<String, T> = Collections.unmodifiableMap(objects)
|
||||
val intView = object : Int2ObjectMap<T> {
|
||||
override fun get(key: Int): T? = intObjects[key]
|
||||
override fun containsKey(key: Int) = intObjects.containsKey(key)
|
||||
override fun defaultReturnValue(rv: T) = throw UnsupportedOperationException()
|
||||
override fun defaultReturnValue(): T = throw UnsupportedOperationException()
|
||||
override fun containsValue(value: T) = intObjects.containsValue(value)
|
||||
override fun isEmpty() = intObjects.isEmpty()
|
||||
override fun putAll(from: Map<out Int, T>) = throw UnsupportedOperationException()
|
||||
|
||||
private val int2ObjectEntrySet: ObjectSet<Int2ObjectMap.Entry<T>> = object : ObjectSet<Int2ObjectMap.Entry<T>> {
|
||||
override val size: Int
|
||||
get() = intObjects.int2ObjectEntrySet().size
|
||||
|
||||
override fun add(element: Int2ObjectMap.Entry<T>) = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<Int2ObjectMap.Entry<T>>) = throw UnsupportedOperationException()
|
||||
override fun clear() = throw UnsupportedOperationException()
|
||||
|
||||
override fun iterator(): ObjectIterator<Int2ObjectMap.Entry<T>> {
|
||||
return object : ObjectIterator<Int2ObjectMap.Entry<T>> {
|
||||
val iterator = intObjects.int2ObjectEntrySet().iterator()
|
||||
override fun hasNext() = iterator.hasNext()
|
||||
override fun remove() = throw UnsupportedOperationException()
|
||||
override fun next() = iterator.next()
|
||||
}
|
||||
}
|
||||
|
||||
override fun contains(element: Int2ObjectMap.Entry<T>) = intObjects.int2ObjectEntrySet().contains(element)
|
||||
override fun containsAll(elements: Collection<Int2ObjectMap.Entry<T>>) = intObjects.int2ObjectEntrySet().containsAll(elements)
|
||||
override fun isEmpty() = intObjects.int2ObjectEntrySet().isEmpty()
|
||||
override fun remove(element: Int2ObjectMap.Entry<T>) = throw UnsupportedOperationException()
|
||||
override fun removeAll(elements: Collection<Int2ObjectMap.Entry<T>>) = throw UnsupportedOperationException()
|
||||
override fun retainAll(elements: Collection<Int2ObjectMap.Entry<T>>) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun int2ObjectEntrySet(): ObjectSet<Int2ObjectMap.Entry<T>> {
|
||||
return int2ObjectEntrySet
|
||||
}
|
||||
|
||||
override val keys: IntSet = object : IntSet {
|
||||
override fun add(key: Int) = throw UnsupportedOperationException()
|
||||
override fun addAll(c: IntCollection) = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<Int>) = throw UnsupportedOperationException()
|
||||
override fun clear() = throw UnsupportedOperationException()
|
||||
|
||||
override fun iterator(): IntIterator {
|
||||
return object : IntIterator {
|
||||
val iterator = intObjects.keys.iterator()
|
||||
override fun hasNext() = iterator.hasNext()
|
||||
override fun remove() = throw UnsupportedOperationException()
|
||||
override fun nextInt() = iterator.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(k: Int) = throw UnsupportedOperationException()
|
||||
override fun removeAll(c: IntCollection) = throw UnsupportedOperationException()
|
||||
override fun removeAll(elements: Collection<Int>) = throw UnsupportedOperationException()
|
||||
override fun retainAll(c: IntCollection) = throw UnsupportedOperationException()
|
||||
override fun retainAll(elements: Collection<Int>) = throw UnsupportedOperationException()
|
||||
override fun contains(key: Int) = intObjects.keys.contains(key)
|
||||
override fun containsAll(c: IntCollection) = intObjects.keys.containsAll(c)
|
||||
override fun containsAll(elements: Collection<Int>) = intObjects.keys.containsAll(elements)
|
||||
override fun isEmpty() = intObjects.keys.isEmpty()
|
||||
override fun toArray(a: IntArray) = intObjects.keys.toArray(a)
|
||||
override fun toIntArray() = intObjects.keys.toIntArray()
|
||||
|
||||
override val size: Int
|
||||
get() = intObjects.keys.size
|
||||
}
|
||||
|
||||
override val values: ObjectCollection<T> = object : ObjectCollection<T> {
|
||||
override val size: Int
|
||||
get() = intObjects.values.size
|
||||
|
||||
override fun add(element: T) = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<T>) = throw UnsupportedOperationException()
|
||||
override fun clear() = throw UnsupportedOperationException()
|
||||
|
||||
override fun iterator(): ObjectIterator<T> {
|
||||
return object : ObjectIterator<T> {
|
||||
val iterator = intObjects.values.iterator()
|
||||
override fun hasNext() = iterator.hasNext()
|
||||
override fun next(): T = iterator.next()
|
||||
override fun remove() = throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun contains(element: T) = intObjects.values.contains(element)
|
||||
override fun containsAll(elements: Collection<T>) = intObjects.values.containsAll(elements)
|
||||
override fun isEmpty() = intObjects.values.isEmpty()
|
||||
override fun remove(element: T) = throw UnsupportedOperationException()
|
||||
override fun removeAll(elements: Collection<T>) = throw UnsupportedOperationException()
|
||||
override fun retainAll(elements: Collection<T>) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = intObjects.size
|
||||
}
|
||||
|
||||
fun clearOrigins() {
|
||||
origins.clear()
|
||||
intOrigins.clear()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
clearOrigins()
|
||||
objects.clear()
|
||||
intObjects.clear()
|
||||
}
|
||||
|
||||
fun add(value: T, origin: Any? = null): Boolean {
|
||||
val key = this.key.invoke(value)
|
||||
|
||||
val existing = objects.put(key, value)
|
||||
|
||||
if (existing != null) {
|
||||
val oldOrigin = origins[key]
|
||||
|
||||
if (origin == null && oldOrigin == null)
|
||||
LOGGER.warn("Registry $name already has object with key $key! Overwriting.")
|
||||
else if (origin == null)
|
||||
LOGGER.warn("Registry $name already has object with key $key! Overwriting. (old originated from $oldOrigin)")
|
||||
else
|
||||
LOGGER.warn("Registry $name already has object with key $key! Overwriting. (old originated from $oldOrigin, new originate from $origin).")
|
||||
}
|
||||
|
||||
if (origin == null)
|
||||
origins.remove(key)
|
||||
else
|
||||
origins[key] = origin
|
||||
|
||||
if (this.intKey == null)
|
||||
return existing != null
|
||||
|
||||
val intKey = this.intKey.invoke(value)
|
||||
val intExisting = intObjects.put(intKey, value)
|
||||
|
||||
if (intExisting != null) {
|
||||
val oldOrigin = intOrigins[intKey]
|
||||
|
||||
if (origin == null && oldOrigin == null)
|
||||
LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting.")
|
||||
else if (origin == null)
|
||||
LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting. (old originated from $oldOrigin)")
|
||||
else
|
||||
LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting. (old originated from $oldOrigin, new originate from $origin).")
|
||||
}
|
||||
|
||||
return existing != null || intExisting != null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
@ -6,15 +6,16 @@ import com.google.gson.*
|
||||
import com.google.gson.internal.bind.TypeAdapters
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.api.ISBFileLocator
|
||||
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
||||
import ru.dbotthepony.kstarbound.api.NonExistingFile
|
||||
import ru.dbotthepony.kstarbound.api.PhysicalFile
|
||||
import ru.dbotthepony.kstarbound.api.explore
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.defs.item.BackArmorItemPrototype
|
||||
@ -27,17 +28,31 @@ import ru.dbotthepony.kstarbound.defs.item.IArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.LegsArmorItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.LeveledStatusEffect
|
||||
import ru.dbotthepony.kstarbound.defs.item.LiquidItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.MaterialItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototypeLayer
|
||||
import ru.dbotthepony.kstarbound.defs.particle.ParticleDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList
|
||||
import ru.dbotthepony.kstarbound.defs.player.PlayerDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyColoring
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyColoringManifold
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkySatellite
|
||||
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
||||
import ru.dbotthepony.kstarbound.io.*
|
||||
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.AABBiTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector4dTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.FactoryAdapter
|
||||
@ -46,6 +61,7 @@ import ru.dbotthepony.kstarbound.io.json.factory.ArrayListAdapterFactory
|
||||
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
|
||||
import ru.dbotthepony.kstarbound.math.*
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||
import ru.dbotthepony.kstarbound.util.WriteOnce
|
||||
import java.io.*
|
||||
import java.text.DateFormat
|
||||
@ -57,60 +73,46 @@ import java.util.function.Supplier
|
||||
import java.util.stream.Collector
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
const val METRES_IN_STARBOUND_UNIT = 0.5
|
||||
const val METRES_IN_STARBOUND_UNITf = 0.5f
|
||||
class Starbound : ISBFileLocator {
|
||||
private val logger = LogManager.getLogger()
|
||||
|
||||
const val PIXELS_IN_STARBOUND_UNIT = 8.0
|
||||
const val PIXELS_IN_STARBOUND_UNITf = 8.0f
|
||||
val stringInterner: Interner<String> = Interners.newWeakInterner()
|
||||
val pathStack = AssetPathStack(stringInterner)
|
||||
|
||||
// class TileDefLoadingException(message: String, cause: Throwable? = null) : IllegalStateException(message, cause)
|
||||
// class ProjectileDefLoadingException(message: String, cause: Throwable? = null) : IllegalStateException(message, cause)
|
||||
private val _tiles = ObjectRegistry("tiles", TileDefinition::materialName, TileDefinition::materialId)
|
||||
val tiles = _tiles.view
|
||||
val tilesByID = _tiles.intView
|
||||
|
||||
fun String.sbIntern(): String = Starbound.STRING_INTERNER.intern(this)
|
||||
private val _tileModifiers = ObjectRegistry("tile modifiers", MaterialModifier::modName, MaterialModifier::modId)
|
||||
val tileModifiers = _tileModifiers.view
|
||||
val tileModifiersByID = _tileModifiers.intView
|
||||
|
||||
object Starbound {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
private val _liquid = ObjectRegistry("liquid", LiquidDefinition::name, LiquidDefinition::liquidId)
|
||||
val liquid = _liquid.view
|
||||
val liquidByID = _liquid.intView
|
||||
|
||||
val STRING_INTERNER: Interner<String> = Interners.newWeakInterner()
|
||||
val pathStack = AssetPathStack(STRING_INTERNER)
|
||||
private val _species = ObjectRegistry("species", Species::kind)
|
||||
val species = _species.view
|
||||
|
||||
fun assetFolder(input: String): String {
|
||||
return pathStack.remap(input)
|
||||
}
|
||||
private val _statusEffects = ObjectRegistry("status effects", StatusEffectDefinition::name)
|
||||
val statusEffects = _statusEffects.view
|
||||
|
||||
private val tiles = Object2ObjectOpenHashMap<String, TileDefinition>()
|
||||
private val tilesByMaterialID = Int2ObjectOpenHashMap<TileDefinition>()
|
||||
private val _particles = ObjectRegistry("particles", ParticleDefinition::kind)
|
||||
val particles = _particles.view
|
||||
|
||||
private val tileModifiers = Object2ObjectOpenHashMap<String, MaterialModifier>()
|
||||
private val tileModifiersByID = Int2ObjectOpenHashMap<MaterialModifier>()
|
||||
private val _items = ObjectRegistry("items", IItemDefinition::itemName)
|
||||
val items = _items.view
|
||||
|
||||
private val liquid = Object2ObjectOpenHashMap<String, LiquidDefinition>()
|
||||
private val liquidByID = Int2ObjectOpenHashMap<LiquidDefinition>()
|
||||
val spriteRegistry: SpriteReference.Adapter
|
||||
|
||||
private val projectiles = Object2ObjectOpenHashMap<String, ConfiguredProjectile>()
|
||||
private val parallax = Object2ObjectOpenHashMap<String, ParallaxPrototype>()
|
||||
private val functions = Object2ObjectOpenHashMap<String, JsonFunction>()
|
||||
private val species = Object2ObjectOpenHashMap<String, Species>()
|
||||
private val _statusEffects = Object2ObjectOpenHashMap<String, StatusEffectDefinition>()
|
||||
private val _particles = Object2ObjectOpenHashMap<String, ParticleDefinition>()
|
||||
val gson: Gson = with(GsonBuilder()) {
|
||||
serializeNulls()
|
||||
setDateFormat(DateFormat.LONG)
|
||||
setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
|
||||
setPrettyPrinting()
|
||||
|
||||
val particles: Map<String, ParticleDefinition> = Collections.unmodifiableMap(_particles)
|
||||
val statusEffects: Map<String, StatusEffectDefinition> = Collections.unmodifiableMap(_statusEffects)
|
||||
|
||||
private val items = Object2ObjectOpenHashMap<String, IItemDefinition>()
|
||||
|
||||
val LIQUID: Map<String, LiquidDefinition> = Collections.unmodifiableMap(liquid)
|
||||
val LIQUID_BY_ID: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
|
||||
val TILE_MODIFIER: Map<String, MaterialModifier> = Collections.unmodifiableMap(tileModifiers)
|
||||
val TILE_MODIFIER_BY_ID: Map<Int, MaterialModifier> = Collections.unmodifiableMap(tileModifiersByID)
|
||||
val TILE: Map<String, TileDefinition> = Collections.unmodifiableMap(tiles)
|
||||
val TILE_BY_ID: Map<Int, TileDefinition> = Collections.unmodifiableMap(tilesByMaterialID)
|
||||
val PROJECTILE: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
||||
val PARALLAX: Map<String, ParallaxPrototype> = Collections.unmodifiableMap(parallax)
|
||||
val FUNCTION: Map<String, JsonFunction> = Collections.unmodifiableMap(functions)
|
||||
val ITEM: Map<String, IItemDefinition> = Collections.unmodifiableMap(items)
|
||||
|
||||
val STRING_ADAPTER: TypeAdapter<String> = object : TypeAdapter<String>() {
|
||||
// чтоб строки всегда intern'ились
|
||||
registerTypeAdapter(object : TypeAdapter<String>() {
|
||||
override fun write(out: JsonWriter, value: String?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
@ -119,84 +121,97 @@ object Starbound {
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): String? {
|
||||
return STRING_INTERNER.intern(TypeAdapters.STRING.read(`in`) ?: return null)
|
||||
return stringInterner.intern(TypeAdapters.STRING.read(`in`) ?: return null)
|
||||
}
|
||||
}
|
||||
|
||||
val GSON: Gson = GsonBuilder()
|
||||
.enableComplexMapKeySerialization()
|
||||
.serializeNulls()
|
||||
.setDateFormat(DateFormat.LONG)
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
|
||||
.setPrettyPrinting()
|
||||
|
||||
// чтоб строки всегда intern'ились
|
||||
.registerTypeAdapter(STRING_ADAPTER)
|
||||
|
||||
// Обработчик @JsonImplementation
|
||||
.registerTypeAdapterFactory(JsonImplementationTypeFactory)
|
||||
|
||||
// ImmutableList, ImmutableSet, ImmutableMap
|
||||
.registerTypeAdapterFactory(ImmutableCollectionAdapterFactory)
|
||||
|
||||
// ArrayList
|
||||
.registerTypeAdapterFactory(ArrayListAdapterFactory)
|
||||
|
||||
// все enum'ы без особых настроек
|
||||
.registerTypeAdapterFactory(EnumAdapter.Companion)
|
||||
|
||||
// автоматическое создание BuilderAdapter по @аннотациям
|
||||
.registerTypeAdapterFactory(BuilderAdapter.Companion)
|
||||
|
||||
// автоматическое создание FactoryAdapter по @аннотациям
|
||||
.registerTypeAdapterFactory(FactoryAdapter.Companion)
|
||||
|
||||
.registerTypeAdapterFactory(EitherTypeAdapter)
|
||||
|
||||
.also(::addStarboundJsonAdapters)
|
||||
|
||||
.registerTypeAdapterFactory(IItemDefinition.InventoryIcon.Factory(pathStack))
|
||||
.registerTypeAdapter(SpriteReference.Adapter(pathStack))
|
||||
.registerTypeAdapterFactory(IArmorItemDefinition.ArmorFrames.Factory(pathStack))
|
||||
.registerTypeAdapterFactory(DirectAssetReferenceFactory(pathStack))
|
||||
.registerTypeAdapter(ImageReference.Adapter(pathStack))
|
||||
|
||||
.registerTypeAdapterFactory(AssetReferenceFactory(pathStack) {
|
||||
val file = locate(it)
|
||||
|
||||
if (file.exists && file.isFile)
|
||||
file.reader()
|
||||
else
|
||||
null
|
||||
})
|
||||
|
||||
.registerTypeAdapterFactory(RegistryReferenceFactory()
|
||||
.add(tiles::get)
|
||||
.add(tileModifiers::get)
|
||||
.add(liquid::get)
|
||||
.add(projectiles::get)
|
||||
.add(parallax::get)
|
||||
.add(functions::get)
|
||||
.add(items::get)
|
||||
.add(species::get)
|
||||
.add(_statusEffects::get)
|
||||
.add(_particles::get)
|
||||
)
|
||||
// Обработчик @JsonImplementation
|
||||
registerTypeAdapterFactory(JsonImplementationTypeFactory)
|
||||
|
||||
// ImmutableList, ImmutableSet, ImmutableMap
|
||||
registerTypeAdapterFactory(ImmutableCollectionAdapterFactory)
|
||||
|
||||
// ArrayList
|
||||
registerTypeAdapterFactory(ArrayListAdapterFactory)
|
||||
|
||||
// все enum'ы без особых настроек
|
||||
registerTypeAdapterFactory(EnumAdapter.Companion)
|
||||
|
||||
// автоматическое создание BuilderAdapter по @аннотациям
|
||||
registerTypeAdapterFactory(BuilderAdapter.Factory(stringInterner))
|
||||
|
||||
// автоматическое создание FactoryAdapter по @аннотациям
|
||||
registerTypeAdapterFactory(FactoryAdapter.Factory(stringInterner))
|
||||
|
||||
registerTypeAdapterFactory(EitherTypeAdapter)
|
||||
registerTypeAdapterFactory(SBPattern.Companion)
|
||||
|
||||
registerTypeAdapter(ColorReplacements.Companion)
|
||||
registerTypeAdapterFactory(BlueprintLearnList.Companion)
|
||||
|
||||
registerTypeAdapter(ColorTypeAdapter.nullSafe())
|
||||
|
||||
// математические классы
|
||||
registerTypeAdapter(AABBTypeAdapter)
|
||||
registerTypeAdapter(AABBiTypeAdapter)
|
||||
registerTypeAdapter(Vector2dTypeAdapter)
|
||||
registerTypeAdapter(Vector2fTypeAdapter)
|
||||
registerTypeAdapter(Vector2iTypeAdapter)
|
||||
registerTypeAdapter(Vector4iTypeAdapter)
|
||||
registerTypeAdapter(Vector4dTypeAdapter)
|
||||
registerTypeAdapter(PolyTypeAdapter)
|
||||
|
||||
// Параметры неба
|
||||
registerTypeAdapterFactory(SkyParameters.ADAPTER)
|
||||
registerTypeAdapter(SkyColoringManifold.ADAPTER)
|
||||
registerTypeAdapterFactory(SkyColoring.ADAPTER)
|
||||
registerTypeAdapterFactory(SkySatellite.ADAPTER)
|
||||
registerTypeAdapterFactory(SkySatellite.LAYER_ADAPTER)
|
||||
|
||||
// Данные о данжах
|
||||
registerTypeAdapterFactory(DungeonWorldDef.ADAPTER)
|
||||
|
||||
// Параллакс
|
||||
registerTypeAdapterFactory(ParallaxPrototype.ADAPTER)
|
||||
registerTypeAdapterFactory(ParallaxPrototypeLayer.ADAPTER)
|
||||
registerTypeAdapter(ParallaxPrototypeLayer.LAYER_PARALLAX_ADAPTER)
|
||||
|
||||
// Функции
|
||||
registerTypeAdapter(JsonFunction.CONSTRAINT_ADAPTER)
|
||||
registerTypeAdapter(JsonFunction.INTERPOLATION_ADAPTER)
|
||||
registerTypeAdapter(JsonFunction.Companion)
|
||||
|
||||
// Общее
|
||||
registerTypeAdapterFactory(LeveledStatusEffect.ADAPTER)
|
||||
registerTypeAdapter(MaterialReference.Companion)
|
||||
registerTypeAdapterFactory(ThingDescription.Factory(stringInterner))
|
||||
|
||||
registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.NORMAL))
|
||||
|
||||
spriteRegistry = SpriteReference.Adapter(pathStack, this@Starbound::atlasRegistry)
|
||||
registerTypeAdapter(spriteRegistry)
|
||||
registerTypeAdapterFactory(IItemDefinition.InventoryIcon.Factory(pathStack, spriteRegistry))
|
||||
|
||||
registerTypeAdapterFactory(IArmorItemDefinition.ArmorFrames.Factory(pathStack, this@Starbound::atlasRegistry))
|
||||
registerTypeAdapterFactory(DirectAssetReferenceFactory(pathStack))
|
||||
registerTypeAdapter(ImageReference.Adapter(pathStack, this@Starbound::atlasRegistry))
|
||||
|
||||
registerTypeAdapterFactory(AssetReferenceFactory(pathStack, this@Starbound))
|
||||
|
||||
registerTypeAdapterFactory(with(RegistryReferenceFactory()) {
|
||||
add(tiles::get)
|
||||
add(tileModifiers::get)
|
||||
add(liquid::get)
|
||||
add(items::get)
|
||||
add(species::get)
|
||||
add(statusEffects::get)
|
||||
add(particles::get)
|
||||
})
|
||||
|
||||
.create()
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T> getTypeAdapter(type: Class<T>): TypeAdapter<T> {
|
||||
return when (type) {
|
||||
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
||||
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
|
||||
String::class.java -> STRING_ADAPTER as TypeAdapter<T>
|
||||
Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T>
|
||||
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
|
||||
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
|
||||
else -> GSON.getAdapter(type) as TypeAdapter<T>
|
||||
}
|
||||
}
|
||||
val atlasRegistry = AtlasConfiguration.Registry(this, pathStack, gson)
|
||||
|
||||
var initializing = false
|
||||
private set
|
||||
@ -217,7 +232,7 @@ object Starbound {
|
||||
fileSystems.add(pak.root)
|
||||
}
|
||||
|
||||
fun exists(path: String): Boolean {
|
||||
override fun exists(path: String): Boolean {
|
||||
@Suppress("name_shadowing")
|
||||
var path = path
|
||||
|
||||
@ -234,7 +249,7 @@ object Starbound {
|
||||
return false
|
||||
}
|
||||
|
||||
fun locate(path: String): IStarboundFile {
|
||||
override fun locate(path: String): IStarboundFile {
|
||||
@Suppress("name_shadowing")
|
||||
var path = path
|
||||
|
||||
@ -272,12 +287,6 @@ object Starbound {
|
||||
archivePaths.add(pak)
|
||||
}
|
||||
|
||||
fun loadJson(path: String): JsonElement {
|
||||
return JsonParser.parseReader(locate(path).reader())
|
||||
}
|
||||
|
||||
fun readDirect(path: String) = locate(path).readDirect()
|
||||
|
||||
fun getTileDefinition(name: String) = tiles[name]
|
||||
private val initCallbacks = ArrayList<() -> Unit>()
|
||||
|
||||
@ -294,7 +303,7 @@ object Starbound {
|
||||
|
||||
val time = System.currentTimeMillis()
|
||||
callback(false, false, "Loading $name...")
|
||||
LOGGER.info("Loading $name...")
|
||||
logger.info("Loading $name...")
|
||||
|
||||
loader {
|
||||
if (terminateLoading) {
|
||||
@ -305,82 +314,40 @@ object Starbound {
|
||||
}
|
||||
|
||||
callback(false, true, "Loaded $name in ${System.currentTimeMillis() - time}ms")
|
||||
LOGGER.info("Loaded $name in ${System.currentTimeMillis() - time}ms")
|
||||
logger.info("Loaded $name in ${System.currentTimeMillis() - time}ms")
|
||||
}
|
||||
|
||||
private fun <T, K : Any> loadStage(
|
||||
private fun <T> loadStage(
|
||||
callback: (Boolean, Boolean, String) -> Unit,
|
||||
clazz: Class<T>,
|
||||
getKey: (T) -> K,
|
||||
put: (K, T) -> T?,
|
||||
registry: ObjectRegistry<T>,
|
||||
files: List<IStarboundFile>,
|
||||
name: String,
|
||||
) {
|
||||
loadStage(callback, loader = {
|
||||
for (listedFile in files) {
|
||||
try {
|
||||
it("Loading $listedFile")
|
||||
val def = pathStack(listedFile.computeDirectory()) { GSON.fromJson(listedFile.reader(), clazz) }
|
||||
val key = getKey.invoke(def)
|
||||
|
||||
if (put.invoke(key, def) != null) {
|
||||
LOGGER.warn("Already had $name with name $key loaded! Older prototype was overwritten (loading $listedFile)")
|
||||
}
|
||||
val def = pathStack(listedFile.computeDirectory()) { gson.fromJson(listedFile.reader(), clazz) }
|
||||
registry.add(def, listedFile)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Loading $name definition file $listedFile", err)
|
||||
logger.error("Loading ${registry.name} definition file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}, name)
|
||||
}
|
||||
|
||||
private fun <T, K0 : Any, K1 : Any> loadStage(
|
||||
callback: (Boolean, Boolean, String) -> Unit,
|
||||
clazz: Class<T>,
|
||||
getKey0: (T) -> K0,
|
||||
put0: (K0, T) -> T?,
|
||||
getKey1: (T) -> K1,
|
||||
put1: (K1, T) -> T?,
|
||||
files: List<IStarboundFile>,
|
||||
name: String,
|
||||
) {
|
||||
loadStage(callback, loader = {
|
||||
for (listedFile in files) {
|
||||
try {
|
||||
it("Loading $listedFile")
|
||||
val def = pathStack(listedFile.computeDirectory()) { GSON.fromJson(listedFile.reader(), clazz) }
|
||||
val key0 = getKey0.invoke(def)
|
||||
val key1 = getKey1.invoke(def)
|
||||
|
||||
if (put0.invoke(key0, def) != null) {
|
||||
LOGGER.warn("Already had $name with name $key0 loaded! Older prototype was overwritten (loading $listedFile)")
|
||||
}
|
||||
|
||||
if (put1.invoke(key1, def) != null) {
|
||||
LOGGER.warn("Already had $name with ID $key1 loaded! Older prototype was overwritten (loading $listedFile)")
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Loading $name definition file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}, name)
|
||||
}, registry.name)
|
||||
}
|
||||
|
||||
private fun doInitialize(callback: (finished: Boolean, replaceStatus: Boolean, status: String) -> Unit) {
|
||||
var time = System.currentTimeMillis()
|
||||
|
||||
if (archivePaths.isNotEmpty()) {
|
||||
callback(false, false, "Searching for pak archives...".also(LOGGER::info))
|
||||
callback(false, false, "Searching for pak archives...".also(logger::info))
|
||||
|
||||
for (path in archivePaths) {
|
||||
callback(false, false, "Reading index of ${path}...".also(LOGGER::info))
|
||||
callback(false, false, "Reading index of ${path}...".also(logger::info))
|
||||
|
||||
addPak(StarboundPak(path) { _, status ->
|
||||
callback(false, true, "${path.parent}/${path.name}: $status")
|
||||
@ -388,9 +355,9 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
callback(false, false, "Finished reading pak archives in ${System.currentTimeMillis() - time}ms".also(LOGGER::info))
|
||||
callback(false, false, "Finished reading pak archives in ${System.currentTimeMillis() - time}ms".also(logger::info))
|
||||
time = System.currentTimeMillis()
|
||||
callback(false, false, "Building file index...".also(LOGGER::info))
|
||||
callback(false, false, "Building file index...".also(logger::info))
|
||||
|
||||
val ext2files = fileSystems.parallelStream()
|
||||
.flatMap { it.explore() }
|
||||
@ -439,22 +406,19 @@ object Starbound {
|
||||
}
|
||||
})
|
||||
|
||||
callback(false, false, "Finished building file index in ${System.currentTimeMillis() - time}ms".also(LOGGER::info))
|
||||
callback(false, false, "Finished building file index in ${System.currentTimeMillis() - time}ms".also(logger::info))
|
||||
|
||||
loadStage(callback, this::loadFunctions, "functions")
|
||||
//loadStage(callback, this::loadProjectiles, "projectiles")
|
||||
loadStage(callback, this::loadParallax, "parallax definitions")
|
||||
loadStage(callback, this::loadItemDefinitions, "item definitions")
|
||||
|
||||
loadStage(callback, TileDefinition::class.java, TileDefinition::materialName, tiles::put, TileDefinition::materialId, tilesByMaterialID::put, ext2files["material"] ?: listOf(), "materials")
|
||||
loadStage(callback, MaterialModifier::class.java, MaterialModifier::modName, tileModifiers::put, MaterialModifier::modId, tileModifiersByID::put, ext2files["matmod"] ?: listOf(), "material modifier definitions")
|
||||
loadStage(callback, LiquidDefinition::class.java, LiquidDefinition::name, liquid::put, LiquidDefinition::liquidId, liquidByID::put, ext2files["liquid"] ?: listOf(), "liquid definitions")
|
||||
loadStage(callback, StatusEffectDefinition::class.java, StatusEffectDefinition::name, _statusEffects::put, ext2files["statuseffect"] ?: listOf(), "status effects")
|
||||
loadStage(callback, Species::class.java, Species::kind, species::put, ext2files["species"] ?: listOf(), "species")
|
||||
loadStage(callback, ParticleDefinition::class.java, ParticleDefinition::kind, _particles::put, ext2files["particle"] ?: listOf(), "particles")
|
||||
loadStage(callback, TileDefinition::class.java, _tiles, ext2files["material"] ?: listOf())
|
||||
loadStage(callback, MaterialModifier::class.java, _tileModifiers, ext2files["matmod"] ?: listOf())
|
||||
loadStage(callback, LiquidDefinition::class.java, _liquid, ext2files["liquid"] ?: listOf())
|
||||
loadStage(callback, StatusEffectDefinition::class.java, _statusEffects, ext2files["statuseffect"] ?: listOf())
|
||||
loadStage(callback, Species::class.java, _species, ext2files["species"] ?: listOf())
|
||||
loadStage(callback, ParticleDefinition::class.java, _particles, ext2files["particle"] ?: listOf())
|
||||
|
||||
pathStack.block("/") {
|
||||
playerDefinition = GSON.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
||||
playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
||||
}
|
||||
|
||||
initializing = false
|
||||
@ -493,68 +457,6 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadProjectiles(callback: (String) -> Unit) {
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".projectile") }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
val def = pathStack(listedFile.computeDirectory()) { GSON.fromJson(listedFile.reader(), ConfigurableProjectile::class.java).assemble(it) }
|
||||
check(projectiles[def.projectileName] == null) { "Already has projectile with ID ${def.projectileName} loaded!" }
|
||||
projectiles[def.projectileName] = def
|
||||
} catch(err: Throwable) {
|
||||
//throw ProjectileDefLoadingException("Loading projectile file $listedFile", err)
|
||||
LOGGER.error("Loading projectile file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFunctions(callback: (String) -> Unit) {
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".functions") }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
||||
|
||||
for (key in readObject.keySet()) {
|
||||
val def = pathStack(listedFile.computeDirectory()) { GSON.fromJson(readObject[key], JsonFunction::class.java) }
|
||||
functions[key] = def
|
||||
}
|
||||
} catch(err: Throwable) {
|
||||
LOGGER.error("Loading function file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadParallax(callback: (String) -> Unit) {
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".parallax") }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
val def = pathStack(listedFile.computeDirectory()) { GSON.fromJson(listedFile.reader(), ParallaxPrototype::class.java) }
|
||||
parallax[listedFile.name.substringBefore('.')] = def
|
||||
} catch(err: Throwable) {
|
||||
LOGGER.error("Loading parallax file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadItemDefinitions(callback: (String) -> Unit) {
|
||||
val files = linkedMapOf(
|
||||
".item" to ItemPrototype::class.java,
|
||||
@ -573,13 +475,10 @@ object Starbound {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { f -> files.keys.any { f.name.endsWith(it) } }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
val def: ItemPrototype = pathStack(listedFile.computeDirectory()) { GSON.fromJson(listedFile.reader(), files.entries.first { listedFile.name.endsWith(it.key) }.value) }
|
||||
|
||||
if (items.put(def.itemName, def.assemble()) != null) {
|
||||
LOGGER.warn("Already has item with name ${def.itemName} loaded! Older prototype was overwritten (loading $listedFile)")
|
||||
}
|
||||
val def: ItemPrototype = pathStack(listedFile.computeDirectory()) { gson.fromJson(listedFile.reader(), files.entries.first { listedFile.name.endsWith(it.key) }.value) }
|
||||
_items.add(def, listedFile)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Loading item definition file $listedFile", err)
|
||||
logger.error("Loading item definition file $listedFile", err)
|
||||
}
|
||||
|
||||
if (terminateLoading) {
|
||||
|
@ -1,94 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import ru.dbotthepony.kstarbound.defs.ColorReplacements
|
||||
import ru.dbotthepony.kstarbound.defs.DamageType
|
||||
import ru.dbotthepony.kstarbound.defs.JsonFunction
|
||||
import ru.dbotthepony.kstarbound.defs.MaterialReference
|
||||
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||
import ru.dbotthepony.kstarbound.defs.ThingDescription
|
||||
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.defs.item.IArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.LeveledStatusEffect
|
||||
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototypeLayer
|
||||
import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ActionActions
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ActionConfig
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ActionLoop
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ActionProjectile
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ActionSound
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyColoring
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyColoringManifold
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkySatellite
|
||||
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
||||
import ru.dbotthepony.kstarbound.io.ColorTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.AABBiTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector4dTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.math.PolyTypeAdapter
|
||||
|
||||
fun addStarboundJsonAdapters(builder: GsonBuilder) {
|
||||
with(builder) {
|
||||
registerTypeAdapterFactory(SBPattern.Companion)
|
||||
|
||||
registerTypeAdapter(ColorReplacements.Companion)
|
||||
registerTypeAdapterFactory(BlueprintLearnList.Companion)
|
||||
|
||||
registerTypeAdapter(ColorTypeAdapter.nullSafe())
|
||||
|
||||
// математические классы
|
||||
registerTypeAdapter(AABBTypeAdapter)
|
||||
registerTypeAdapter(AABBiTypeAdapter)
|
||||
registerTypeAdapter(Vector2dTypeAdapter)
|
||||
registerTypeAdapter(Vector2fTypeAdapter)
|
||||
registerTypeAdapter(Vector2iTypeAdapter)
|
||||
registerTypeAdapter(Vector4iTypeAdapter)
|
||||
registerTypeAdapter(Vector4dTypeAdapter)
|
||||
registerTypeAdapter(PolyTypeAdapter)
|
||||
|
||||
// Снаряды
|
||||
registerTypeAdapterFactory(ActionConfig.ADAPTER)
|
||||
registerTypeAdapterFactory(ActionProjectile.ADAPTER)
|
||||
registerTypeAdapterFactory(ActionSound.ADAPTER)
|
||||
registerTypeAdapterFactory(ActionLoop.ADAPTER)
|
||||
registerTypeAdapterFactory(ActionActions.ADAPTER)
|
||||
|
||||
// Параметры неба
|
||||
registerTypeAdapterFactory(SkyParameters.ADAPTER)
|
||||
registerTypeAdapter(SkyColoringManifold.ADAPTER)
|
||||
registerTypeAdapterFactory(SkyColoring.ADAPTER)
|
||||
registerTypeAdapterFactory(SkySatellite.ADAPTER)
|
||||
registerTypeAdapterFactory(SkySatellite.LAYER_ADAPTER)
|
||||
|
||||
// Данные о данжах
|
||||
registerTypeAdapterFactory(DungeonWorldDef.ADAPTER)
|
||||
|
||||
// Параллакс
|
||||
registerTypeAdapterFactory(ParallaxPrototype.ADAPTER)
|
||||
registerTypeAdapterFactory(ParallaxPrototypeLayer.ADAPTER)
|
||||
registerTypeAdapter(ParallaxPrototypeLayer.LAYER_PARALLAX_ADAPTER)
|
||||
|
||||
// Функции
|
||||
registerTypeAdapter(JsonFunction.CONSTRAINT_ADAPTER)
|
||||
registerTypeAdapter(JsonFunction.INTERPOLATION_ADAPTER)
|
||||
registerTypeAdapter(JsonFunction.Companion)
|
||||
|
||||
// Общее
|
||||
registerTypeAdapterFactory(AtlasConfiguration.Companion)
|
||||
|
||||
registerTypeAdapterFactory(LeveledStatusEffect.ADAPTER)
|
||||
registerTypeAdapter(MaterialReference.Companion)
|
||||
registerTypeAdapter(ThingDescription.Companion)
|
||||
|
||||
registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.NORMAL))
|
||||
}
|
||||
}
|
@ -10,7 +10,42 @@ import java.io.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.stream.Stream
|
||||
|
||||
interface IStarboundFile {
|
||||
fun interface ISBFileLocator {
|
||||
fun locate(path: String): IStarboundFile
|
||||
fun exists(path: String): Boolean = locate(path).exists
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun read(path: String) = locate(path).read()
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun readDirect(path: String) = locate(path).readDirect()
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun open(path: String) = locate(path).open()
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun reader(path: String) = locate(path).reader()
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun readJson(path: String) = locate(path).readJson()
|
||||
}
|
||||
|
||||
interface IStarboundFile : ISBFileLocator {
|
||||
val exists: Boolean
|
||||
val isDirectory: Boolean
|
||||
|
||||
@ -60,7 +95,7 @@ interface IStarboundFile {
|
||||
return path
|
||||
}
|
||||
|
||||
fun locate(path: String): IStarboundFile {
|
||||
override fun locate(path: String): IStarboundFile {
|
||||
@Suppress("name_shadowing")
|
||||
val path = path.trim()
|
||||
|
||||
|
@ -58,13 +58,13 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
val material = tile.material
|
||||
|
||||
if (material != null) {
|
||||
state.tileRenderers.getTileRenderer(material.materialName).tesselate(tile, view, layers, pos, background = isBackground)
|
||||
world.client.tileRenderers.getTileRenderer(material.materialName).tesselate(tile, view, layers, pos, background = isBackground)
|
||||
}
|
||||
|
||||
val modifier = tile.modifier
|
||||
|
||||
if (modifier != null) {
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
|
||||
world.client.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,11 +75,11 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
val modifier = tile.modifier
|
||||
|
||||
if (material != null) {
|
||||
state.tileRenderers.getTileRenderer(material.materialName)
|
||||
world.client.tileRenderers.getTileRenderer(material.materialName)
|
||||
}
|
||||
|
||||
if (modifier != null) {
|
||||
state.tileRenderers.getModifierRenderer(modifier.modName)
|
||||
world.client.tileRenderers.getModifierRenderer(modifier.modName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import ru.dbotthepony.kstarbound.client.input.UserInput
|
||||
import ru.dbotthepony.kstarbound.client.render.Camera
|
||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
@ -31,7 +32,7 @@ import java.util.concurrent.locks.LockSupport
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class StarboundClient : AutoCloseable {
|
||||
class StarboundClient(val starbound: Starbound) : AutoCloseable {
|
||||
val window: Long
|
||||
val camera = Camera(this)
|
||||
val input = UserInput()
|
||||
@ -221,7 +222,8 @@ class StarboundClient : AutoCloseable {
|
||||
putDebugLog("Initialized GLFW window")
|
||||
}
|
||||
|
||||
val gl = GLStateTracker()
|
||||
val gl = GLStateTracker(starbound)
|
||||
val tileRenderers = TileRenderers(this)
|
||||
val lightRenderer = GPULightRenderer(gl)
|
||||
|
||||
init {
|
||||
@ -359,7 +361,7 @@ class StarboundClient : AutoCloseable {
|
||||
|
||||
val measure = GLFW.glfwGetTime()
|
||||
|
||||
if (frameRenderTime != 0.0 && Starbound.initialized)
|
||||
if (frameRenderTime != 0.0 && starbound.initialized)
|
||||
world?.think(frameRenderTime)
|
||||
|
||||
gl.clearColor = Color.SLATE_GREY
|
||||
|
@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.opengl.GL
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.api.ISBFileLocator
|
||||
import ru.dbotthepony.kstarbound.client.freetype.FreeType
|
||||
import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLPrograms
|
||||
@ -199,7 +200,7 @@ interface GLStreamBuilderList {
|
||||
}
|
||||
|
||||
@Suppress("PropertyName", "unused")
|
||||
class GLStateTracker {
|
||||
class GLStateTracker(val locator: ISBFileLocator) {
|
||||
private fun isMe(state: GLStateTracker?) {
|
||||
if (state != null && state != this) {
|
||||
throw InvalidArgumentException("Provided object does not belong to $this state tracker (belongs to $state)")
|
||||
@ -419,7 +420,6 @@ class GLStateTracker {
|
||||
}
|
||||
|
||||
val thread: Thread = Thread.currentThread()
|
||||
val tileRenderers = TileRenderers(this)
|
||||
|
||||
fun ensureSameThread() {
|
||||
if (thread !== Thread.currentThread()) {
|
||||
@ -445,21 +445,21 @@ class GLStateTracker {
|
||||
|
||||
fun loadNamedTexture(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!Starbound.exists(path)) {
|
||||
if (!locator.exists(path)) {
|
||||
throw FileNotFoundException("Unable to locate $path")
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadNamedTexture(path: String): GLTexture2D {
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!Starbound.exists(path)) {
|
||||
if (!locator.exists(path)) {
|
||||
throw FileNotFoundException("Unable to locate $path")
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips()
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path)).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,32 +469,32 @@ class GLStateTracker {
|
||||
fun loadNamedTextureSafe(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||
if (!loadedEmptyTexture) {
|
||||
loadedEmptyTexture = true
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), memoryFormat, fileFormat).generateMips()
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(locator.readDirect(missingTexturePath), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!Starbound.exists(path)) {
|
||||
if (!locator.exists(path)) {
|
||||
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||
return@computeIfAbsent named2DTextures[missingTexturePath]!!
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadNamedTextureSafe(path: String): GLTexture2D {
|
||||
if (!loadedEmptyTexture) {
|
||||
loadedEmptyTexture = true
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips()
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(locator.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips()
|
||||
}
|
||||
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!Starbound.exists(path)) {
|
||||
if (!locator.exists(path)) {
|
||||
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||
return@computeIfAbsent named2DTextures[missingTexturePath]!!
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips().also {
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path)).generateMips().also {
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.gl.*
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.*
|
||||
@ -62,7 +62,8 @@ class TileLayerList {
|
||||
*
|
||||
* Создаётся единожды как потомок [GLStateTracker]
|
||||
*/
|
||||
class TileRenderers(val state: GLStateTracker) {
|
||||
class TileRenderers(val client: StarboundClient) {
|
||||
val state get() = client.gl
|
||||
private val foregroundTilePrograms = HashMap<GLTexture2D, ForegroundTileProgram>()
|
||||
private val backgroundTilePrograms = HashMap<GLTexture2D, BackgroundTileProgram>()
|
||||
private val tileRenderersCache = HashMap<String, TileRenderer>()
|
||||
@ -70,15 +71,15 @@ class TileRenderers(val state: GLStateTracker) {
|
||||
|
||||
fun getTileRenderer(defName: String): TileRenderer {
|
||||
return tileRenderersCache.computeIfAbsent(defName) {
|
||||
val def = Starbound.TILE[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(state, def!!)
|
||||
val def = client.starbound.tiles[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(this, def!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun getModifierRenderer(defName: String): TileRenderer {
|
||||
return modifierRenderersCache.computeIfAbsent(defName) {
|
||||
val def = Starbound.TILE_MODIFIER[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(state, def!!)
|
||||
val def = client.starbound.tileModifiers[defName] // TODO: Пустой рендерер
|
||||
return@computeIfAbsent TileRenderer(this, def!!)
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +183,8 @@ private class ModifierEqualityTester(val definition: MaterialModifier) : Equalit
|
||||
}
|
||||
}
|
||||
|
||||
class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
val state get() = renderers.state
|
||||
val texture = state.loadNamedTexture(def.renderParameters.texture.image).also {
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
@ -193,8 +195,8 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
val bakedProgramState = state.tileRenderers.foreground(texture)
|
||||
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
||||
val bakedProgramState = renderers.foreground(texture)
|
||||
val bakedBackgroundProgramState = renderers.background(texture)
|
||||
// private var notifiedDepth = false
|
||||
|
||||
private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileChunk, builder: AbstractVertexBuilder<*>, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
||||
@ -252,9 +254,9 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
for (renderPiece in matchPiece.pieces) {
|
||||
if (renderPiece.piece.texture != null) {
|
||||
val program = if (background) {
|
||||
state.tileRenderers.background(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
renderers.background(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
} else {
|
||||
state.tileRenderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
renderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
}
|
||||
|
||||
tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier)
|
||||
|
@ -3,11 +3,7 @@ package ru.dbotthepony.kstarbound.client.render.entity
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.render.entity.ItemRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.entity.ProjectileRenderer
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import java.io.Closeable
|
||||
@ -50,14 +46,17 @@ open class EntityRenderer(val state: GLStateTracker, val entity: Entity, open va
|
||||
check(renderers.put(clazz, renderer as (state: GLStateTracker, entity: Entity, chunk: ClientChunk?) -> EntityRenderer) == null) { "Already has renderer for ${clazz.canonicalName}!" }
|
||||
}
|
||||
|
||||
inline fun <reified T : Entity> registerRenderer(noinline renderer: (state: GLStateTracker, entity: T, chunk: ClientChunk?) -> EntityRenderer) {
|
||||
registerRenderer(T::class.java, renderer)
|
||||
}
|
||||
|
||||
fun getRender(state: GLStateTracker, entity: Entity, chunk: ClientChunk? = null): EntityRenderer {
|
||||
val factory = renderers[entity::class.java] ?: return EntityRenderer(state, entity, chunk)
|
||||
return factory.invoke(state, entity, chunk)
|
||||
}
|
||||
|
||||
init {
|
||||
registerRenderer(Projectile::class.java, ::ProjectileRenderer)
|
||||
registerRenderer(ItemEntity::class.java, ::ItemRenderer)
|
||||
registerRenderer(::ItemRenderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.client.render.entity
|
||||
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
|
||||
import ru.dbotthepony.kstarbound.client.render.FrameAnimator
|
||||
import ru.dbotthepony.kstarbound.client.render.SpriteAnimator
|
||||
import ru.dbotthepony.kstarbound.client.render.makeSpriteAnimator
|
||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
|
||||
open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
||||
private val def = entity.def
|
||||
private val animator = def.image.makeSpriteAnimator(state, def.animationCycle, def.animationLoops)
|
||||
|
||||
override fun render(stack: Matrix4fStack) {
|
||||
state.shaderVertexTexture.use()
|
||||
state.shaderVertexTexture.transform.set(stack.last)
|
||||
state.activeTexture = 0
|
||||
state.shaderVertexTexture["_texture"] = 0
|
||||
|
||||
animator.advance()
|
||||
val sprite = animator.sprite
|
||||
sprite.texture.bind()
|
||||
|
||||
val builder = state.flat2DTexturedQuads.small
|
||||
|
||||
builder.begin()
|
||||
|
||||
val width = (sprite.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||
val height = (sprite.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||
|
||||
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, sprite.transformer)
|
||||
|
||||
builder.upload()
|
||||
builder.draw()
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||
import ru.dbotthepony.kstarbound.api.ISBFileLocator
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import java.io.Reader
|
||||
import java.lang.reflect.ParameterizedType
|
||||
@ -19,7 +20,7 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
*
|
||||
* Созданный [TypeAdapter] имеет встроенный кеш.
|
||||
*/
|
||||
class AssetReferenceFactory(val remapper: AssetPathStack, val reader: (String) -> Reader?) : TypeAdapterFactory {
|
||||
class AssetReferenceFactory(val remapper: AssetPathStack, val locator: ISBFileLocator) : TypeAdapterFactory {
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == AssetReference::class.java) {
|
||||
val param = type.type as? ParameterizedType ?: return null
|
||||
@ -51,13 +52,15 @@ class AssetReferenceFactory(val remapper: AssetPathStack, val reader: (String) -
|
||||
if (fullPath in missing)
|
||||
return null
|
||||
|
||||
val reader = reader.invoke(fullPath)
|
||||
val file = locator.locate(fullPath)
|
||||
|
||||
if (reader == null) {
|
||||
if (!file.exists) {
|
||||
missing.add(fullPath)
|
||||
return AssetReference(path, fullPath, null)
|
||||
}
|
||||
|
||||
val reader = file.reader()
|
||||
|
||||
val value = remapper(fullPath) {
|
||||
adapter.read(JsonReader(reader).also {
|
||||
it.isLenient = true
|
||||
|
@ -1,13 +1,15 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.Interner
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonImplementation
|
||||
import ru.dbotthepony.kstarbound.sbIntern
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
|
||||
@JsonImplementation(ThingDescription::class)
|
||||
interface IThingWithDescription {
|
||||
@ -77,12 +79,40 @@ data class ThingDescription(
|
||||
override val racialDescription: Map<String, String>,
|
||||
override val racialShortDescription: Map<String, String>,
|
||||
) : IThingWithDescription {
|
||||
companion object : TypeAdapter<ThingDescription>() {
|
||||
override fun write(out: JsonWriter, value: ThingDescription) {
|
||||
TODO("Not yet implemented")
|
||||
class Factory(val interner: Interner<String> = Interner { it }) : TypeAdapterFactory {
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == ThingDescription::class.java) {
|
||||
return object : TypeAdapter<ThingDescription>() {
|
||||
override fun write(out: JsonWriter, value: ThingDescription?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
else {
|
||||
out.beginObject()
|
||||
|
||||
out.name("shortdescription")
|
||||
out.value(value.shortdescription)
|
||||
|
||||
out.name("description")
|
||||
out.value(value.description)
|
||||
|
||||
for ((k, v) in value.racialDescription) {
|
||||
out.name("${k}Description")
|
||||
out.value(v)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): ThingDescription {
|
||||
for ((k, v) in value.racialShortDescription) {
|
||||
out.name("${k}Shortdescription")
|
||||
out.value(v)
|
||||
}
|
||||
|
||||
out.endObject()
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): ThingDescription? {
|
||||
if (`in`.peek() == JsonToken.NULL)
|
||||
return null
|
||||
|
||||
`in`.beginObject()
|
||||
|
||||
var shortdescription = "..."
|
||||
@ -96,9 +126,9 @@ data class ThingDescription(
|
||||
"description" -> description = `in`.nextString()
|
||||
else -> {
|
||||
if (name.endsWith("shortdescription") || name.endsWith("shortDescription") || name.endsWith("Shortdescription") || name.endsWith("ShortDescription")) {
|
||||
racialShort.put(name.substring(0, name.length - "shortdescription".length).sbIntern(), `in`.nextString())
|
||||
racialShort.put(interner.intern(name.substring(0, name.length - "shortdescription".length)), interner.intern(`in`.nextString()))
|
||||
} else if (name.endsWith("description") || name.endsWith("Description")) {
|
||||
racial.put(name.substring(0, name.length - "description".length).sbIntern(), `in`.nextString())
|
||||
racial.put(interner.intern(name.substring(0, name.length - "description".length)), interner.intern(`in`.nextString()))
|
||||
} else {
|
||||
`in`.skipValue()
|
||||
}
|
||||
@ -115,5 +145,10 @@ data class ThingDescription(
|
||||
racialShortDescription = racialShort.build()
|
||||
)
|
||||
}
|
||||
} as TypeAdapter<T>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.util.enrollMap
|
||||
import ru.dbotthepony.kstarbound.defs.util.flattenMap
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.INativeJsonHolder
|
||||
|
||||
/**
|
||||
* Базовый класс описания прототипа игрового объекта
|
||||
*
|
||||
* Должен иметь все (или больше) поля объекта, который он будет создавать
|
||||
*
|
||||
* Поля должны иметь базовые ограничения (т.е. ограничения, которые применимы для всех конфигураций прототипа).
|
||||
* Если границы поля зависят от других полей, то проверка такого поля должна осуществляться уже при самой
|
||||
* сборке прототипа.
|
||||
*/
|
||||
abstract class RawPrototype<RAW : RawPrototype<RAW, ASSEMBLED>, ASSEMBLED : AssembledPrototype<ASSEMBLED, RAW>> :
|
||||
INativeJsonHolder {
|
||||
val json = Object2ObjectArrayMap<String, Any>()
|
||||
fun enroll() = enrollMap(json, Starbound.STRING_INTERNER::intern)
|
||||
abstract fun assemble(directory: String = ""): ASSEMBLED
|
||||
|
||||
override fun acceptJson(json: MutableMap<String, Any>) {
|
||||
this.json.clear()
|
||||
this.json.putAll(json)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовый класс описанного прототипа игрового объекта
|
||||
*
|
||||
* Должен иметь все поля объекта, которые будут использоваться движком напрямую
|
||||
*
|
||||
* Создается соответствующим [RawPrototype], который проверил уже все поля
|
||||
* на их правильность.
|
||||
*/
|
||||
abstract class AssembledPrototype<ASSEMBLED : AssembledPrototype<ASSEMBLED, RAW>, RAW : RawPrototype<RAW, ASSEMBLED>>(
|
||||
val json: ImmutableMap<String, Any>
|
||||
) {
|
||||
open fun getParameter(key: String): Any? = json[key]
|
||||
fun unroll() = flattenMap(json)
|
||||
abstract fun disassemble(): RAW
|
||||
}
|
@ -13,11 +13,16 @@ import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.internal.bind.TypeAdapters
|
||||
import com.google.gson.reflect.TypeToken
|
||||
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.api.ISBFileLocator
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||
import ru.dbotthepony.kstarbound.io.json.stream
|
||||
import ru.dbotthepony.kstarbound.io.json.transform
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import ru.dbotthepony.kstarbound.util.WriteOnce
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector4i
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -155,14 +160,16 @@ class AtlasConfiguration private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
companion object {
|
||||
val EMPTY: AtlasConfiguration
|
||||
|
||||
init {
|
||||
val sprite = Sprite("root", Vector4i(0, 0, 0, 0))
|
||||
EMPTY = AtlasConfiguration("null", ImmutableMap.of("root", sprite, "default", sprite, "0", sprite), ImmutableList.of(sprite))
|
||||
}
|
||||
}
|
||||
|
||||
class Registry(val locator: ISBFileLocator, val remapper: AssetPathStack, val gson: Gson) {
|
||||
private val cache = ConcurrentHashMap<String, AtlasConfiguration>()
|
||||
|
||||
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
|
||||
@ -193,8 +200,8 @@ class AtlasConfiguration private constructor(
|
||||
val sprites = LinkedHashMap<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)
|
||||
val size = gson.fromJson(frameGrid["size"] ?: throw JsonSyntaxException("Missing frameGrid.size"), Vector2i::class.java)
|
||||
val dimensions = 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}" }
|
||||
@ -233,7 +240,7 @@ class AtlasConfiguration private constructor(
|
||||
}
|
||||
|
||||
for ((spriteName, coords) in frameList.entrySet()) {
|
||||
sprites[spriteName] = Sprite(spriteName, Starbound.GSON.fromJson(coords, Vector4i::class.java))
|
||||
sprites[spriteName] = Sprite(spriteName, gson.fromJson(coords, Vector4i::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +263,7 @@ class AtlasConfiguration private constructor(
|
||||
|
||||
while (current != "/" && current != "") {
|
||||
val get = cache.computeIfAbsent("$current/$name") {
|
||||
val file = Starbound.locate("$it.frames")
|
||||
val file = locator.locate("$it.frames")
|
||||
|
||||
if (file.exists) {
|
||||
try {
|
||||
@ -306,13 +313,5 @@ class AtlasConfiguration private constructor(
|
||||
|
||||
return EMPTY
|
||||
}
|
||||
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == AtlasConfiguration::class.java) {
|
||||
return gson.getAdapter(String::class.java).transform(read = read@{ get(it ?: return@read it as AtlasConfiguration?) }, write = write@{ it?.name }) as TypeAdapter<T>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,7 @@ data class ImageReference(
|
||||
val image: String,
|
||||
val config: AtlasConfiguration,
|
||||
) {
|
||||
/**
|
||||
* Вызывает [AtlasConfiguration.Companion.get] автоматически
|
||||
*
|
||||
* @see ImageReference
|
||||
*/
|
||||
constructor(image: String) : this(image, AtlasConfiguration.get(image))
|
||||
|
||||
class Adapter(val remapper: AssetPathStack) : TypeAdapter<ImageReference>() {
|
||||
class Adapter(private val remapper: AssetPathStack, private val atlasRegistry: () -> AtlasConfiguration.Registry) : TypeAdapter<ImageReference>() {
|
||||
override fun write(out: JsonWriter, value: ImageReference?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
@ -42,7 +35,7 @@ data class ImageReference(
|
||||
if (image.contains(':'))
|
||||
throw JsonSyntaxException("Expected atlas/image reference, but got sprite reference: $image")
|
||||
|
||||
return ImageReference(image, AtlasConfiguration.get(image))
|
||||
return ImageReference(image, atlasRegistry.invoke().get(image))
|
||||
}
|
||||
|
||||
throw JsonSyntaxException("Expected atlas/image reference, but got: ${`in`.peek()} near ${`in`.path}")
|
||||
|
@ -22,7 +22,7 @@ data class SpriteReference(
|
||||
return atlas[resolved] ?: atlas.any()
|
||||
}
|
||||
|
||||
class Adapter(val remapper: AssetPathStack) : TypeAdapter<SpriteReference>() {
|
||||
class Adapter(private val remapper: AssetPathStack, private val atlasRegistry: () -> AtlasConfiguration.Registry) : TypeAdapter<SpriteReference>() {
|
||||
override fun write(out: JsonWriter, value: SpriteReference?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
@ -40,11 +40,9 @@ data class SpriteReference(
|
||||
|
||||
return parse(remapper.remap(value))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(input: String): SpriteReference {
|
||||
val grid = AtlasConfiguration.get(input.substringBefore(':'))
|
||||
val grid = atlasRegistry.invoke().get(input.substringBefore(':'))
|
||||
|
||||
return when (input.count { it == ':' }) {
|
||||
0 -> SpriteReference(input, grid, SBPattern.raw(grid.any().name))
|
||||
|
@ -46,7 +46,7 @@ interface IArmorItemDefinition : ILeveledItemDefinition, IScriptableItemDefiniti
|
||||
override val backSleeve: ImageReference? = null,
|
||||
override val frontSleeve: ImageReference? = null,
|
||||
) : IArmorFrames {
|
||||
class Factory(private val remapper: AssetPathStack) : TypeAdapterFactory {
|
||||
class Factory(private val remapper: AssetPathStack, private val atlasRegistry: () -> AtlasConfiguration.Registry) : TypeAdapterFactory {
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == ArmorFrames::class.java) {
|
||||
return object : TypeAdapter<ArmorFrames>() {
|
||||
@ -70,7 +70,7 @@ interface IArmorItemDefinition : ILeveledItemDefinition, IScriptableItemDefiniti
|
||||
|
||||
if (`in`.peek() == JsonToken.STRING) {
|
||||
val path = remapper.remap(`in`.nextString())
|
||||
return ArmorFrames(ImageReference(path, AtlasConfiguration.get(path)), null, null)
|
||||
return ArmorFrames(ImageReference(path, atlasRegistry.invoke().get(path)), null, null)
|
||||
}
|
||||
|
||||
return adapter.read(`in`)
|
||||
|
@ -53,7 +53,7 @@ interface IItemDefinition : IThingWithDescription {
|
||||
data class InventoryIcon(
|
||||
override val image: SpriteReference
|
||||
) : IInventoryIcon {
|
||||
class Factory(val remapper: AssetPathStack) : TypeAdapterFactory {
|
||||
class Factory(val remapper: AssetPathStack, val spriteRegistry: SpriteReference.Adapter) : TypeAdapterFactory {
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == InventoryIcon::class.java) {
|
||||
return object : TypeAdapter<InventoryIcon>() {
|
||||
@ -71,7 +71,7 @@ interface IItemDefinition : IThingWithDescription {
|
||||
return null
|
||||
|
||||
if (`in`.peek() == JsonToken.STRING) {
|
||||
return InventoryIcon(SpriteReference.parse(remapper.remap(`in`.nextString())))
|
||||
return InventoryIcon(spriteRegistry.parse(remapper.remap(`in`.nextString())))
|
||||
}
|
||||
|
||||
return adapter.read(`in`)
|
||||
|
@ -1,220 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs.projectile
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonBuilder
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@JsonBuilder
|
||||
class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredProjectile>() {
|
||||
var projectileName by Delegates.notNull<String>()
|
||||
var physics: ProjectilePhysics = ProjectilePhysics.DEFAULT
|
||||
var damageKindImage: String? = null
|
||||
var damageType = DamageType.NORMAL
|
||||
var damageKind: String? = null
|
||||
|
||||
var pointLight: Boolean = false
|
||||
var animationLoops: Boolean = true
|
||||
var lightColor: Color? = null
|
||||
|
||||
var onlyHitTerrain: Boolean = false
|
||||
var orientationLocked: Boolean = false
|
||||
|
||||
var image: String? = null
|
||||
|
||||
var timeToLive: Double = Double.POSITIVE_INFINITY
|
||||
var animationCycle: Double = Double.POSITIVE_INFINITY
|
||||
var bounces: Int = -1
|
||||
var frameNumber: Int = 1
|
||||
|
||||
var scripts: ArrayList<String> = ArrayList()
|
||||
|
||||
var hydrophobic: Boolean = false
|
||||
|
||||
// we can't have concrete type here, since final class is commanded by `action` property of each entry
|
||||
var actionOnReap: ArrayList<JsonObject>? = null
|
||||
|
||||
var piercing = false
|
||||
|
||||
var speed = 0.0
|
||||
var power = 0.0
|
||||
|
||||
override fun assemble(directory: String): ConfiguredProjectile {
|
||||
val actions = ArrayList<IActionOnReap>()
|
||||
|
||||
if (actionOnReap != null) {
|
||||
for (action in actionOnReap!!) {
|
||||
val configurable = constructAction(action)
|
||||
|
||||
if (configurable != null) {
|
||||
actions.add(configurable.configure(directory))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeToLive.isInfinite() && animationCycle.isFinite() && !animationLoops) {
|
||||
timeToLive = animationCycle * (frameNumber - 1)
|
||||
// LOGGER.warn("{} has no time to live defined, assuming it live as long as its animation plays: {}", projectileName, timeToLive)
|
||||
}
|
||||
|
||||
check(timeToLive >= 0.0) { "Invalid time to live $timeToLive" }
|
||||
|
||||
return ConfiguredProjectile(
|
||||
json = enroll(),
|
||||
projectileName = projectileName,
|
||||
physics = physics,
|
||||
damageKindImage = damageKindImage,
|
||||
damageType = damageType,
|
||||
damageKind = damageKind,
|
||||
pointLight = pointLight,
|
||||
lightColor = lightColor,
|
||||
onlyHitTerrain = onlyHitTerrain,
|
||||
orientationLocked = orientationLocked,
|
||||
image = ImageReference(Starbound.assetFolder(requireNotNull(image) { "image is null" })),
|
||||
timeToLive = timeToLive,
|
||||
animationCycle = animationCycle,
|
||||
bounces = bounces,
|
||||
frameNumber = frameNumber,
|
||||
scripts = scripts.toTypedArray(),
|
||||
actionOnReap = ImmutableList.copyOf(actions),
|
||||
animationLoops = animationLoops,
|
||||
hydrophobic = hydrophobic,
|
||||
piercing = piercing,
|
||||
speed = speed,
|
||||
power = power,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
// Action on Reap
|
||||
/////////////////////////////////
|
||||
|
||||
interface IConfigurableAction {
|
||||
fun configure(directory: String = ""): IActionOnReap
|
||||
}
|
||||
|
||||
private val MISSING_ACTIONS = ObjectArraySet<String>()
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
/**
|
||||
* Определяет тип действия и возвращает прототип действия
|
||||
*/
|
||||
private fun constructAction(input: JsonObject): IConfigurableAction? {
|
||||
return when (val elem = (input["action"] ?: throw IllegalArgumentException("Action has no, well, `action` key to specify whatever is it.")).asString) {
|
||||
"config" -> Starbound.GSON.fromJson(input, ActionConfig::class.java)
|
||||
"projectile" -> Starbound.GSON.fromJson(input, ActionProjectile::class.java)
|
||||
"sound" -> Starbound.GSON.fromJson(input, ActionSound::class.java)
|
||||
"loop" -> Starbound.GSON.fromJson(input, ActionLoop::class.java)
|
||||
"actions" -> Starbound.GSON.fromJson(input, ActionActions::class.java)
|
||||
else -> {
|
||||
if (!MISSING_ACTIONS.contains(elem)) {
|
||||
MISSING_ACTIONS.add(elem)
|
||||
LOGGER.error("No projectile action on reap handler is registered for '{}'!", elem)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет действия из указанного файла
|
||||
*
|
||||
* По смыслу равен include
|
||||
*/
|
||||
class ActionConfig : IConfigurableAction {
|
||||
lateinit var file: String
|
||||
|
||||
override fun configure(directory: String): IActionOnReap {
|
||||
return cache.computeIfAbsent(ensureAbsolutePath(file, directory)) {
|
||||
if (!Starbound.exists(it)) {
|
||||
LOGGER.error("Config $it does not exist")
|
||||
return@computeIfAbsent CActionConfig(file, null)
|
||||
}
|
||||
|
||||
return@computeIfAbsent CActionConfig(file, constructAction(Starbound.loadJson(it) as JsonObject)?.configure())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ActionConfig, ActionConfig::file).ignoreKey("action")
|
||||
|
||||
private val cache = ConcurrentHashMap<String, CActionConfig>()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает новый прожектайл с заданными параметрами
|
||||
*/
|
||||
class ActionProjectile : IConfigurableAction {
|
||||
var type by NotNullVar<String>()
|
||||
var angle = 0.0
|
||||
var inheritDamageFactor = 1.0
|
||||
|
||||
override fun configure(directory: String): IActionOnReap {
|
||||
return CActionProjectile(type, angle, inheritDamageFactor)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ActionProjectile, ActionProjectile::type, ActionProjectile::angle, ActionProjectile::inheritDamageFactor).ignoreKey("action")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проигрывает звук
|
||||
*/
|
||||
class ActionSound : IConfigurableAction {
|
||||
lateinit var options: Array<String>
|
||||
|
||||
override fun configure(directory: String): IActionOnReap {
|
||||
return CActionSound(ImmutableList.copyOf(options))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ActionSound, ActionSound::options).ignoreKey("action")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет указанные действия в [body] на [count] раз
|
||||
*/
|
||||
class ActionLoop : IConfigurableAction {
|
||||
var count by Delegates.notNull<Int>()
|
||||
var body by Delegates.notNull<Array<JsonObject>>()
|
||||
|
||||
override fun configure(directory: String): IActionOnReap {
|
||||
return CActionLoop(count, ImmutableList.copyOf(body.mapNotNull { constructAction(it)?.configure() }))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ActionLoop, ActionLoop::count, ActionLoop::body).ignoreKey("action")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет указанные [list] действия
|
||||
*/
|
||||
class ActionActions : IConfigurableAction {
|
||||
var list by Delegates.notNull<Array<JsonObject>>()
|
||||
|
||||
override fun configure(directory: String): IActionOnReap {
|
||||
return CActionActions(ImmutableList.copyOf(list.mapNotNull { constructAction(it)?.configure() }))
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ActionActions, ActionActions::list).ignoreKey("action")
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs.projectile
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
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.image.ImageReference
|
||||
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
|
||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
|
||||
class ConfiguredProjectile(
|
||||
json: ImmutableMap<String, Any>,
|
||||
val projectileName: String,
|
||||
val physics: ProjectilePhysics,
|
||||
val damageKindImage: String?,
|
||||
val damageType: DamageType,
|
||||
val damageKind: String?,
|
||||
val pointLight: Boolean,
|
||||
val lightColor: Color?,
|
||||
val onlyHitTerrain: Boolean,
|
||||
val orientationLocked: Boolean,
|
||||
val image: ImageReference,
|
||||
val timeToLive: Double,
|
||||
val animationCycle: Double,
|
||||
val bounces: Int,
|
||||
val frameNumber: Int,
|
||||
val scripts: Array<String>,
|
||||
val actionOnReap: List<IActionOnReap>,
|
||||
val animationLoops: Boolean,
|
||||
val hydrophobic: Boolean,
|
||||
val piercing: Boolean,
|
||||
val speed: Double,
|
||||
val power: Double,
|
||||
) : AssembledPrototype<ConfiguredProjectile, ConfigurableProjectile>(json) {
|
||||
override fun disassemble(): ConfigurableProjectile {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ConfiguredProjectile($projectileName)"
|
||||
}
|
||||
}
|
||||
|
||||
interface IActionOnReap {
|
||||
val name: String
|
||||
fun execute(projectile: Projectile)
|
||||
}
|
||||
|
||||
data class CActionConfig(
|
||||
val file: String,
|
||||
val delegate: IActionOnReap?,
|
||||
) : IActionOnReap {
|
||||
override val name: String = "config"
|
||||
|
||||
override fun execute(projectile: Projectile) {
|
||||
delegate?.execute(projectile)
|
||||
}
|
||||
}
|
||||
|
||||
data class CActionProjectile(
|
||||
val type: String,
|
||||
val angle: Double,
|
||||
val inheritDamageFactor: Double,
|
||||
) : IActionOnReap {
|
||||
override val name: String = "projectile"
|
||||
|
||||
override fun execute(projectile: Projectile) {
|
||||
val def = Starbound.PROJECTILE[type]
|
||||
|
||||
if (def == null) {
|
||||
LOGGER.error("Tried to create unknown projectile '{}' as result of reap of '{}'!", type, projectile.def.projectileName)
|
||||
return
|
||||
}
|
||||
|
||||
val ent = Projectile(projectile.world, def)
|
||||
ent.position = projectile.position
|
||||
// ent.angle = projectile.angle
|
||||
ent.angle = Math.toRadians(angle)
|
||||
|
||||
if (ent.movement is AbstractProjectileMovementController) {
|
||||
ent.movement.push()
|
||||
}
|
||||
|
||||
ent.spawn()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger(CActionProjectile::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
data class CActionSound(
|
||||
val options: List<String>
|
||||
) : IActionOnReap {
|
||||
override val name: String = "sound"
|
||||
|
||||
override fun execute(projectile: Projectile) {
|
||||
println("Play sound ${options.random()}!")
|
||||
}
|
||||
}
|
||||
|
||||
data class CActionLoop(
|
||||
val count: Int,
|
||||
val body: List<IActionOnReap>
|
||||
) : IActionOnReap {
|
||||
override val name: String = "loop"
|
||||
|
||||
override fun execute(projectile: Projectile) {
|
||||
for (i in 0 until count) {
|
||||
for (action in body) {
|
||||
action.execute(projectile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CActionActions(
|
||||
val list: List<IActionOnReap>
|
||||
) : IActionOnReap {
|
||||
override val name: String = "actions"
|
||||
|
||||
override fun execute(projectile: Projectile) {
|
||||
for (action in list) {
|
||||
action.execute(projectile)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs.projectile
|
||||
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.IStringSerializable
|
||||
|
||||
enum class ProjectilePhysics(vararg aliases: String) : IStringSerializable {
|
||||
GAS,
|
||||
LASER,
|
||||
BOOMERANG,
|
||||
DEFAULT,
|
||||
BULLET,
|
||||
STICKY_BULLET("STICKYBULLET"),
|
||||
ARROW,
|
||||
UNDERWATER_ARROW("UNDERWATERARROW"),
|
||||
UNDERWATER_ARROW_NO_STICKY("UNDERWATERARROWNOSTICKY"),
|
||||
ROCKET,
|
||||
GRAVITY_BULLET("GRAVITYBULLET"),
|
||||
FLAME,
|
||||
ARROW_NO_STICKY("ARROWNOSTICKY"),
|
||||
|
||||
SQUIRT,
|
||||
FLYBUG,
|
||||
ROLLER,
|
||||
BOWLDER,
|
||||
SMOOTH_ROLLING_BOULDER("SMOOTHROLLINGBOULDER"),
|
||||
ROLLING_BOULDER("ROLLINGBOULDER"),
|
||||
|
||||
DRAGON_BONE("DRAGONBONE"),
|
||||
DRAGON_HEAD("DRAGONHEAD"),
|
||||
|
||||
STICKY,
|
||||
BOWLING_BALL("BOWLINGBALL"),
|
||||
PAPER_PLANE("PAPERPLANE"),
|
||||
BOULDER,
|
||||
|
||||
STATUS_POD("STATUSPOD"),
|
||||
|
||||
// ???
|
||||
ILLUSION,
|
||||
ILLUSION_ROCKET("ROCKETILLUSION"),
|
||||
|
||||
// ?????????????
|
||||
FRIENDLY_BUBBLE("FRIENDLYBUBBLE"),
|
||||
|
||||
STICKY_HEAVY_GAS("STICKYHEAVYGAS"),
|
||||
HEAVY_GAS("HEAVYGAS"),
|
||||
BOUNCY_GAS("BOUNCYGAS"),
|
||||
FIREBALL,
|
||||
SLIDER,
|
||||
GOOP,
|
||||
HOVER,
|
||||
|
||||
BONE_THORN("BONETHORN"),
|
||||
|
||||
BIG_BUBBLE("BIGBUBBLE"),
|
||||
FIREWORK_FALL("FIREWORKFALL"),
|
||||
LIGHTNING_BOLT("LIGHTNINGBOLT"),
|
||||
SIMPLE_ARC("SIMPLEARC"),
|
||||
LOW_GRAVITY_ARC("LOWGRAVARC"),
|
||||
|
||||
SPIKE_BALL("SPIKEBALL"),
|
||||
SHRAPNEL,
|
||||
|
||||
// что
|
||||
WEATHER,
|
||||
|
||||
FIRE_SPREAD("FIRESPREAD"),
|
||||
|
||||
GRAPPLE_HOOK("GRAPPLEHOOK"),
|
||||
BALLISTIC_GRAPPLE_HOOK("BALLISTICGRAPPLEHOOK"),
|
||||
|
||||
FLOATY_STICKY_BOMB("FLOATYSTICKYBOMB"),
|
||||
STICKY_BOMB("STICKYBOMB"),
|
||||
BOUNCY,
|
||||
GRAVITY_BOMB("GRAVITYBOMB"),
|
||||
DISC,
|
||||
HEAVY_BOUNCER("HEAVYBOUNCER"),
|
||||
|
||||
WALL_STICKY("WALLSTICKY"),
|
||||
FISHING_LURE_SINKING("FISHINGLURESINKING"),
|
||||
FISHING_LURE("FISHINGLURE"),
|
||||
RAIN("RAIN"),
|
||||
|
||||
PET_BALL("PETBALL"),
|
||||
BOUNCY_BALL("BOUNCYBALL"),
|
||||
BEACH_BALL("BEACHBALL"),
|
||||
NOVELTY_BANANA("NOVELTYBANANA"),
|
||||
|
||||
SPACE_MINE("SPACEMINE"),
|
||||
MECH_BATTERY("MECHBATTERY"),
|
||||
|
||||
GRENADE,
|
||||
GRENADE_LARGE("LARGEGRENADE"),
|
||||
GRENADE_Z_BOMB("GRENADEZBOMB"),
|
||||
GRENADE_STICKY("STICKYGRENADE"),
|
||||
GRENADE_SUPER_GRAVITY("SUPERHIGHGRAVGRENADE"),
|
||||
GRENADE_HIGH_GRAVITY_V("VHIGHGRAVGRENADE"),
|
||||
GRENADE_HIGH_GRAVITY("HIGHGRAVGRENADE"),
|
||||
GRENADE_LOW_BOUNCE("GRENADELOWBOUNCE"),
|
||||
GRENADE_NO_BOUNCE("GRENADENOBOUNCE");
|
||||
|
||||
private val aliases = Array(aliases.size) { aliases[it].lowercase() }
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
@Suppress("name_shadowing")
|
||||
val name = name.lowercase()
|
||||
|
||||
for (alias in aliases)
|
||||
if (name == alias)
|
||||
return true
|
||||
|
||||
return name == this.name
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(this.name)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.io.json.builder
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.google.common.collect.Interner
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSyntaxException
|
||||
@ -90,6 +91,8 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
* @see Builder.logMisses
|
||||
*/
|
||||
val logMisses: Boolean,
|
||||
|
||||
val stringInterner: Interner<String> = Interner { it },
|
||||
) : TypeAdapter<T>() {
|
||||
private val loggedMisses = ObjectOpenHashSet<String>()
|
||||
|
||||
@ -112,7 +115,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
reader = JsonTreeReader(json)
|
||||
|
||||
if (instance is INativeJsonHolder) {
|
||||
instance.acceptJson(flattenJsonElement(json, Starbound.STRING_INTERNER::intern))
|
||||
instance.acceptJson(flattenJsonElement(json, stringInterner::intern))
|
||||
} else if (instance is IJsonHolder) {
|
||||
instance.acceptJson(json)
|
||||
}
|
||||
@ -216,6 +219,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
var extraPropertiesAreFatal = false
|
||||
var logMisses: Boolean? = null
|
||||
private val factoryReturnType by lazy { factory.invoke()::class.java }
|
||||
var stringInterner: Interner<String> = Interner { it }
|
||||
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == factoryReturnType) {
|
||||
@ -234,6 +238,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
return BuilderAdapter(
|
||||
factory = factory,
|
||||
properties = map.build(),
|
||||
stringInterner = stringInterner,
|
||||
ignoreKeys = ImmutableSet.copyOf(ignoreKeys),
|
||||
extraPropertiesAreFatal = extraPropertiesAreFatal,
|
||||
logMisses = logMisses ?: properties.none { it.isFlat },
|
||||
@ -250,6 +255,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
return BuilderAdapter(
|
||||
factory = factory,
|
||||
properties = map.build(),
|
||||
stringInterner = stringInterner,
|
||||
ignoreKeys = ImmutableSet.copyOf(ignoreKeys),
|
||||
extraPropertiesAreFatal = extraPropertiesAreFatal,
|
||||
logMisses = logMisses ?: properties.none { it.isFlat },
|
||||
@ -348,7 +354,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
private val declCache = Reference2ObjectOpenHashMap<KClass<*>, ArrayList<KMutableProperty1<*, *>>>()
|
||||
|
||||
@ -386,7 +392,9 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
synchronized(declCache) { declCache.put(input, list) }
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
|
||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
val raw = type.rawType
|
||||
|
||||
@ -400,6 +408,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
|
||||
builder.logMisses = bconfig.realLogMisses
|
||||
builder.extraPropertiesAreFatal = bconfig.extraPropertiesAreFatal
|
||||
builder.stringInterner = stringInterner
|
||||
|
||||
for (name in bconfig.ignoreKeys) {
|
||||
builder.ignoreKey(name)
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.io.json.builder
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.Interner
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
@ -43,6 +44,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
val asJsonArray: Boolean,
|
||||
val storesJson: Boolean,
|
||||
val logMisses: Boolean,
|
||||
val stringInterner: Interner<String>
|
||||
) : TypeAdapter<T>() {
|
||||
private val name2index = Object2IntArrayMap<String>()
|
||||
private val loggedMisses = ObjectArraySet<String>()
|
||||
@ -186,7 +188,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
}
|
||||
|
||||
reader = JsonTreeReader(readArray)
|
||||
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, Starbound.STRING_INTERNER::intern)
|
||||
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, stringInterner::intern)
|
||||
}
|
||||
|
||||
reader.beginArray()
|
||||
@ -196,11 +198,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
val name = fieldId.toString()
|
||||
|
||||
if (!storesJson && loggedMisses.add(name)) {
|
||||
if (currentSymbolicName == null) {
|
||||
LOGGER.warn("${bound.qualifiedName} has no property for storing $name")
|
||||
} else {
|
||||
LOGGER.warn("${bound.qualifiedName} has no property for storing $name (reading: $currentSymbolicName)")
|
||||
}
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
@ -235,7 +233,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
json = readMap
|
||||
reader = JsonTreeReader(readMap)
|
||||
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, Starbound.STRING_INTERNER::intern)
|
||||
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, stringInterner::intern)
|
||||
}
|
||||
|
||||
reader.beginObject()
|
||||
@ -246,11 +244,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
if (fieldId == -1) {
|
||||
if (!storesJson && !hasFlatValues && logMisses && loggedMisses.add(name)) {
|
||||
if (currentSymbolicName == null) {
|
||||
LOGGER.warn("${bound.qualifiedName} has no property for storing $name ")
|
||||
} else {
|
||||
LOGGER.warn("${bound.qualifiedName} has no property for storing $name (reading: $currentSymbolicName)")
|
||||
}
|
||||
LOGGER.warn("${bound.qualifiedName} has no property for storing $name")
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
@ -386,6 +380,12 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
private var logMisses = true
|
||||
private val types = ArrayList<IResolvableProperty<T, *>>()
|
||||
private var stringTransformer: ((String) -> T)? = null
|
||||
var stringInterner: Interner<String> = Interner { it }
|
||||
|
||||
fun stringInterner(interner: Interner<String>): Builder<T> {
|
||||
this.stringInterner = interner
|
||||
return this
|
||||
}
|
||||
|
||||
fun ifString(transformer: (String) -> T): Builder<T> {
|
||||
stringTransformer = transformer
|
||||
@ -412,6 +412,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
asJsonArray = asList,
|
||||
storesJson = storesJson,
|
||||
logMisses = logMisses,
|
||||
stringInterner = stringInterner,
|
||||
).let {
|
||||
if (stringTransformer != null)
|
||||
it.ifString(stringTransformer!!)
|
||||
@ -433,10 +434,11 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
return FactoryAdapter(
|
||||
bound = clazz,
|
||||
types = types.stream().map { it.resolve(null) }.collect(ImmutableList.toImmutableList()),
|
||||
types = ImmutableList.copyOf(types.map { it.resolve(null) }),
|
||||
asJsonArray = asList,
|
||||
storesJson = storesJson,
|
||||
logMisses = logMisses,
|
||||
stringInterner = stringInterner,
|
||||
).let {
|
||||
if (stringTransformer != null)
|
||||
it.ifString(stringTransformer!!)
|
||||
@ -522,11 +524,11 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
|
||||
var currentSymbolicName by ThreadLocal<String>()
|
||||
|
||||
class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
|
||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
val raw = type.rawType
|
||||
|
||||
@ -545,6 +547,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
builder.storesJson(bconfig.storesJson)
|
||||
builder.logMisses(bconfig.logMisses)
|
||||
builder.stringInterner = stringInterner
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
throw IllegalArgumentException("${kclass.qualifiedName} has no valid members")
|
||||
@ -554,14 +557,18 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
if (!bconfig.storesJson) {
|
||||
for (argument in foundConstructor.parameters) {
|
||||
builder.auto(properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) })
|
||||
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
|
||||
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
|
||||
builder.auto(property, isFlat = config?.isFlat ?: false)
|
||||
}
|
||||
} else {
|
||||
val params = foundConstructor.parameters
|
||||
|
||||
for (i in 0 until params.size - 1) {
|
||||
val argument = params[i]
|
||||
builder.auto(properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) })
|
||||
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
|
||||
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
|
||||
builder.auto(property, isFlat = config?.isFlat ?: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ import ru.dbotthepony.kstarbound.math.*
|
||||
import ru.dbotthepony.kstarbound.util.Timer
|
||||
import ru.dbotthepony.kstarbound.world.entities.CollisionResolution
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
|
||||
import ru.dbotthepony.kvector.narray.Double2Dimensional
|
||||
import ru.dbotthepony.kvector.narray.Int2Dimensional
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
@ -25,7 +24,6 @@ import ru.dbotthepony.kvector.util2d.AABBi
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import java.util.LinkedList
|
||||
import java.util.function.DoubleBinaryOperator
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.cos
|
||||
@ -162,9 +160,9 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
val dataA = fixtureA.body!!.userData
|
||||
val dataB = fixtureB.body!!.userData
|
||||
|
||||
if (dataA is AbstractProjectileMovementController && dataB is AbstractProjectileMovementController) {
|
||||
return false
|
||||
}
|
||||
//if (dataA is AbstractProjectileMovementController && dataB is AbstractProjectileMovementController) {
|
||||
// return false
|
||||
//}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -1,127 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities.projectile
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.ContactImpulse
|
||||
import ru.dbotthepony.kbox2d.api.FixtureDef
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ConfiguredProjectile
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ProjectilePhysics
|
||||
import ru.dbotthepony.kstarbound.world.Chunk
|
||||
import ru.dbotthepony.kstarbound.world.entities.AliveEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.LogicalMovementController
|
||||
import ru.dbotthepony.kstarbound.world.entities.MovementController
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import kotlin.math.PI
|
||||
|
||||
abstract class AbstractProjectileMovementController(entity: Projectile, val def: ConfiguredProjectile) : MovementController<Projectile>(entity) {
|
||||
var bounces = 0
|
||||
protected set
|
||||
|
||||
override fun beginContact(contact: AbstractContact) {
|
||||
val dataA = contact.fixtureA.body!!.userData
|
||||
val dataB = contact.fixtureB.body!!.userData
|
||||
|
||||
if (dataA is Chunk<*, *>.TileLayer || dataB is Chunk<*, *>.TileLayer) {
|
||||
bounces++
|
||||
|
||||
if (def.bounces > 0 && bounces >= def.bounces) {
|
||||
// We can't detonate inside physics simulation
|
||||
entity.markForDetonation()
|
||||
}
|
||||
} else if (dataA is MovementController<*>) {
|
||||
entity.collideWithEntity(dataA.entity)
|
||||
} else if (dataB is MovementController<*>) {
|
||||
entity.collideWithEntity(dataB.entity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun endContact(contact: AbstractContact) {
|
||||
|
||||
}
|
||||
|
||||
override fun preSolve(contact: AbstractContact, oldManifold: Manifold) {
|
||||
|
||||
}
|
||||
|
||||
override fun postSolve(contact: AbstractContact, impulse: ContactImpulse) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies linear velocity along current facing angle scaled with [ConfiguredProjectile.speed]
|
||||
*/
|
||||
open fun push() {
|
||||
body.linearVelocity += Vector2d.POSITIVE_Y.rotate(body.angle) * def.speed
|
||||
}
|
||||
|
||||
protected open fun updateAngle() {
|
||||
body.setTransform(position, body.linearVelocity.normalized.toAngle())
|
||||
}
|
||||
|
||||
override fun think(delta: Double) {
|
||||
super.think(delta)
|
||||
updateAngle()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun factorize(entity: Projectile, def: ConfiguredProjectile): MovementController<*>? {
|
||||
return when (def.physics) {
|
||||
ProjectilePhysics.DEFAULT -> LogicalMovementController(entity)
|
||||
ProjectilePhysics.BOUNCY -> BouncyPhysics(entity, def)
|
||||
ProjectilePhysics.FLAME -> FlamePhysics(entity, def)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BouncyPhysics(entity: Projectile, def: ConfiguredProjectile) : AbstractProjectileMovementController(entity, def) {
|
||||
override fun onSpawnedInWorld() {
|
||||
super.onSpawnedInWorld()
|
||||
|
||||
body.createFixture(FixtureDef(
|
||||
shape = PolygonShape().also { it.setAsBox(0.5, 0.2) },
|
||||
restitution = 0.9,
|
||||
friction = 0.7,
|
||||
density = 2.0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
class FlamePhysics(entity: Projectile, def: ConfiguredProjectile) : AbstractProjectileMovementController(entity, def) {
|
||||
override fun onSpawnedInWorld() {
|
||||
super.onSpawnedInWorld()
|
||||
|
||||
body.createFixture(FixtureDef(
|
||||
shape = PolygonShape().also { it.setAsBox(0.2, 0.2) },
|
||||
restitution = 0.0,
|
||||
friction = 1.0,
|
||||
density = 0.3,
|
||||
))
|
||||
}
|
||||
|
||||
private var touchedGround = false
|
||||
private var fixedRotation = false
|
||||
|
||||
override fun updateAngle() {
|
||||
if (!fixedRotation && !touchedGround)
|
||||
super.updateAngle()
|
||||
}
|
||||
|
||||
override fun think(delta: Double) {
|
||||
super.think(delta)
|
||||
|
||||
if (touchedGround && !fixedRotation) {
|
||||
fixedRotation = true
|
||||
body.setTransform(body.position, -PI / 2.0)
|
||||
body.isFixedRotation = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun beginContact(contact: AbstractContact) {
|
||||
super.beginContact(contact)
|
||||
touchedGround = true
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities.projectile
|
||||
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ConfiguredProjectile
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.entities.IEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.LogicalMovementController
|
||||
import ru.dbotthepony.kstarbound.world.entities.MovementController
|
||||
|
||||
class Projectile(world: World<*, *>, val def: ConfiguredProjectile) : Entity(world) {
|
||||
override val movement: MovementController<Projectile> = (
|
||||
AbstractProjectileMovementController.factorize(this, def) ?:
|
||||
LogicalMovementController(this).also { LOGGER.error("No physics controller for ${def.physics}, defaulting to dummy movement controller!") }) as MovementController<Projectile>
|
||||
|
||||
private var timeToLive = def.timeToLive
|
||||
private var markForDeath = false
|
||||
|
||||
override fun thinkAI(delta: Double) {
|
||||
timeToLive -= delta
|
||||
|
||||
if (timeToLive <= 0.0 || markForDeath) {
|
||||
detonate()
|
||||
}
|
||||
}
|
||||
|
||||
fun markForDetonation() {
|
||||
markForDeath = true
|
||||
}
|
||||
|
||||
fun collideWithEntity(other: IEntity) {
|
||||
// Can't do anything if we are technically dead
|
||||
if (markForDeath)
|
||||
return
|
||||
|
||||
if (!def.piercing) {
|
||||
markForDeath = true
|
||||
}
|
||||
|
||||
if (def.damageKind != null)
|
||||
other.dealDamage(def.power, def.damageKind, def.damageType)
|
||||
}
|
||||
|
||||
fun detonate() {
|
||||
for (action in def.actionOnReap) {
|
||||
action.execute(this)
|
||||
}
|
||||
|
||||
remove()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger(Projectile::class.java)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user