давайте снова попробуем builder'ов, но на этот раз с интерфейсами
This commit is contained in:
parent
78cdc2c886
commit
3da8450a2c
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.TypeAdapter
|
||||
import java.util.Arrays
|
||||
@ -19,3 +20,5 @@ operator fun <T> ThreadLocal<T>.getValue(thisRef: Any, property: KProperty<*>):
|
||||
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T?) {
|
||||
set(value)
|
||||
}
|
||||
|
||||
operator fun <K, V> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> = put(key, value)
|
||||
|
@ -17,8 +17,16 @@ import ru.dbotthepony.kstarbound.api.explore
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ArmorItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.ArmorPieceType
|
||||
import ru.dbotthepony.kstarbound.defs.item.CurrencyItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.IArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.IFossilItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemTooltipKind
|
||||
import ru.dbotthepony.kstarbound.defs.item.LeveledStatusEffect
|
||||
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
@ -30,7 +38,7 @@ 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.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||
@ -57,24 +65,24 @@ object Starbound {
|
||||
/**
|
||||
* Служит переменной для указания из какой папки происходит чтение asset'а в данном потоке
|
||||
*/
|
||||
var readingFolder by ThreadLocal<String>()
|
||||
var assetFolder by ThreadLocal<String>()
|
||||
private set
|
||||
|
||||
fun readingFolderTransformer(input: String): String {
|
||||
val readingFolder = readingFolder
|
||||
require(readingFolder != null) { "Not reading an asset on current thread" }
|
||||
fun assetFolder(input: String): String {
|
||||
val assetFolder = assetFolder
|
||||
require(assetFolder != null) { "Not reading an asset on current thread" }
|
||||
|
||||
if (input[0] == '/')
|
||||
return input
|
||||
|
||||
return assetStringInterner.intern("$readingFolder/$input")
|
||||
return STRING_INTERNER.intern("$assetFolder/$input")
|
||||
}
|
||||
|
||||
fun readingFolderTransformerNullable(input: String?): String? {
|
||||
require(readingFolder != null) { "Not reading an asset on current thread" }
|
||||
fun assetFolderNullable(input: String?): String? {
|
||||
require(assetFolder != null) { "Not reading an asset on current thread" }
|
||||
|
||||
if (input != null)
|
||||
return readingFolderTransformer(input)
|
||||
return assetFolder(input)
|
||||
|
||||
return null
|
||||
}
|
||||
@ -83,7 +91,7 @@ object Starbound {
|
||||
if (input == null)
|
||||
return null
|
||||
|
||||
return input.stream().map { readingFolderTransformer(it) }.collect(ImmutableList.toImmutableList())
|
||||
return input.stream().map { assetFolder(it) }.collect(ImmutableList.toImmutableList())
|
||||
}
|
||||
|
||||
private val tiles = Object2ObjectOpenHashMap<String, TileDefinition>()
|
||||
@ -99,7 +107,7 @@ object Starbound {
|
||||
private val parallax = Object2ObjectOpenHashMap<String, ParallaxPrototype>()
|
||||
private val functions = Object2ObjectOpenHashMap<String, JsonFunction>()
|
||||
|
||||
private val items = Object2ObjectOpenHashMap<String, ItemDefinition>()
|
||||
private val items = Object2ObjectOpenHashMap<String, IItemDefinition>()
|
||||
|
||||
val liquidAccess: Map<String, LiquidDefinition> = Collections.unmodifiableMap(liquid)
|
||||
val liquidByIDAccess: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
|
||||
@ -110,21 +118,21 @@ object Starbound {
|
||||
val projectilesAccess: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
||||
val parallaxAccess: Map<String, ParallaxPrototype> = Collections.unmodifiableMap(parallax)
|
||||
val functionsAccess: Map<String, JsonFunction> = Collections.unmodifiableMap(functions)
|
||||
val itemAccess: Map<String, ItemDefinition> = Collections.unmodifiableMap(items)
|
||||
val itemAccess: Map<String, IItemDefinition> = Collections.unmodifiableMap(items)
|
||||
|
||||
val assetStringInterner: Interner<String> = Interners.newStrongInterner()
|
||||
val STRING_INTERNER: Interner<String> = Interners.newStrongInterner()
|
||||
|
||||
val nonnullStringTypeAdapter: TypeAdapter<String> = object : TypeAdapter<String>() {
|
||||
val STRING_ADAPTER: TypeAdapter<String> = object : TypeAdapter<String>() {
|
||||
override fun write(out: JsonWriter, value: String) {
|
||||
out.value(value)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): String {
|
||||
return assetStringInterner.intern(TypeAdapters.STRING.read(`in`))
|
||||
return STRING_INTERNER.intern(TypeAdapters.STRING.read(`in`))
|
||||
}
|
||||
}
|
||||
|
||||
val stringTypeAdapter: TypeAdapter<String?> = nonnullStringTypeAdapter.nullSafe()
|
||||
val NULLABLE_STRING_ADAPTER: TypeAdapter<String?> = STRING_ADAPTER.nullSafe()
|
||||
|
||||
val gson: Gson = GsonBuilder()
|
||||
.enableComplexMapKeySerialization()
|
||||
@ -135,7 +143,7 @@ object Starbound {
|
||||
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe())
|
||||
|
||||
// чтоб строки всегда intern'ились
|
||||
.registerTypeAdapter(stringTypeAdapter)
|
||||
.registerTypeAdapter(NULLABLE_STRING_ADAPTER)
|
||||
|
||||
// math
|
||||
.registerTypeAdapter(AABBTypeAdapter)
|
||||
@ -156,12 +164,22 @@ object Starbound {
|
||||
.also(RenderTemplate::registerGson)
|
||||
.also(TileDefinition::registerGson)
|
||||
.also(LiquidDefinition::registerGson)
|
||||
.also(ItemDefinition::registerGson)
|
||||
.also(ItemRarity::registerGson)
|
||||
.also(SpriteReference::registerGson)
|
||||
.also(AtlasConfiguration::registerGson)
|
||||
|
||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||
.registerTypeAdapter(LeveledStatusEffect.ADAPTER)
|
||||
|
||||
.registerTypeAdapter(ItemPrototype.ADAPTER)
|
||||
.registerTypeAdapter(CurrencyItemPrototype.ADAPTER)
|
||||
.registerTypeAdapter(ArmorItemPrototype.ADAPTER)
|
||||
|
||||
.registerTypeAdapter(IItemDefinition.InventoryIcon.ADAPTER)
|
||||
.registerTypeAdapter(IFossilItemDefinition.FossilSetDescription.ADAPTER)
|
||||
.registerTypeAdapter(IArmorItemDefinition.ArmorFrames.ADAPTER)
|
||||
|
||||
.registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.NORMAL).neverNull())
|
||||
.registerTypeAdapter(EnumAdapter(ItemRarity::class).neverNull())
|
||||
.registerTypeAdapter(EnumAdapter(ItemTooltipKind::class).neverNull())
|
||||
|
||||
.create()
|
||||
|
||||
@ -170,7 +188,7 @@ object Starbound {
|
||||
return when (type) {
|
||||
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
||||
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
|
||||
String::class.java -> stringTypeAdapter as TypeAdapter<T>
|
||||
String::class.java -> NULLABLE_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>
|
||||
@ -339,14 +357,14 @@ object Starbound {
|
||||
}
|
||||
|
||||
private fun loadTileMaterials(callback: (String) -> Unit) {
|
||||
readingFolder = "/tiles/materials"
|
||||
assetFolder = "/tiles/materials"
|
||||
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".material") }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val tileDef = gson.fromJson(listedFile.reader(), TileDefinition::class.java)
|
||||
|
||||
check(tiles[tileDef.materialName] == null) { "Already has material with name ${tileDef.materialName} loaded!" }
|
||||
@ -364,7 +382,7 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadProjectiles(callback: (String) -> Unit) {
|
||||
@ -373,7 +391,7 @@ object Starbound {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val def = gson.fromJson(listedFile.reader(), ConfigurableProjectile::class.java).assemble(listedFile.computeDirectory())
|
||||
check(projectiles[def.projectileName] == null) { "Already has projectile with ID ${def.projectileName} loaded!" }
|
||||
projectiles[def.projectileName] = def
|
||||
@ -388,7 +406,7 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadFunctions(callback: (String) -> Unit) {
|
||||
@ -397,7 +415,7 @@ object Starbound {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
||||
|
||||
for (key in readObject.keySet()) {
|
||||
@ -414,7 +432,7 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadParallax(callback: (String) -> Unit) {
|
||||
@ -423,7 +441,7 @@ object Starbound {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val def = gson.fromJson(listedFile.reader(), ParallaxPrototype::class.java)
|
||||
parallax[listedFile.name.substringBefore('.')] = def
|
||||
} catch(err: Throwable) {
|
||||
@ -436,18 +454,18 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
||||
readingFolder = "/tiles/materials"
|
||||
assetFolder = "/tiles/materials"
|
||||
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".matmod") }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val tileDef = gson.fromJson(listedFile.reader(), MaterialModifier::class.java)
|
||||
|
||||
check(tileModifiers[tileDef.modName] == null) { "Already has material with name ${tileDef.modName} loaded!" }
|
||||
@ -465,7 +483,7 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadLiquidDefinitions(callback: (String) -> Unit) {
|
||||
@ -474,7 +492,7 @@ object Starbound {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
val liquidDef = gson.fromJson(listedFile.reader(), LiquidDefinition::class.java)
|
||||
|
||||
check(liquid.put(liquidDef.name, liquidDef) == null) { "Already has liquid with name ${liquidDef.name} loaded!" }
|
||||
@ -490,22 +508,42 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
|
||||
private fun loadItemDefinitions(callback: (String) -> Unit) {
|
||||
val files = listOf(".item", ".currency", ".head", ".chest", ".legs", ".activeitem")
|
||||
val files = listOf(".item", ".currency", ".head", ".chest", ".legs", ".back", ".activeitem")
|
||||
|
||||
for (fs in fileSystems) {
|
||||
for (listedFile in fs.explore().filter { it.isFile }.filter { f -> files.any { f.name.endsWith(it) } }) {
|
||||
try {
|
||||
callback("Loading $listedFile")
|
||||
|
||||
readingFolder = listedFile.computeDirectory()
|
||||
ItemDefinition.ADAPTER.currentSymbolicName = listedFile.computeFullPath()
|
||||
val def = gson.fromJson(listedFile.reader(), ItemDefinition::class.java)
|
||||
assetFolder = listedFile.computeDirectory()
|
||||
|
||||
check(items.put(def.itemName, def) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
if (listedFile.name.endsWith(".item")) {
|
||||
val def = gson.fromJson(listedFile.reader(), ItemPrototype::class.java)
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
} else if (listedFile.name.endsWith(".currency")) {
|
||||
val def = gson.fromJson(listedFile.reader(), CurrencyItemPrototype::class.java)
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
} else if (listedFile.name.endsWith(".head")) {
|
||||
val def = gson.fromJson(listedFile.reader(), ArmorItemPrototype::class.java)
|
||||
def.armorType = ArmorPieceType.HEAD
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
} else if (listedFile.name.endsWith(".chest")) {
|
||||
val def = gson.fromJson(listedFile.reader(), ArmorItemPrototype::class.java)
|
||||
def.armorType = ArmorPieceType.CHEST
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
} else if (listedFile.name.endsWith(".legs")) {
|
||||
val def = gson.fromJson(listedFile.reader(), ArmorItemPrototype::class.java)
|
||||
def.armorType = ArmorPieceType.LEGS
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
} else if (listedFile.name.endsWith(".back")) {
|
||||
val def = gson.fromJson(listedFile.reader(), ArmorItemPrototype::class.java)
|
||||
def.armorType = ArmorPieceType.BACK
|
||||
check(items.put(def.itemName, def.assemble()) == null) { "Already has item with name ${def.itemName} loaded!" }
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Loading item definition file $listedFile", err)
|
||||
}
|
||||
@ -516,6 +554,6 @@ object Starbound {
|
||||
}
|
||||
}
|
||||
|
||||
readingFolder = null
|
||||
assetFolder = null
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
interface IThingWithDescription {
|
||||
/**
|
||||
* Краткое описание штуки. Несмотря на то, что название свойства подразумевает "описание",
|
||||
* на самом деле данное поле отвечает за название штуки.
|
||||
*
|
||||
* Примеры:
|
||||
* * Microwave Oven
|
||||
* * Copper Ore
|
||||
* * Poptop
|
||||
*/
|
||||
val shortdescription: String
|
||||
|
||||
/**
|
||||
* Полное описание штуки. Оно отображается игроку, когда последний наводит курсор на штуку.
|
||||
*
|
||||
* Примеры:
|
||||
* * A microwave. For when you're hungry enough to nuke your food.
|
||||
* * Copper ore. Can be used for smelting.
|
||||
* * The Poptop hums beautifully to confuse its prey.
|
||||
*/
|
||||
val description: String
|
||||
}
|
@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
|
||||
/**
|
||||
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||
* Возвращает глубокую, неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||
*/
|
||||
fun enrollList(input: List<Any>, interner: (String) -> String = String::intern): ImmutableList<Any> {
|
||||
val builder = ImmutableList.builder<Any>()
|
||||
@ -21,7 +21,7 @@ fun enrollList(input: List<Any>, interner: (String) -> String = String::intern):
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||
* Возвращает глубокую, неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||
*/
|
||||
fun enrollMap(input: Map<String, Any>, interner: (String) -> String = String::intern): ImmutableMap<String, Any> {
|
||||
val builder = ImmutableMap.builder<String, Any>()
|
||||
|
@ -6,7 +6,7 @@ import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
@ -29,7 +29,7 @@ enum class JsonFunctionInterpolation(vararg aliases: String) : IStringSerializab
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER: TypeAdapter<JsonFunctionInterpolation> = CustomEnumTypeAdapter(values()).nullSafe()
|
||||
val ADAPTER: TypeAdapter<JsonFunctionInterpolation> = EnumAdapter(JsonFunctionInterpolation::class).neverNull()
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ enum class JsonFunctionConstraint(vararg aliases: String) : IStringSerializable
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER: TypeAdapter<JsonFunctionConstraint> = CustomEnumTypeAdapter(values()).nullSafe()
|
||||
val ADAPTER: TypeAdapter<JsonFunctionConstraint> = EnumAdapter(JsonFunctionConstraint::class).neverNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import ru.dbotthepony.kstarbound.io.json.INativeJsonHolder
|
||||
*/
|
||||
abstract class RawPrototype<RAW : RawPrototype<RAW, ASSEMBLED>, ASSEMBLED : AssembledPrototype<ASSEMBLED, RAW>> : INativeJsonHolder {
|
||||
val json = Object2ObjectArrayMap<String, Any>()
|
||||
fun enroll() = enrollMap(json, Starbound.assetStringInterner::intern)
|
||||
fun enroll() = enrollMap(json, Starbound.STRING_INTERNER::intern)
|
||||
abstract fun assemble(directory: String = ""): ASSEMBLED
|
||||
|
||||
override fun acceptJson(json: MutableMap<String, Any>) {
|
||||
|
@ -304,7 +304,7 @@ class AtlasConfiguration private constructor(
|
||||
return EMPTY
|
||||
}
|
||||
|
||||
val ADAPTER: TypeAdapter<AtlasConfiguration?> = Starbound.stringTypeAdapter.transform(read = read@{ get(it ?: return@read it as AtlasConfiguration?) }, write = write@{ it?.name })
|
||||
val ADAPTER: TypeAdapter<AtlasConfiguration?> = Starbound.NULLABLE_STRING_ADAPTER.transform(read = read@{ get(it ?: return@read it as AtlasConfiguration?) }, write = write@{ it?.name })
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||
|
@ -30,7 +30,7 @@ data class ImageReference(
|
||||
|
||||
override fun read(`in`: JsonReader): ImageReference {
|
||||
if (`in`.peek() == JsonToken.STRING) {
|
||||
val image = Starbound.readingFolderTransformer(`in`.nextString())
|
||||
val image = Starbound.assetFolder(`in`.nextString())
|
||||
|
||||
if (image.contains(':')) {
|
||||
throw JsonSyntaxException("Expected atlas/image reference, but got sprite reference: $image")
|
||||
|
@ -30,7 +30,7 @@ data class SpriteReference(
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): SpriteReference {
|
||||
return parse(Starbound.readingFolderTransformer(`in`.nextString()))
|
||||
return parse(Starbound.assetFolder(`in`.nextString()))
|
||||
}
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
|
@ -0,0 +1,32 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
data class ArmorItemDefinition(
|
||||
override val shortdescription: String,
|
||||
override val description: String,
|
||||
override val itemName: String,
|
||||
override val price: Long,
|
||||
override val rarity: ItemRarity,
|
||||
override val category: String?,
|
||||
override val inventoryIcon: List<IItemDefinition.IInventoryIcon>?,
|
||||
override val itemTags: List<String>,
|
||||
override val learnBlueprintsOnPickup: List<String>,
|
||||
override val maxStack: Long,
|
||||
override val eventCategory: String?,
|
||||
override val consumeOnPickup: Boolean,
|
||||
override val pickupQuestTemplates: List<String>,
|
||||
override val scripts: List<String>,
|
||||
override val tooltipKind: ItemTooltipKind,
|
||||
override val twoHanded: Boolean,
|
||||
override val radioMessagesOnPickup: List<String>,
|
||||
override val fuelAmount: Long?,
|
||||
|
||||
override val colorOptions: List<Map<String, String>>,
|
||||
override val maleFrames: IArmorItemDefinition.IArmorFrames,
|
||||
override val femaleFrames: IArmorItemDefinition.IArmorFrames,
|
||||
override val level: Double,
|
||||
override val leveledStatusEffects: List<ILeveledStatusEffect>,
|
||||
|
||||
override val armorType: ArmorPieceType,
|
||||
|
||||
val json: Map<String, Any>,
|
||||
) : IArmorItemDefinition
|
@ -0,0 +1,67 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.enrollMap
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.asJsonObject
|
||||
import ru.dbotthepony.kstarbound.io.json.asList
|
||||
import ru.dbotthepony.kstarbound.io.json.neverNull
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
|
||||
class ArmorItemPrototype : ItemPrototype(), IArmorItemDefinition {
|
||||
override var colorOptions: List<Map<String, String>> = listOf()
|
||||
override var maleFrames: IArmorItemDefinition.ArmorFrames by NotNullVar()
|
||||
override var femaleFrames: IArmorItemDefinition.ArmorFrames by NotNullVar()
|
||||
override var level: Double = 1.0
|
||||
override var leveledStatusEffects: List<LeveledStatusEffect> = listOf()
|
||||
|
||||
override var armorType: ArmorPieceType by NotNullVar()
|
||||
|
||||
init {
|
||||
maxStack = 1L
|
||||
}
|
||||
|
||||
override fun assemble(): IItemDefinition {
|
||||
return ArmorItemDefinition(
|
||||
shortdescription = shortdescription,
|
||||
description = description,
|
||||
itemName = itemName,
|
||||
price = price,
|
||||
rarity = rarity,
|
||||
category = category,
|
||||
inventoryIcon = inventoryIcon,
|
||||
itemTags = itemTags,
|
||||
learnBlueprintsOnPickup = learnBlueprintsOnPickup,
|
||||
maxStack = maxStack,
|
||||
eventCategory = eventCategory,
|
||||
consumeOnPickup = consumeOnPickup,
|
||||
pickupQuestTemplates = pickupQuestTemplates,
|
||||
scripts = scripts,
|
||||
tooltipKind = tooltipKind,
|
||||
twoHanded = twoHanded,
|
||||
radioMessagesOnPickup = radioMessagesOnPickup,
|
||||
fuelAmount = fuelAmount,
|
||||
|
||||
json = enrollMap(json),
|
||||
|
||||
colorOptions = colorOptions,
|
||||
maleFrames = maleFrames,
|
||||
femaleFrames = femaleFrames,
|
||||
level = level,
|
||||
leveledStatusEffects = leveledStatusEffects,
|
||||
|
||||
armorType = armorType,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ArmorItemPrototype)
|
||||
.also { addFields(it as BuilderAdapter.Builder<ItemPrototype>) } // безопасность: свойства родительского класса объявлены как final
|
||||
.add(ArmorItemPrototype::colorOptions, Starbound.NULLABLE_STRING_ADAPTER.neverNull().asJsonObject().asList())
|
||||
.auto(ArmorItemPrototype::maleFrames)
|
||||
.auto(ArmorItemPrototype::femaleFrames)
|
||||
.auto(ArmorItemPrototype::level)
|
||||
.autoList(ArmorItemPrototype::leveledStatusEffects)
|
||||
.build()
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
/**
|
||||
* Тип брони. Более формально, в какой слот надевается данный предмет
|
||||
*/
|
||||
enum class ArmorPieceType {
|
||||
/**
|
||||
* Шлем
|
||||
*/
|
||||
HEAD,
|
||||
|
||||
/**
|
||||
* Нагрудник
|
||||
*/
|
||||
CHEST,
|
||||
|
||||
/**
|
||||
* Поножи
|
||||
*/
|
||||
LEGS,
|
||||
|
||||
/**
|
||||
* Плащ/рюкзак/прочее
|
||||
*/
|
||||
BACK
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
data class CurrencyItemDefinition(
|
||||
override val shortdescription: String,
|
||||
override val description: String,
|
||||
override val itemName: String,
|
||||
override val price: Long,
|
||||
override val rarity: ItemRarity,
|
||||
override val category: String?,
|
||||
override val inventoryIcon: List<IItemDefinition.IInventoryIcon>?,
|
||||
override val itemTags: List<String>,
|
||||
override val learnBlueprintsOnPickup: List<String>,
|
||||
override val maxStack: Long,
|
||||
override val eventCategory: String?,
|
||||
override val consumeOnPickup: Boolean,
|
||||
override val pickupQuestTemplates: List<String>,
|
||||
override val scripts: List<String>,
|
||||
override val tooltipKind: ItemTooltipKind,
|
||||
override val twoHanded: Boolean,
|
||||
override val radioMessagesOnPickup: List<String>,
|
||||
override val fuelAmount: Long?,
|
||||
|
||||
override val pickupSoundsSmall: List<String>,
|
||||
override val pickupSoundsMedium: List<String>,
|
||||
override val pickupSoundsLarge: List<String>,
|
||||
override val smallStackLimit: Long,
|
||||
override val mediumStackLimit: Long,
|
||||
override val currency: String,
|
||||
override val value: Long,
|
||||
|
||||
val json: Map<String, Any>,
|
||||
) : ICurrencyItemDefinition
|
@ -0,0 +1,65 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.enrollMap
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
|
||||
class CurrencyItemPrototype : ItemPrototype(), ICurrencyItemDefinition {
|
||||
override var pickupSoundsSmall: List<String> = listOf()
|
||||
override var pickupSoundsMedium: List<String> = listOf()
|
||||
override var pickupSoundsLarge: List<String> = listOf()
|
||||
override var smallStackLimit: Long by NotNullVar()
|
||||
override var mediumStackLimit: Long by NotNullVar()
|
||||
override var currency: String by NotNullVar()
|
||||
override var value: Long by NotNullVar()
|
||||
|
||||
init {
|
||||
maxStack = 16777216L
|
||||
}
|
||||
|
||||
override fun assemble(): IItemDefinition {
|
||||
return CurrencyItemDefinition(
|
||||
shortdescription = shortdescription,
|
||||
description = description,
|
||||
itemName = itemName,
|
||||
price = price,
|
||||
rarity = rarity,
|
||||
category = category,
|
||||
inventoryIcon = inventoryIcon,
|
||||
itemTags = itemTags,
|
||||
learnBlueprintsOnPickup = learnBlueprintsOnPickup,
|
||||
maxStack = maxStack,
|
||||
eventCategory = eventCategory,
|
||||
consumeOnPickup = consumeOnPickup,
|
||||
pickupQuestTemplates = pickupQuestTemplates,
|
||||
scripts = scripts,
|
||||
tooltipKind = tooltipKind,
|
||||
twoHanded = twoHanded,
|
||||
radioMessagesOnPickup = radioMessagesOnPickup,
|
||||
fuelAmount = fuelAmount,
|
||||
|
||||
json = enrollMap(json),
|
||||
|
||||
pickupSoundsSmall = pickupSoundsSmall,
|
||||
pickupSoundsMedium = pickupSoundsMedium,
|
||||
pickupSoundsLarge = pickupSoundsLarge,
|
||||
smallStackLimit = smallStackLimit,
|
||||
mediumStackLimit = mediumStackLimit,
|
||||
currency = currency,
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::CurrencyItemPrototype)
|
||||
.also { addFields(it as BuilderAdapter.Builder<ItemPrototype>) } // безопасность: свойства родительского класса объявлены как final
|
||||
.autoList(CurrencyItemPrototype::pickupSoundsSmall)
|
||||
.autoList(CurrencyItemPrototype::pickupSoundsMedium)
|
||||
.autoList(CurrencyItemPrototype::pickupSoundsLarge)
|
||||
.auto(CurrencyItemPrototype::smallStackLimit)
|
||||
.auto(CurrencyItemPrototype::mediumStackLimit)
|
||||
.auto(CurrencyItemPrototype::currency)
|
||||
.auto(CurrencyItemPrototype::value)
|
||||
.build()
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.io.json.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.ifString
|
||||
|
||||
interface IArmorItemDefinition : ILeveledItemDefinition {
|
||||
/**
|
||||
* @see ArmorPieceType
|
||||
*/
|
||||
val armorType: ArmorPieceType
|
||||
|
||||
/**
|
||||
* Варианты покраски (???)
|
||||
*/
|
||||
val colorOptions: List<Map<String, String>>
|
||||
|
||||
/**
|
||||
* Визуальные кадры анимации, когда надето на гуманоида мужского пола
|
||||
*/
|
||||
val maleFrames: IArmorFrames
|
||||
|
||||
/**
|
||||
* Визуальные кадры анимации, когда надето на гуманоида женского пола
|
||||
*/
|
||||
val femaleFrames: IArmorFrames
|
||||
|
||||
interface IArmorFrames {
|
||||
val body: String
|
||||
val backSleeve: String?
|
||||
val frontSleeve: String?
|
||||
}
|
||||
|
||||
data class ArmorFrames(
|
||||
override val body: String,
|
||||
override val backSleeve: String?,
|
||||
override val frontSleeve: String?,
|
||||
) : IArmorFrames {
|
||||
companion object {
|
||||
val ADAPTER = FactoryAdapter.Builder(
|
||||
ArmorFrames::class,
|
||||
ArmorFrames::body,
|
||||
ArmorFrames::backSleeve,
|
||||
ArmorFrames::frontSleeve,
|
||||
)
|
||||
.build()
|
||||
.ifString { ArmorFrames(Starbound.assetFolder(it), null, null) }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
interface ICurrencyItemDefinition : IItemDefinition {
|
||||
/**
|
||||
* Звуки при поднятии "малого" количества предметов. Не имеет никакого смысла без [smallStackLimit]
|
||||
*/
|
||||
val pickupSoundsSmall: List<String>
|
||||
|
||||
/**
|
||||
* Звуки при поднятии "среднего" количества предметов. Не имеет никакого смысла без [mediumStackLimit]
|
||||
*/
|
||||
val pickupSoundsMedium: List<String>
|
||||
|
||||
/**
|
||||
* Звуки при поднятии "большого" количества предметов. Не имеет никакого смысла без [smallStackLimit] и без [mediumStackLimit]
|
||||
*/
|
||||
val pickupSoundsLarge: List<String>
|
||||
|
||||
/**
|
||||
* Количество предметов ниже или равному данному значению проиграет звук [pickupSoundsSmall]
|
||||
*/
|
||||
val smallStackLimit: Long
|
||||
|
||||
/**
|
||||
* Количество предметов ниже или равному данному значению (но не меньше [smallStackLimit]) проиграет звук [pickupSoundsMedium]
|
||||
*/
|
||||
val mediumStackLimit: Long
|
||||
|
||||
/**
|
||||
* ID валюты
|
||||
*/
|
||||
val currency: String
|
||||
|
||||
/**
|
||||
* Ценность одного предмета в [currency]
|
||||
*/
|
||||
val value: Long
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.IThingWithDescription
|
||||
import ru.dbotthepony.kstarbound.io.json.FactoryAdapter
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
interface IFossilItemDefinition : IItemDefinition {
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val race: String
|
||||
val displayImage: String
|
||||
val displayoffset: Vector2d
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val fossilSetName: String
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setIndex: Int
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setCount: Int
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setCollectables: Map<String, String>
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeFossilIcon: String?
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeFossilObject: String?
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeSetDescriptions: IFossilSetDescription?
|
||||
|
||||
interface IFossilSetDescription : IThingWithDescription {
|
||||
/**
|
||||
* Цена в пикселях
|
||||
*/
|
||||
val price: Long
|
||||
}
|
||||
|
||||
data class FossilSetDescription(
|
||||
override val price: Long = 0L,
|
||||
override val shortdescription: String = "...",
|
||||
override val description: String = "..."
|
||||
) : IFossilSetDescription {
|
||||
companion object {
|
||||
val ADAPTER = FactoryAdapter.Builder(
|
||||
FossilSetDescription::class,
|
||||
FossilSetDescription::price,
|
||||
FossilSetDescription::shortdescription,
|
||||
FossilSetDescription::description).build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.IThingWithDescription
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.io.json.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.ifString
|
||||
|
||||
interface IItemDefinition : IThingWithDescription {
|
||||
/**
|
||||
* Внутреннее имя предмета (ID).
|
||||
* Не путать с именем предмета!
|
||||
*
|
||||
* @see shortdescription
|
||||
* @see description
|
||||
*/
|
||||
val itemName: String
|
||||
|
||||
/**
|
||||
* Цена в пикселях
|
||||
*/
|
||||
val price: Long
|
||||
|
||||
/**
|
||||
* Редкость как [ItemRarity]
|
||||
*/
|
||||
val rarity: ItemRarity
|
||||
|
||||
/**
|
||||
* Категория предмета, определяет, в какую вкладку инвентаря оно попадает
|
||||
*/
|
||||
val category: String?
|
||||
|
||||
/**
|
||||
* Иконка в инвентаре, относительный и абсолютный пути
|
||||
*/
|
||||
val inventoryIcon: List<IInventoryIcon>?
|
||||
|
||||
interface IInventoryIcon {
|
||||
val image: SpriteReference
|
||||
}
|
||||
|
||||
data class InventoryIcon(
|
||||
override val image: SpriteReference
|
||||
) : IInventoryIcon {
|
||||
companion object {
|
||||
val ADAPTER = FactoryAdapter.Builder(InventoryIcon::class, InventoryIcon::image).build().ifString { InventoryIcon(SpriteReference.parse(Starbound.assetFolder(it))) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Теги предмета
|
||||
*/
|
||||
val itemTags: List<String>
|
||||
|
||||
/**
|
||||
* При подборе предмета мгновенно заставляет игрока изучить эти рецепты крафта
|
||||
*/
|
||||
val learnBlueprintsOnPickup: List<String>
|
||||
|
||||
/**
|
||||
* Максимальное количество предмета в стопке
|
||||
*/
|
||||
val maxStack: Long
|
||||
|
||||
/**
|
||||
* snip
|
||||
*/
|
||||
val eventCategory: String?
|
||||
|
||||
/**
|
||||
* Заставляет предмет "использовать" сразу же при подборе
|
||||
*/
|
||||
val consumeOnPickup: Boolean
|
||||
|
||||
/**
|
||||
* Запускает следующие квест(ы) при подборе
|
||||
*/
|
||||
val pickupQuestTemplates: List<String>
|
||||
|
||||
/**
|
||||
* Lua скрипты для выполнения
|
||||
*/
|
||||
val scripts: List<String>
|
||||
|
||||
/**
|
||||
* это где либо ещё применяется кроме брони?
|
||||
*/
|
||||
val tooltipKind: ItemTooltipKind
|
||||
|
||||
/**
|
||||
* Занимает ли предмет обе руки
|
||||
*/
|
||||
val twoHanded: Boolean
|
||||
|
||||
/**
|
||||
* Заставляет SAIL/прочих болтать при подборе предмета в первый раз
|
||||
*/
|
||||
val radioMessagesOnPickup: List<String>
|
||||
|
||||
/**
|
||||
* Топливо корабля
|
||||
*/
|
||||
val fuelAmount: Long?
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
interface ILeveledItemDefinition : IItemDefinition {
|
||||
/**
|
||||
* Изначальный уровень предмета, может быть изменён позднее чем угодно
|
||||
*/
|
||||
val level: Double
|
||||
|
||||
/**
|
||||
* Эффекты предмета, растущие с уровнем
|
||||
*/
|
||||
val leveledStatusEffects: List<ILeveledStatusEffect>
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.io.json.FactoryAdapter
|
||||
|
||||
interface ILeveledStatusEffect {
|
||||
val levelFunction: String
|
||||
val stat: String
|
||||
val baseMultiplier: Double
|
||||
val amount: Double
|
||||
}
|
||||
|
||||
data class LeveledStatusEffect(
|
||||
override val levelFunction: String,
|
||||
override val stat: String,
|
||||
override val baseMultiplier: Double = 1.0,
|
||||
override val amount: Double = 0.0,
|
||||
) : ILeveledStatusEffect {
|
||||
companion object {
|
||||
val ADAPTER = FactoryAdapter.Builder(LeveledStatusEffect::class,
|
||||
LeveledStatusEffect::levelFunction,
|
||||
LeveledStatusEffect::stat,
|
||||
LeveledStatusEffect::baseMultiplier,
|
||||
LeveledStatusEffect::amount,
|
||||
).build()
|
||||
}
|
||||
}
|
@ -1,354 +1,24 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.io.json.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.ListAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.asJsonObject
|
||||
import ru.dbotthepony.kstarbound.io.json.asList
|
||||
import ru.dbotthepony.kstarbound.io.json.ifString
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
data class ItemDefinition(
|
||||
/**
|
||||
* Внутреннее имя предмета, как строка
|
||||
*/
|
||||
val itemName: String,
|
||||
override val shortdescription: String,
|
||||
override val description: String,
|
||||
override val itemName: String,
|
||||
override val price: Long,
|
||||
override val rarity: ItemRarity,
|
||||
override val category: String?,
|
||||
override val inventoryIcon: List<IItemDefinition.IInventoryIcon>?,
|
||||
override val itemTags: List<String>,
|
||||
override val learnBlueprintsOnPickup: List<String>,
|
||||
override val maxStack: Long,
|
||||
override val eventCategory: String?,
|
||||
override val consumeOnPickup: Boolean,
|
||||
override val pickupQuestTemplates: List<String>,
|
||||
override val scripts: List<String>,
|
||||
override val tooltipKind: ItemTooltipKind,
|
||||
override val twoHanded: Boolean,
|
||||
override val radioMessagesOnPickup: List<String>,
|
||||
override val fuelAmount: Long?,
|
||||
|
||||
/**
|
||||
* Цена в пикселях
|
||||
*/
|
||||
val price: Long = 0L,
|
||||
|
||||
/**
|
||||
* Редкость как [ItemRarity]
|
||||
*/
|
||||
val rarity: ItemRarity = ItemRarity.COMMON,
|
||||
|
||||
/**
|
||||
* Категория предмета, определяет, в какую вкладку инвентаря оно попадает
|
||||
*/
|
||||
val category: String? = null,
|
||||
|
||||
/**
|
||||
* Иконка в инвентаре, относительный и абсолютный пути
|
||||
*/
|
||||
val inventoryIcon: List<InventoryIcon>? = null,
|
||||
|
||||
/**
|
||||
* Описание предмета
|
||||
*/
|
||||
val description: String = "...",
|
||||
|
||||
/**
|
||||
* Название предмета
|
||||
*/
|
||||
val shortdescription: String = "...",
|
||||
|
||||
/**
|
||||
* Теги предмета
|
||||
*/
|
||||
val itemTags: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* При подборе предмета мгновенно заставляет игрока изучить эти рецепты крафта
|
||||
*/
|
||||
val learnBlueprintsOnPickup: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Максимальное количество предмета в стопке, по умолчанию 9999
|
||||
*/
|
||||
val maxStack: Long = 9999L,
|
||||
|
||||
/**
|
||||
* snip
|
||||
*/
|
||||
val eventCategory: String? = null,
|
||||
|
||||
/**
|
||||
* Заставляет предмет "использовать" сразу же при подборе
|
||||
*/
|
||||
val consumeOnPickup: Boolean = false,
|
||||
|
||||
/**
|
||||
* Запускает следующие квест(ы) при подборе
|
||||
*/
|
||||
val pickupQuestTemplates: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val race: String? = null,
|
||||
val displayImage: String? = null,
|
||||
val displayoffset: Vector2d? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val fossilSetName: String? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setIndex: Int? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setCount: Int? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val setCollectables: Map<String, String>? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeFossilIcon: String? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeFossilObject: String? = null,
|
||||
|
||||
/**
|
||||
* Используется в костях-ископаемых
|
||||
*/
|
||||
val completeSetDescriptions: FossilSetDescription? = null,
|
||||
|
||||
/**
|
||||
* Заставляет SAIL/прочих болтать при подборе предмета в первый раз
|
||||
*/
|
||||
val radioMessagesOnPickup: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Топливо корабля
|
||||
*/
|
||||
val fuelAmount: Long? = null,
|
||||
|
||||
// ----------------
|
||||
// Поля ниже были видны только в файлах валюты
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* Звуки при поднятии "малого" количества предметов. Не имеет никакого смысла без [smallStackLimit]
|
||||
*/
|
||||
val pickupSoundsSmall: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Звуки при поднятии "среднего" количества предметов. Не имеет никакого смысла без [mediumStackLimit]
|
||||
*/
|
||||
val pickupSoundsMedium: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Звуки при поднятии "большого" количества предметов. Не имеет никакого смысла без [smallStackLimit] и без [mediumStackLimit]
|
||||
*/
|
||||
val pickupSoundsLarge: List<String> = listOf(),
|
||||
|
||||
/**
|
||||
* Количество предметов ниже или равному данному значению проиграет звук [pickupSoundsSmall]
|
||||
*/
|
||||
val smallStackLimit: Long? = null,
|
||||
|
||||
/**
|
||||
* Количество предметов ниже или равному данному значению (но не меньше [smallStackLimit]) проиграет звук [pickupSoundsMedium]
|
||||
*/
|
||||
val mediumStackLimit: Long? = null,
|
||||
|
||||
/**
|
||||
* Превращает предмет в валюту
|
||||
*/
|
||||
val currency: String? = null,
|
||||
|
||||
/**
|
||||
* Ценность в [currency]
|
||||
*/
|
||||
val value: Long? = null,
|
||||
|
||||
// ----------------
|
||||
// /Валюта
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* Lua скрипты для выполнения
|
||||
*/
|
||||
val scripts: List<String> = listOf(),
|
||||
val animationScripts: List<String> = listOf(),
|
||||
|
||||
// ----------------
|
||||
// Броня
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* это где либо ещё применяется кроме брони?
|
||||
*/
|
||||
val tooltipKind: String? = null,
|
||||
|
||||
/**
|
||||
* Изначальный уровень, может быть изменён позднее чем угодно
|
||||
*/
|
||||
val level: Int? = null,
|
||||
|
||||
/**
|
||||
* Эффекты предмета, растущие с уровнем
|
||||
*/
|
||||
val leveledStatusEffects: List<StatusEffect> = listOf(),
|
||||
|
||||
/**
|
||||
* Варианты покраски (???)
|
||||
*/
|
||||
val colorOptions: List<Map<String, String>> = listOf(),
|
||||
|
||||
/**
|
||||
* Визуальные кадры анимации, когда надето на гуманоида мужского пола
|
||||
*/
|
||||
val maleFrames: ArmorFrames? = null,
|
||||
|
||||
/**
|
||||
* Визуальные кадры анимации, когда надето на гуманоида женского пола
|
||||
*/
|
||||
val femaleFrames: ArmorFrames? = null,
|
||||
|
||||
// ----------------
|
||||
// /Броня
|
||||
// ----------------
|
||||
|
||||
// ----------------
|
||||
// activeitem
|
||||
// ----------------
|
||||
|
||||
// TODO: это указатель на структуру
|
||||
val animation: String? = null,
|
||||
|
||||
/**
|
||||
* Занимает ли предмет обе руки
|
||||
*/
|
||||
val twoHanded: Boolean = false,
|
||||
|
||||
// ----------------
|
||||
// /activeitem
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* Прототип данного предмета, как JSON структура
|
||||
*
|
||||
* Имеет смысл только для Lua скриптов
|
||||
*/
|
||||
val json: Map<String, Any>,
|
||||
) {
|
||||
data class FossilSetDescription(
|
||||
val price: Long = 0L,
|
||||
val shortdescription: String = "...",
|
||||
val description: String = "..."
|
||||
)
|
||||
|
||||
data class ArmorFrames(
|
||||
val body: String,
|
||||
val backSleeve: String?,
|
||||
val frontSleeve: String?,
|
||||
)
|
||||
|
||||
data class StatusEffect(
|
||||
val levelFunction: String,
|
||||
val stat: String,
|
||||
val baseMultiplier: Double = 1.0,
|
||||
val amount: Double = 0.0,
|
||||
)
|
||||
|
||||
data class InventoryIcon(
|
||||
val image: SpriteReference
|
||||
)
|
||||
|
||||
companion object {
|
||||
val INVENTORY_ICON_ADAPTER = FactoryAdapter.Builder(InventoryIcon::class)
|
||||
.auto(InventoryIcon::image)
|
||||
.build()
|
||||
|
||||
val ADAPTER = FactoryAdapter.Builder(ItemDefinition::class)
|
||||
.auto(ItemDefinition::itemName)
|
||||
.auto(ItemDefinition::price)
|
||||
.auto(ItemDefinition::rarity)
|
||||
.auto(ItemDefinition::category)
|
||||
.add(ItemDefinition::inventoryIcon, ListAdapter(INVENTORY_ICON_ADAPTER).ifString { listOf(InventoryIcon(SpriteReference.parse(Starbound.readingFolderTransformer(it)))) }.nullSafe())
|
||||
.auto(ItemDefinition::description)
|
||||
.auto(ItemDefinition::shortdescription)
|
||||
|
||||
.autoList(ItemDefinition::itemTags)
|
||||
.autoList(ItemDefinition::learnBlueprintsOnPickup)
|
||||
|
||||
.auto(ItemDefinition::maxStack)
|
||||
.auto(ItemDefinition::eventCategory)
|
||||
.auto(ItemDefinition::consumeOnPickup)
|
||||
.autoList(ItemDefinition::pickupQuestTemplates)
|
||||
|
||||
.auto(ItemDefinition::race)
|
||||
.auto(ItemDefinition::displayImage, transformer = Starbound::readingFolderTransformerNullable)
|
||||
.auto(ItemDefinition::displayoffset)
|
||||
.auto(ItemDefinition::fossilSetName)
|
||||
.auto(ItemDefinition::setIndex)
|
||||
.auto(ItemDefinition::setCount)
|
||||
.mapAsObject(ItemDefinition::setCollectables, String::class)
|
||||
.auto(ItemDefinition::completeFossilIcon)
|
||||
.auto(ItemDefinition::completeFossilObject)
|
||||
|
||||
.auto(ItemDefinition::completeSetDescriptions)
|
||||
.autoList(ItemDefinition::radioMessagesOnPickup)
|
||||
.auto(ItemDefinition::fuelAmount)
|
||||
|
||||
.autoList(ItemDefinition::pickupSoundsSmall)
|
||||
.autoList(ItemDefinition::pickupSoundsMedium)
|
||||
.autoList(ItemDefinition::pickupSoundsLarge)
|
||||
.auto(ItemDefinition::smallStackLimit)
|
||||
.auto(ItemDefinition::mediumStackLimit)
|
||||
.auto(ItemDefinition::currency)
|
||||
.auto(ItemDefinition::value)
|
||||
|
||||
.autoList(ItemDefinition::scripts, transformer = Starbound::readingFolderListTransformer)
|
||||
.autoList(ItemDefinition::animationScripts, transformer = Starbound::readingFolderListTransformer)
|
||||
|
||||
.auto(ItemDefinition::tooltipKind)
|
||||
.auto(ItemDefinition::level)
|
||||
.autoList(ItemDefinition::leveledStatusEffects)
|
||||
.add(ItemDefinition::colorOptions, Starbound.nonnullStringTypeAdapter.asJsonObject().asList())
|
||||
.auto(ItemDefinition::maleFrames)
|
||||
.auto(ItemDefinition::femaleFrames)
|
||||
|
||||
.auto(ItemDefinition::animation, transformer = Starbound::readingFolderTransformerNullable)
|
||||
.auto(ItemDefinition::twoHanded)
|
||||
|
||||
.storesJson()
|
||||
|
||||
.build()
|
||||
|
||||
val FOSSIL_ADAPTER = FactoryAdapter.Builder(FossilSetDescription::class)
|
||||
.auto(FossilSetDescription::price)
|
||||
.auto(FossilSetDescription::shortdescription)
|
||||
.auto(FossilSetDescription::description)
|
||||
.build()
|
||||
|
||||
val ARMOR_FRAMES_ADAPTER = FactoryAdapter.Builder(ArmorFrames::class)
|
||||
.auto(ArmorFrames::body, transformer = Starbound::readingFolderTransformer)
|
||||
.auto(ArmorFrames::backSleeve, transformer = Starbound::readingFolderTransformerNullable)
|
||||
.auto(ArmorFrames::frontSleeve, transformer = Starbound::readingFolderTransformerNullable)
|
||||
.build()
|
||||
.ifString { ArmorFrames(Starbound.readingFolderTransformer(it), null, null) }
|
||||
|
||||
val STATUS_EFFECT_ADAPTER = FactoryAdapter.Builder(StatusEffect::class)
|
||||
.auto(StatusEffect::levelFunction)
|
||||
.auto(StatusEffect::stat)
|
||||
.auto(StatusEffect::baseMultiplier)
|
||||
.auto(StatusEffect::amount)
|
||||
.build()
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(FOSSIL_ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(ARMOR_FRAMES_ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(STATUS_EFFECT_ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(INVENTORY_ICON_ADAPTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
val json: Map<String, Any>
|
||||
) : IItemDefinition
|
||||
|
@ -0,0 +1,87 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.enrollMap
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.INativeJsonHolder
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
|
||||
open class ItemPrototype : IItemDefinition, INativeJsonHolder {
|
||||
final override var shortdescription: String = "..."
|
||||
final override var description: String = "..."
|
||||
final override var itemName: String by NotNullVar()
|
||||
final override var price: Long = 0L
|
||||
final override var rarity: ItemRarity = ItemRarity.COMMON
|
||||
final override var category: String? = null
|
||||
final override var inventoryIcon: List<IItemDefinition.InventoryIcon>? = null
|
||||
final override var itemTags: List<String> = listOf()
|
||||
final override var learnBlueprintsOnPickup: List<String> = listOf()
|
||||
final override var maxStack: Long = 9999L
|
||||
final override var eventCategory: String? = null
|
||||
final override var consumeOnPickup: Boolean = false
|
||||
final override var pickupQuestTemplates: List<String> = listOf()
|
||||
final override var scripts: List<String> = listOf()
|
||||
final override var tooltipKind: ItemTooltipKind = ItemTooltipKind.NORMAL
|
||||
final override var twoHanded: Boolean = false
|
||||
final override var radioMessagesOnPickup: List<String> = listOf()
|
||||
final override var fuelAmount: Long? = null
|
||||
|
||||
var json: Map<String, Any> = mapOf()
|
||||
|
||||
final override fun acceptJson(json: MutableMap<String, Any>) {
|
||||
this.json = json
|
||||
}
|
||||
|
||||
open fun assemble(): IItemDefinition {
|
||||
return ItemDefinition(
|
||||
shortdescription = shortdescription,
|
||||
description = description,
|
||||
itemName = itemName,
|
||||
price = price,
|
||||
rarity = rarity,
|
||||
category = category,
|
||||
inventoryIcon = inventoryIcon,
|
||||
itemTags = itemTags,
|
||||
learnBlueprintsOnPickup = learnBlueprintsOnPickup,
|
||||
maxStack = maxStack,
|
||||
eventCategory = eventCategory,
|
||||
consumeOnPickup = consumeOnPickup,
|
||||
pickupQuestTemplates = pickupQuestTemplates,
|
||||
scripts = scripts,
|
||||
tooltipKind = tooltipKind,
|
||||
twoHanded = twoHanded,
|
||||
radioMessagesOnPickup = radioMessagesOnPickup,
|
||||
fuelAmount = fuelAmount,
|
||||
|
||||
json = enrollMap(json),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER = BuilderAdapter.Builder(::ItemPrototype)
|
||||
.also(::addFields)
|
||||
.build()
|
||||
|
||||
fun addFields(builder: BuilderAdapter.Builder<ItemPrototype>) {
|
||||
with(builder) {
|
||||
auto(ItemPrototype::shortdescription)
|
||||
auto(ItemPrototype::description)
|
||||
auto(ItemPrototype::itemName)
|
||||
auto(ItemPrototype::price)
|
||||
auto(ItemPrototype::rarity)
|
||||
auto(ItemPrototype::category)
|
||||
autoNullableList(ItemPrototype::inventoryIcon)
|
||||
autoList(ItemPrototype::itemTags)
|
||||
autoList(ItemPrototype::learnBlueprintsOnPickup)
|
||||
auto(ItemPrototype::maxStack)
|
||||
auto(ItemPrototype::eventCategory)
|
||||
auto(ItemPrototype::consumeOnPickup)
|
||||
autoList(ItemPrototype::pickupQuestTemplates)
|
||||
autoList(ItemPrototype::scripts)
|
||||
auto(ItemPrototype::tooltipKind)
|
||||
auto(ItemPrototype::twoHanded)
|
||||
autoList(ItemPrototype::radioMessagesOnPickup)
|
||||
auto(ItemPrototype::fuelAmount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package ru.dbotthepony.kstarbound.defs.item
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
|
||||
@ -21,12 +21,4 @@ enum class ItemRarity(val canonical: String) : IStringSerializable {
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(canonical)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ADAPTER: TypeAdapter<ItemRarity> = CustomEnumTypeAdapter(values()).nullSafe()
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package ru.dbotthepony.kstarbound.defs.item
|
||||
|
||||
enum class ItemTooltipKind {
|
||||
/**
|
||||
* Обычные предметы
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* Улучшение для рюкзака
|
||||
*/
|
||||
BASE_AUGMENT,
|
||||
|
||||
/**
|
||||
* Рюкзаки
|
||||
*/
|
||||
BACK,
|
||||
|
||||
/**
|
||||
* Броня
|
||||
*/
|
||||
ARMOR,
|
||||
|
||||
/**
|
||||
* "Руки" меха
|
||||
*/
|
||||
MECH_ARM,
|
||||
|
||||
/**
|
||||
* "Ноги" меха
|
||||
*/
|
||||
MECH_LEGS,
|
||||
|
||||
/**
|
||||
* Ускорители меха
|
||||
*/
|
||||
MECH_BOOSTER,
|
||||
|
||||
/**
|
||||
* Тело меха
|
||||
*/
|
||||
MECH_BODY,
|
||||
}
|
@ -9,7 +9,7 @@ import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
@ -80,7 +80,7 @@ class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredPr
|
||||
lightColor = lightColor,
|
||||
onlyHitTerrain = onlyHitTerrain,
|
||||
orientationLocked = orientationLocked,
|
||||
image = ImageReference(Starbound.readingFolderTransformer(requireNotNull(image) { "image is null" })),
|
||||
image = ImageReference(Starbound.assetFolder(requireNotNull(image) { "image is null" })),
|
||||
timeToLive = timeToLive,
|
||||
animationCycle = animationCycle,
|
||||
bounces = bounces,
|
||||
@ -123,7 +123,7 @@ class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredPr
|
||||
|
||||
fun registerGson(gson: GsonBuilder) {
|
||||
gson.registerTypeAdapter(ADAPTER)
|
||||
gson.registerTypeAdapter(CustomEnumTypeAdapter(ProjectilePhysics.values()).nullSafe())
|
||||
gson.registerTypeAdapter(EnumAdapter(ProjectilePhysics::class).neverNull())
|
||||
gson.registerTypeAdapter(ActionConfig.ADAPTER)
|
||||
gson.registerTypeAdapter(ActionProjectile.ADAPTER)
|
||||
gson.registerTypeAdapter(ActionSound.ADAPTER)
|
||||
|
@ -3,7 +3,7 @@ package ru.dbotthepony.kstarbound.defs.projectile
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.io.json.IStringSerializable
|
||||
|
||||
enum class ProjectilePhysics(private vararg val aliases: String) : IStringSerializable {
|
||||
enum class ProjectilePhysics(vararg aliases: String) : IStringSerializable {
|
||||
GAS,
|
||||
LASER,
|
||||
BOOMERANG,
|
||||
@ -99,7 +99,12 @@ enum class ProjectilePhysics(private vararg val aliases: String) : IStringSerial
|
||||
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
|
||||
|
@ -17,7 +17,7 @@ data class RenderParameters(
|
||||
val absoluteTexturePath: String
|
||||
|
||||
init {
|
||||
val dir = Starbound.readingFolder
|
||||
val dir = Starbound.assetFolder
|
||||
|
||||
if (dir == null || texture[0] == '/') {
|
||||
absoluteTexturePath = texture
|
||||
|
@ -280,7 +280,7 @@ data class RenderTemplate(
|
||||
|
||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||
|
||||
gsonBuilder.registerTypeAdapter(RenderRuleList.Combination::class.java, EnumAdapter(RenderRuleList.Combination::class.java))
|
||||
gsonBuilder.registerTypeAdapter(EnumAdapter(RenderRuleList.Combination::class.java))
|
||||
}
|
||||
|
||||
private val cache = ConcurrentHashMap<String, RenderTemplate>()
|
||||
@ -300,7 +300,7 @@ data class RenderTemplate(
|
||||
if (path[0] != '/') {
|
||||
// относительный путь
|
||||
|
||||
val readingFolder = Starbound.readingFolder ?: throw NullPointerException("Currently read folder is not specified")
|
||||
val readingFolder = Starbound.assetFolder ?: throw NullPointerException("Currently read folder is not specified")
|
||||
path = "$readingFolder/$path"
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.io.ColorTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
@ -43,7 +43,7 @@ class SkyParameters {
|
||||
gsonBuilder.registerTypeAdapter(SkyParameters::class.java, ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(SkyColoringManifold::class.java, SkyColoringManifold.ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(SkyColoring::class.java, SkyColoring.ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(SkyType::class.java, CustomEnumTypeAdapter(SkyType.values()).nullSafe())
|
||||
gsonBuilder.registerTypeAdapter(SkyType::class.java, EnumAdapter(SkyType::class))
|
||||
SkySatellite.registerGson(gsonBuilder)
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package ru.dbotthepony.kstarbound.defs.world.dungeon
|
||||
import com.google.gson.GsonBuilder
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||
import ru.dbotthepony.kstarbound.defs.world.WorldProperties
|
||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class DungeonWorldDef {
|
||||
@ -50,8 +51,8 @@ class DungeonWorldDef {
|
||||
|
||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||
gsonBuilder.registerTypeAdapter(DungeonWorldDef::class.java, ADAPTER)
|
||||
gsonBuilder.registerTypeAdapter(BeamUpRule::class.java, CustomEnumTypeAdapter(BeamUpRule.values()).nullSafe())
|
||||
gsonBuilder.registerTypeAdapter(DungeonType::class.java, CustomEnumTypeAdapter(DungeonType.values()).nullSafe())
|
||||
gsonBuilder.registerTypeAdapter(EnumAdapter(BeamUpRule::class))
|
||||
gsonBuilder.registerTypeAdapter(EnumAdapter(DungeonType::class))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
reader = JsonTreeReader(obj)
|
||||
|
||||
if (instance is INativeJsonHolder) {
|
||||
instance.acceptJson(flattenJsonElement(obj.asJsonObject, Starbound.assetStringInterner::intern))
|
||||
instance.acceptJson(flattenJsonElement(obj.asJsonObject, Starbound.STRING_INTERNER::intern))
|
||||
} else {
|
||||
instance.acceptJson(obj.asJsonObject)
|
||||
}
|
||||
@ -121,12 +121,16 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
val peek = reader.peek()
|
||||
|
||||
if (!property.returnType.isMarkedNullable && peek == JsonToken.NULL) {
|
||||
throw NullPointerException("Property ${property.name} of ${instance::class.qualifiedName} does not accept nulls")
|
||||
throw NullPointerException("Property ${property.name} of ${instance::class.qualifiedName} does not accept nulls (JSON contains null)")
|
||||
} else if (peek == JsonToken.NULL) {
|
||||
property.set(instance, null)
|
||||
reader.nextNull()
|
||||
} else {
|
||||
val readValue = property.adapter.read(reader)
|
||||
|
||||
if (!property.returnType.isMarkedNullable && readValue == null)
|
||||
throw JsonSyntaxException("Property ${property.name} of ${instance::class.qualifiedName} does not accept nulls (Type provider returned null)")
|
||||
|
||||
property.set(instance, readValue)
|
||||
check(missing.remove(property))
|
||||
}
|
||||
@ -248,22 +252,39 @@ class BuilderAdapter<T : Any> private constructor(
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Автоматически определяет тип свойства и необходимый [TypeAdapter]
|
||||
*/
|
||||
fun <V> auto(property: KMutableProperty1<T, V>, configurator: PropertyConfigurator<T, V>.() -> Unit = {}): Builder<T> {
|
||||
val returnType = property.returnType
|
||||
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${property.name}!")
|
||||
|
||||
if (classifier.isSubclassOf(List::class)) {
|
||||
throw IllegalArgumentException("${property.name} is a List, please use autoList() method instead")
|
||||
throw IllegalArgumentException("${property.name} is a List, please use autoList() or directly specify type adapter method instead")
|
||||
}
|
||||
|
||||
if (classifier.isSubclassOf(Map::class)) {
|
||||
throw IllegalArgumentException("${property.name} is a Map, please use autoMap() method instead")
|
||||
throw IllegalArgumentException("${property.name} is a Map, please use autoMap() or directly specify type adapter method instead")
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast") // classifier.java не имеет обозначенного типа
|
||||
return add(property, LazyTypeProvider(classifier.java) as TypeAdapter<V>, configurator)
|
||||
}
|
||||
|
||||
/**
|
||||
* Автоматически создаёт [ListAdapter] для заданного свойства
|
||||
*/
|
||||
inline fun <reified V : Any> autoList(property: KMutableProperty1<T, List<V>>, noinline configurator: PropertyConfigurator<T, List<V>>.() -> Unit = {}): Builder<T> {
|
||||
return add(property, ListAdapter(V::class.java), configurator)
|
||||
}
|
||||
|
||||
/**
|
||||
* Автоматически создаёт [ListAdapter] для заданного свойства, но в данном случае само свойство может принимать значение null
|
||||
*/
|
||||
inline fun <reified V : Any> autoNullableList(property: KMutableProperty1<T, List<V>?>, noinline configurator: PropertyConfigurator<T, List<V>?>.() -> Unit = {}): Builder<T> {
|
||||
return add(property, ListAdapter(V::class.java).nullSafe(), configurator)
|
||||
}
|
||||
|
||||
fun ignoreKey(name: String): Builder<T> {
|
||||
if (properties.any { it.property.name == name }) {
|
||||
throw IllegalArgumentException("Can not ignore key $name because we have property with this name!")
|
||||
|
@ -1,31 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.io.json
|
||||
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
|
||||
interface IStringSerializable {
|
||||
fun match(name: String): Boolean
|
||||
fun write(out: JsonWriter)
|
||||
}
|
||||
|
||||
class CustomEnumTypeAdapter<T : Enum<T>>(private val clazz: Array<T>) : TypeAdapter<T>() {
|
||||
override fun write(out: JsonWriter, value: T) {
|
||||
if (value is IStringSerializable)
|
||||
value.write(out)
|
||||
else
|
||||
out.value(value.name)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): T {
|
||||
val str = `in`.nextString().uppercase()
|
||||
|
||||
for (value in clazz) {
|
||||
if (value is IStringSerializable && value.match(str) || value.name == str) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("${clazz[0]::class.qualifiedName} does not have value for $str")
|
||||
}
|
||||
}
|
@ -1,40 +1,133 @@
|
||||
package ru.dbotthepony.kstarbound.io.json
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.Streams
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import ru.dbotthepony.kstarbound.set
|
||||
import java.util.Arrays
|
||||
import java.util.stream.Stream
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSuperclassOf
|
||||
|
||||
class EnumAdapter<T : Enum<T>>(private val enum: Class<T>) : TypeAdapter<T>() {
|
||||
private val mapping: ImmutableMap<String, T> = Object2ObjectArrayMap<String, T>().let {
|
||||
for (value in enum.enumConstants) {
|
||||
it[value.name] = value
|
||||
it[value.name.uppercase()] = value
|
||||
it[value.name.lowercase()] = value
|
||||
interface IStringSerializable {
|
||||
fun match(name: String): Boolean
|
||||
fun write(out: JsonWriter)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
inline fun <reified T : Enum<T>>EnumAdapter(values: Stream<T> = Arrays.stream(T::class.java.enumConstants), default: T? = null): EnumAdapter<T> {
|
||||
return EnumAdapter(T::class, values, default)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
inline fun <reified T : Enum<T>>EnumAdapter(values: Iterator<T>, default: T? = null): EnumAdapter<T> {
|
||||
return EnumAdapter(T::class, Streams.stream(values), default)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
inline fun <reified T : Enum<T>>EnumAdapter(values: Array<out T>, default: T? = null): EnumAdapter<T> {
|
||||
return EnumAdapter(T::class, Arrays.stream(values), default)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
inline fun <reified T : Enum<T>>EnumAdapter(values: Collection<T>, default: T? = null): EnumAdapter<T> {
|
||||
return EnumAdapter(T::class, values.stream(), default)
|
||||
}
|
||||
|
||||
@Suppress("name_shadowing")
|
||||
class EnumAdapter<T : Enum<T>>(private val enum: KClass<T>, values: Stream<T> = Arrays.stream(enum.java.enumConstants), val default: T? = null) : TypeAdapter<T?>() {
|
||||
constructor(clazz: Class<T>, values: Stream<T> = Arrays.stream(clazz.enumConstants), default: T? = null) : this(clazz.kotlin, values, default)
|
||||
|
||||
constructor(clazz: Class<T>, values: Iterator<T>, default: T? = null) : this(clazz.kotlin, Streams.stream(values), default)
|
||||
constructor(clazz: Class<T>, values: Array<out T>, default: T? = null) : this(clazz.kotlin, Arrays.stream(values), default)
|
||||
constructor(clazz: Class<T>, values: Collection<T>, default: T? = null) : this(clazz.kotlin, values.stream(), default)
|
||||
|
||||
constructor(clazz: KClass<T>, values: Iterator<T>, default: T? = null) : this(clazz, Streams.stream(values), default)
|
||||
constructor(clazz: KClass<T>, values: Array<out T>, default: T? = null) : this(clazz, Arrays.stream(values), default)
|
||||
constructor(clazz: KClass<T>, values: Collection<T>, default: T? = null) : this(clazz, values.stream(), default)
|
||||
|
||||
private val values = values.collect(ImmutableList.toImmutableList())
|
||||
private val mapping: ImmutableMap<String, T>
|
||||
private val areCustom = IStringSerializable::class.isSuperclassOf(enum)
|
||||
|
||||
init {
|
||||
val builder = Object2ObjectArrayMap<String, T>()
|
||||
|
||||
for (value in this.values) {
|
||||
builder[value.name] = value
|
||||
builder[value.name.uppercase()] = value
|
||||
builder[value.name.lowercase()] = value
|
||||
|
||||
val spaced = value.name.replace('_', ' ')
|
||||
val stitched = value.name.replace("_", "")
|
||||
|
||||
it[spaced] = value
|
||||
it[spaced.uppercase()] = value
|
||||
it[spaced.lowercase()] = value
|
||||
builder[spaced] = value
|
||||
builder[spaced.uppercase()] = value
|
||||
builder[spaced.lowercase()] = value
|
||||
|
||||
it[stitched] = value
|
||||
it[stitched.uppercase()] = value
|
||||
it[stitched.lowercase()] = value
|
||||
builder[stitched] = value
|
||||
builder[stitched.uppercase()] = value
|
||||
builder[stitched.lowercase()] = value
|
||||
}
|
||||
|
||||
ImmutableMap.copyOf(it)
|
||||
mapping = ImmutableMap.copyOf(builder)
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter, value: T) {
|
||||
out.value(value.name)
|
||||
override fun write(out: JsonWriter, value: T?) {
|
||||
if (value == null) {
|
||||
out.nullValue()
|
||||
} else {
|
||||
out.value(value.name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): T {
|
||||
@Suppress("unchecked_cast")
|
||||
override fun read(`in`: JsonReader): T? {
|
||||
if (`in`.peek() == JsonToken.NULL) {
|
||||
return null
|
||||
}
|
||||
|
||||
val key = `in`.nextString()
|
||||
return mapping[key] ?: throw JsonSyntaxException("Unable to match '$key' against ${enum.canonicalName}")
|
||||
|
||||
if (areCustom) {
|
||||
for (value in values) {
|
||||
if ((value as IStringSerializable).match(key)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mapping[key] ?: default
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает [EnumAdapter] с тем же типом и набором [T], которому запрещено возвращать или принимать null'ы
|
||||
*/
|
||||
fun neverNull(): TypeAdapter<T> {
|
||||
return object : TypeAdapter<T>() {
|
||||
override fun write(out: JsonWriter, value: T) {
|
||||
return this@EnumAdapter.write(out, value)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): T {
|
||||
val key = `in`.nextString()
|
||||
|
||||
if (areCustom) {
|
||||
for (value in values) {
|
||||
if ((value as IStringSerializable).match(key)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mapping[key] ?: default ?: throw JsonSyntaxException("$key is not a valid ${enum.qualifiedName} value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.io.json
|
||||
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
@ -56,3 +57,18 @@ fun <T> TypeAdapter<T>.ifString(reader: (String) -> T): TypeAdapter<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> TypeAdapter<T?>.neverNull(): TypeAdapter<T> {
|
||||
return object : TypeAdapter<T>() {
|
||||
override fun write(out: JsonWriter, value: T) {
|
||||
this@neverNull.write(out, value)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): T {
|
||||
val path = `in`.path
|
||||
return this@neverNull.read(`in`) ?: throw JsonSyntaxException("Value was null near $path")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> TypeAdapter<T>.allowNull(): TypeAdapter<T?> = nullSafe()
|
||||
|
@ -188,7 +188,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
}
|
||||
|
||||
reader = JsonTreeReader(readArray)
|
||||
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, Starbound.assetStringInterner::intern)
|
||||
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, Starbound.STRING_INTERNER::intern)
|
||||
}
|
||||
|
||||
reader.beginArray()
|
||||
@ -233,7 +233,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
}
|
||||
|
||||
reader = JsonTreeReader(readMap)
|
||||
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, Starbound.assetStringInterner::intern)
|
||||
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, Starbound.STRING_INTERNER::intern)
|
||||
}
|
||||
|
||||
reader.beginObject()
|
||||
@ -357,6 +357,12 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
|
||||
*/
|
||||
class Builder<T : Any>(val clazz: KClass<T>) {
|
||||
constructor(clazz: KClass<T>, vararg fields: KProperty1<T, *>) : this(clazz) {
|
||||
for (field in fields) {
|
||||
auto(field)
|
||||
}
|
||||
}
|
||||
|
||||
private val types = ArrayList<PackedProperty<T, *>>()
|
||||
|
||||
/**
|
||||
@ -418,8 +424,8 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
*
|
||||
* Список неизменяем (создаётся объект [ImmutableList])
|
||||
*/
|
||||
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>, transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
|
||||
types.add(PackedProperty(field, ListAdapter(type).nullSafe(), transformer = transformer))
|
||||
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>, transformer: (V) -> V = { it }): Builder<T> {
|
||||
types.add(PackedProperty(field, ListAdapter(type, transformer).nullSafe()))
|
||||
return this
|
||||
}
|
||||
|
||||
@ -428,8 +434,8 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
*
|
||||
* Список неизменяем (создаётся объект [ImmutableList])
|
||||
*/
|
||||
inline fun <reified V : Any> autoList(field: KProperty1<T, List<V>?>, noinline transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
|
||||
return add(field, ListAdapter(V::class.java).nullSafe())
|
||||
inline fun <reified V : Any> autoList(field: KProperty1<T, List<V>?>, noinline transformer: (V) -> V = { it }): Builder<T> {
|
||||
return add(field, ListAdapter(V::class.java, transformer).nullSafe())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ class String2ObjectAdapter<T>(val adapter: TypeAdapter<T>, val valueTransformer:
|
||||
reader.beginObject()
|
||||
|
||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||
builder.put(Starbound.assetStringInterner.intern(reader.nextName()), valueTransformer(adapter.read(reader)))
|
||||
builder.put(Starbound.STRING_INTERNER.intern(reader.nextName()), valueTransformer(adapter.read(reader)))
|
||||
}
|
||||
|
||||
reader.endObject()
|
||||
|
@ -5,10 +5,11 @@ 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.item.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
|
||||
class ItemEntity(world: World<*, *>, val def: ItemDefinition) : Entity(world) {
|
||||
class ItemEntity(world: World<*, *>, val def: IItemDefinition) : Entity(world) {
|
||||
override val movement = object : MovementController<ItemEntity>(this) {
|
||||
override fun beginContact(contact: AbstractContact) {
|
||||
// тут надо код подбора предмета игроком, если мы начинаем коллизию с окружностью подбора
|
||||
|
Loading…
Reference in New Issue
Block a user