давайте снова попробуем builder'ов, но на этот раз с интерфейсами
This commit is contained in:
parent
78cdc2c886
commit
3da8450a2c
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound
|
package ru.dbotthepony.kstarbound
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import java.util.Arrays
|
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?) {
|
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T?) {
|
||||||
set(value)
|
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.*
|
||||||
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
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.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.liquid.LiquidDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
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.*
|
||||||
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.AABBiTypeAdapter
|
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.Vector2dTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||||
@ -57,24 +65,24 @@ object Starbound {
|
|||||||
/**
|
/**
|
||||||
* Служит переменной для указания из какой папки происходит чтение asset'а в данном потоке
|
* Служит переменной для указания из какой папки происходит чтение asset'а в данном потоке
|
||||||
*/
|
*/
|
||||||
var readingFolder by ThreadLocal<String>()
|
var assetFolder by ThreadLocal<String>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun readingFolderTransformer(input: String): String {
|
fun assetFolder(input: String): String {
|
||||||
val readingFolder = readingFolder
|
val assetFolder = assetFolder
|
||||||
require(readingFolder != null) { "Not reading an asset on current thread" }
|
require(assetFolder != null) { "Not reading an asset on current thread" }
|
||||||
|
|
||||||
if (input[0] == '/')
|
if (input[0] == '/')
|
||||||
return input
|
return input
|
||||||
|
|
||||||
return assetStringInterner.intern("$readingFolder/$input")
|
return STRING_INTERNER.intern("$assetFolder/$input")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readingFolderTransformerNullable(input: String?): String? {
|
fun assetFolderNullable(input: String?): String? {
|
||||||
require(readingFolder != null) { "Not reading an asset on current thread" }
|
require(assetFolder != null) { "Not reading an asset on current thread" }
|
||||||
|
|
||||||
if (input != null)
|
if (input != null)
|
||||||
return readingFolderTransformer(input)
|
return assetFolder(input)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -83,7 +91,7 @@ object Starbound {
|
|||||||
if (input == null)
|
if (input == null)
|
||||||
return 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>()
|
private val tiles = Object2ObjectOpenHashMap<String, TileDefinition>()
|
||||||
@ -99,7 +107,7 @@ object Starbound {
|
|||||||
private val parallax = Object2ObjectOpenHashMap<String, ParallaxPrototype>()
|
private val parallax = Object2ObjectOpenHashMap<String, ParallaxPrototype>()
|
||||||
private val functions = Object2ObjectOpenHashMap<String, JsonFunction>()
|
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 liquidAccess: Map<String, LiquidDefinition> = Collections.unmodifiableMap(liquid)
|
||||||
val liquidByIDAccess: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
|
val liquidByIDAccess: Map<Int, LiquidDefinition> = Collections.unmodifiableMap(liquidByID)
|
||||||
@ -110,21 +118,21 @@ object Starbound {
|
|||||||
val projectilesAccess: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
val projectilesAccess: Map<String, ConfiguredProjectile> = Collections.unmodifiableMap(projectiles)
|
||||||
val parallaxAccess: Map<String, ParallaxPrototype> = Collections.unmodifiableMap(parallax)
|
val parallaxAccess: Map<String, ParallaxPrototype> = Collections.unmodifiableMap(parallax)
|
||||||
val functionsAccess: Map<String, JsonFunction> = Collections.unmodifiableMap(functions)
|
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) {
|
override fun write(out: JsonWriter, value: String) {
|
||||||
out.value(value)
|
out.value(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): String {
|
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()
|
val gson: Gson = GsonBuilder()
|
||||||
.enableComplexMapKeySerialization()
|
.enableComplexMapKeySerialization()
|
||||||
@ -135,7 +143,7 @@ object Starbound {
|
|||||||
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe())
|
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe())
|
||||||
|
|
||||||
// чтоб строки всегда intern'ились
|
// чтоб строки всегда intern'ились
|
||||||
.registerTypeAdapter(stringTypeAdapter)
|
.registerTypeAdapter(NULLABLE_STRING_ADAPTER)
|
||||||
|
|
||||||
// math
|
// math
|
||||||
.registerTypeAdapter(AABBTypeAdapter)
|
.registerTypeAdapter(AABBTypeAdapter)
|
||||||
@ -156,12 +164,22 @@ object Starbound {
|
|||||||
.also(RenderTemplate::registerGson)
|
.also(RenderTemplate::registerGson)
|
||||||
.also(TileDefinition::registerGson)
|
.also(TileDefinition::registerGson)
|
||||||
.also(LiquidDefinition::registerGson)
|
.also(LiquidDefinition::registerGson)
|
||||||
.also(ItemDefinition::registerGson)
|
|
||||||
.also(ItemRarity::registerGson)
|
|
||||||
.also(SpriteReference::registerGson)
|
.also(SpriteReference::registerGson)
|
||||||
.also(AtlasConfiguration::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()
|
.create()
|
||||||
|
|
||||||
@ -170,7 +188,7 @@ object Starbound {
|
|||||||
return when (type) {
|
return when (type) {
|
||||||
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
||||||
Double::class.java -> TypeAdapters.DOUBLE 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>
|
Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T>
|
||||||
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
|
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
|
||||||
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
|
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
|
||||||
@ -339,14 +357,14 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadTileMaterials(callback: (String) -> Unit) {
|
private fun loadTileMaterials(callback: (String) -> Unit) {
|
||||||
readingFolder = "/tiles/materials"
|
assetFolder = "/tiles/materials"
|
||||||
|
|
||||||
for (fs in fileSystems) {
|
for (fs in fileSystems) {
|
||||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".material") }) {
|
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".material") }) {
|
||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val tileDef = gson.fromJson(listedFile.reader(), TileDefinition::class.java)
|
val tileDef = gson.fromJson(listedFile.reader(), TileDefinition::class.java)
|
||||||
|
|
||||||
check(tiles[tileDef.materialName] == null) { "Already has material with name ${tileDef.materialName} loaded!" }
|
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) {
|
private fun loadProjectiles(callback: (String) -> Unit) {
|
||||||
@ -373,7 +391,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val def = gson.fromJson(listedFile.reader(), ConfigurableProjectile::class.java).assemble(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!" }
|
check(projectiles[def.projectileName] == null) { "Already has projectile with ID ${def.projectileName} loaded!" }
|
||||||
projectiles[def.projectileName] = def
|
projectiles[def.projectileName] = def
|
||||||
@ -388,7 +406,7 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readingFolder = null
|
assetFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFunctions(callback: (String) -> Unit) {
|
private fun loadFunctions(callback: (String) -> Unit) {
|
||||||
@ -397,7 +415,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
||||||
|
|
||||||
for (key in readObject.keySet()) {
|
for (key in readObject.keySet()) {
|
||||||
@ -414,7 +432,7 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readingFolder = null
|
assetFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadParallax(callback: (String) -> Unit) {
|
private fun loadParallax(callback: (String) -> Unit) {
|
||||||
@ -423,7 +441,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val def = gson.fromJson(listedFile.reader(), ParallaxPrototype::class.java)
|
val def = gson.fromJson(listedFile.reader(), ParallaxPrototype::class.java)
|
||||||
parallax[listedFile.name.substringBefore('.')] = def
|
parallax[listedFile.name.substringBefore('.')] = def
|
||||||
} catch(err: Throwable) {
|
} catch(err: Throwable) {
|
||||||
@ -436,18 +454,18 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readingFolder = null
|
assetFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
||||||
readingFolder = "/tiles/materials"
|
assetFolder = "/tiles/materials"
|
||||||
|
|
||||||
for (fs in fileSystems) {
|
for (fs in fileSystems) {
|
||||||
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".matmod") }) {
|
for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".matmod") }) {
|
||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val tileDef = gson.fromJson(listedFile.reader(), MaterialModifier::class.java)
|
val tileDef = gson.fromJson(listedFile.reader(), MaterialModifier::class.java)
|
||||||
|
|
||||||
check(tileModifiers[tileDef.modName] == null) { "Already has material with name ${tileDef.modName} loaded!" }
|
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) {
|
private fun loadLiquidDefinitions(callback: (String) -> Unit) {
|
||||||
@ -474,7 +492,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
val liquidDef = gson.fromJson(listedFile.reader(), LiquidDefinition::class.java)
|
val liquidDef = gson.fromJson(listedFile.reader(), LiquidDefinition::class.java)
|
||||||
|
|
||||||
check(liquid.put(liquidDef.name, liquidDef) == null) { "Already has liquid with name ${liquidDef.name} loaded!" }
|
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) {
|
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 (fs in fileSystems) {
|
||||||
for (listedFile in fs.explore().filter { it.isFile }.filter { f -> files.any { f.name.endsWith(it) } }) {
|
for (listedFile in fs.explore().filter { it.isFile }.filter { f -> files.any { f.name.endsWith(it) } }) {
|
||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
readingFolder = listedFile.computeDirectory()
|
assetFolder = listedFile.computeDirectory()
|
||||||
ItemDefinition.ADAPTER.currentSymbolicName = listedFile.computeFullPath()
|
|
||||||
val def = gson.fromJson(listedFile.reader(), ItemDefinition::class.java)
|
|
||||||
|
|
||||||
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) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Loading item definition file $listedFile", err)
|
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
|
import com.google.common.collect.ImmutableMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
* Возвращает глубокую, неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||||
*/
|
*/
|
||||||
fun enrollList(input: List<Any>, interner: (String) -> String = String::intern): ImmutableList<Any> {
|
fun enrollList(input: List<Any>, interner: (String) -> String = String::intern): ImmutableList<Any> {
|
||||||
val builder = ImmutableList.builder<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> {
|
fun enrollMap(input: Map<String, Any>, interner: (String) -> String = String::intern): ImmutableMap<String, Any> {
|
||||||
val builder = ImmutableMap.builder<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.JsonReader
|
||||||
import com.google.gson.stream.JsonToken
|
import com.google.gson.stream.JsonToken
|
||||||
import com.google.gson.stream.JsonWriter
|
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.IStringSerializable
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
@ -29,7 +29,7 @@ enum class JsonFunctionInterpolation(vararg aliases: String) : IStringSerializab
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
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 {
|
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 {
|
abstract class RawPrototype<RAW : RawPrototype<RAW, ASSEMBLED>, ASSEMBLED : AssembledPrototype<ASSEMBLED, RAW>> : INativeJsonHolder {
|
||||||
val json = Object2ObjectArrayMap<String, Any>()
|
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
|
abstract fun assemble(directory: String = ""): ASSEMBLED
|
||||||
|
|
||||||
override fun acceptJson(json: MutableMap<String, Any>) {
|
override fun acceptJson(json: MutableMap<String, Any>) {
|
||||||
|
@ -304,7 +304,7 @@ class AtlasConfiguration private constructor(
|
|||||||
return EMPTY
|
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) {
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||||
|
@ -30,7 +30,7 @@ data class ImageReference(
|
|||||||
|
|
||||||
override fun read(`in`: JsonReader): ImageReference {
|
override fun read(`in`: JsonReader): ImageReference {
|
||||||
if (`in`.peek() == JsonToken.STRING) {
|
if (`in`.peek() == JsonToken.STRING) {
|
||||||
val image = Starbound.readingFolderTransformer(`in`.nextString())
|
val image = Starbound.assetFolder(`in`.nextString())
|
||||||
|
|
||||||
if (image.contains(':')) {
|
if (image.contains(':')) {
|
||||||
throw JsonSyntaxException("Expected atlas/image reference, but got sprite reference: $image")
|
throw JsonSyntaxException("Expected atlas/image reference, but got sprite reference: $image")
|
||||||
|
@ -30,7 +30,7 @@ data class SpriteReference(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): SpriteReference {
|
override fun read(`in`: JsonReader): SpriteReference {
|
||||||
return parse(Starbound.readingFolderTransformer(`in`.nextString()))
|
return parse(Starbound.assetFolder(`in`.nextString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
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
|
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(
|
data class ItemDefinition(
|
||||||
/**
|
override val shortdescription: String,
|
||||||
* Внутреннее имя предмета, как строка
|
override val description: String,
|
||||||
*/
|
override val itemName: String,
|
||||||
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 json: Map<String, Any>
|
||||||
* Цена в пикселях
|
) : IItemDefinition
|
||||||
*/
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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.GsonBuilder
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.stream.JsonWriter
|
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.IStringSerializable
|
||||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
|
|
||||||
@ -21,12 +21,4 @@ enum class ItemRarity(val canonical: String) : IStringSerializable {
|
|||||||
override fun write(out: JsonWriter) {
|
override fun write(out: JsonWriter) {
|
||||||
out.value(canonical)
|
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.*
|
||||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
||||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
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.registerTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.util.NotNullVar
|
import ru.dbotthepony.kstarbound.util.NotNullVar
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
@ -80,7 +80,7 @@ class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredPr
|
|||||||
lightColor = lightColor,
|
lightColor = lightColor,
|
||||||
onlyHitTerrain = onlyHitTerrain,
|
onlyHitTerrain = onlyHitTerrain,
|
||||||
orientationLocked = orientationLocked,
|
orientationLocked = orientationLocked,
|
||||||
image = ImageReference(Starbound.readingFolderTransformer(requireNotNull(image) { "image is null" })),
|
image = ImageReference(Starbound.assetFolder(requireNotNull(image) { "image is null" })),
|
||||||
timeToLive = timeToLive,
|
timeToLive = timeToLive,
|
||||||
animationCycle = animationCycle,
|
animationCycle = animationCycle,
|
||||||
bounces = bounces,
|
bounces = bounces,
|
||||||
@ -123,7 +123,7 @@ class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredPr
|
|||||||
|
|
||||||
fun registerGson(gson: GsonBuilder) {
|
fun registerGson(gson: GsonBuilder) {
|
||||||
gson.registerTypeAdapter(ADAPTER)
|
gson.registerTypeAdapter(ADAPTER)
|
||||||
gson.registerTypeAdapter(CustomEnumTypeAdapter(ProjectilePhysics.values()).nullSafe())
|
gson.registerTypeAdapter(EnumAdapter(ProjectilePhysics::class).neverNull())
|
||||||
gson.registerTypeAdapter(ActionConfig.ADAPTER)
|
gson.registerTypeAdapter(ActionConfig.ADAPTER)
|
||||||
gson.registerTypeAdapter(ActionProjectile.ADAPTER)
|
gson.registerTypeAdapter(ActionProjectile.ADAPTER)
|
||||||
gson.registerTypeAdapter(ActionSound.ADAPTER)
|
gson.registerTypeAdapter(ActionSound.ADAPTER)
|
||||||
|
@ -3,7 +3,7 @@ package ru.dbotthepony.kstarbound.defs.projectile
|
|||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import ru.dbotthepony.kstarbound.io.json.IStringSerializable
|
import ru.dbotthepony.kstarbound.io.json.IStringSerializable
|
||||||
|
|
||||||
enum class ProjectilePhysics(private vararg val aliases: String) : IStringSerializable {
|
enum class ProjectilePhysics(vararg aliases: String) : IStringSerializable {
|
||||||
GAS,
|
GAS,
|
||||||
LASER,
|
LASER,
|
||||||
BOOMERANG,
|
BOOMERANG,
|
||||||
@ -99,7 +99,12 @@ enum class ProjectilePhysics(private vararg val aliases: String) : IStringSerial
|
|||||||
GRENADE_LOW_BOUNCE("GRENADELOWBOUNCE"),
|
GRENADE_LOW_BOUNCE("GRENADELOWBOUNCE"),
|
||||||
GRENADE_NO_BOUNCE("GRENADENOBOUNCE");
|
GRENADE_NO_BOUNCE("GRENADENOBOUNCE");
|
||||||
|
|
||||||
|
private val aliases = Array(aliases.size) { aliases[it].lowercase() }
|
||||||
|
|
||||||
override fun match(name: String): Boolean {
|
override fun match(name: String): Boolean {
|
||||||
|
@Suppress("name_shadowing")
|
||||||
|
val name = name.lowercase()
|
||||||
|
|
||||||
for (alias in aliases)
|
for (alias in aliases)
|
||||||
if (name == alias)
|
if (name == alias)
|
||||||
return true
|
return true
|
||||||
|
@ -17,7 +17,7 @@ data class RenderParameters(
|
|||||||
val absoluteTexturePath: String
|
val absoluteTexturePath: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dir = Starbound.readingFolder
|
val dir = Starbound.assetFolder
|
||||||
|
|
||||||
if (dir == null || texture[0] == '/') {
|
if (dir == null || texture[0] == '/') {
|
||||||
absoluteTexturePath = texture
|
absoluteTexturePath = texture
|
||||||
|
@ -280,7 +280,7 @@ data class RenderTemplate(
|
|||||||
|
|
||||||
gsonBuilder.registerTypeAdapter(ADAPTER)
|
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>()
|
private val cache = ConcurrentHashMap<String, RenderTemplate>()
|
||||||
@ -300,7 +300,7 @@ data class RenderTemplate(
|
|||||||
if (path[0] != '/') {
|
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"
|
path = "$readingFolder/$path"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import com.google.gson.stream.JsonReader
|
|||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.io.ColorTypeAdapter
|
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.BuilderAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
@ -43,7 +43,7 @@ class SkyParameters {
|
|||||||
gsonBuilder.registerTypeAdapter(SkyParameters::class.java, ADAPTER)
|
gsonBuilder.registerTypeAdapter(SkyParameters::class.java, ADAPTER)
|
||||||
gsonBuilder.registerTypeAdapter(SkyColoringManifold::class.java, SkyColoringManifold.ADAPTER)
|
gsonBuilder.registerTypeAdapter(SkyColoringManifold::class.java, SkyColoringManifold.ADAPTER)
|
||||||
gsonBuilder.registerTypeAdapter(SkyColoring::class.java, SkyColoring.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)
|
SkySatellite.registerGson(gsonBuilder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ package ru.dbotthepony.kstarbound.defs.world.dungeon
|
|||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.world.WorldProperties
|
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.BuilderAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.EnumAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class DungeonWorldDef {
|
class DungeonWorldDef {
|
||||||
@ -50,8 +51,8 @@ class DungeonWorldDef {
|
|||||||
|
|
||||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
gsonBuilder.registerTypeAdapter(DungeonWorldDef::class.java, ADAPTER)
|
gsonBuilder.registerTypeAdapter(DungeonWorldDef::class.java, ADAPTER)
|
||||||
gsonBuilder.registerTypeAdapter(BeamUpRule::class.java, CustomEnumTypeAdapter(BeamUpRule.values()).nullSafe())
|
gsonBuilder.registerTypeAdapter(EnumAdapter(BeamUpRule::class))
|
||||||
gsonBuilder.registerTypeAdapter(DungeonType::class.java, CustomEnumTypeAdapter(DungeonType.values()).nullSafe())
|
gsonBuilder.registerTypeAdapter(EnumAdapter(DungeonType::class))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ class BuilderAdapter<T : Any> private constructor(
|
|||||||
reader = JsonTreeReader(obj)
|
reader = JsonTreeReader(obj)
|
||||||
|
|
||||||
if (instance is INativeJsonHolder) {
|
if (instance is INativeJsonHolder) {
|
||||||
instance.acceptJson(flattenJsonElement(obj.asJsonObject, Starbound.assetStringInterner::intern))
|
instance.acceptJson(flattenJsonElement(obj.asJsonObject, Starbound.STRING_INTERNER::intern))
|
||||||
} else {
|
} else {
|
||||||
instance.acceptJson(obj.asJsonObject)
|
instance.acceptJson(obj.asJsonObject)
|
||||||
}
|
}
|
||||||
@ -121,12 +121,16 @@ class BuilderAdapter<T : Any> private constructor(
|
|||||||
val peek = reader.peek()
|
val peek = reader.peek()
|
||||||
|
|
||||||
if (!property.returnType.isMarkedNullable && peek == JsonToken.NULL) {
|
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) {
|
} else if (peek == JsonToken.NULL) {
|
||||||
property.set(instance, null)
|
property.set(instance, null)
|
||||||
reader.nextNull()
|
reader.nextNull()
|
||||||
} else {
|
} else {
|
||||||
val readValue = property.adapter.read(reader)
|
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)
|
property.set(instance, readValue)
|
||||||
check(missing.remove(property))
|
check(missing.remove(property))
|
||||||
}
|
}
|
||||||
@ -248,22 +252,39 @@ class BuilderAdapter<T : Any> private constructor(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Автоматически определяет тип свойства и необходимый [TypeAdapter]
|
||||||
|
*/
|
||||||
fun <V> auto(property: KMutableProperty1<T, V>, configurator: PropertyConfigurator<T, V>.() -> Unit = {}): Builder<T> {
|
fun <V> auto(property: KMutableProperty1<T, V>, configurator: PropertyConfigurator<T, V>.() -> Unit = {}): Builder<T> {
|
||||||
val returnType = property.returnType
|
val returnType = property.returnType
|
||||||
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${property.name}!")
|
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${property.name}!")
|
||||||
|
|
||||||
if (classifier.isSubclassOf(List::class)) {
|
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)) {
|
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 не имеет обозначенного типа
|
@Suppress("unchecked_cast") // classifier.java не имеет обозначенного типа
|
||||||
return add(property, LazyTypeProvider(classifier.java) as TypeAdapter<V>, configurator)
|
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> {
|
fun ignoreKey(name: String): Builder<T> {
|
||||||
if (properties.any { it.property.name == name }) {
|
if (properties.any { it.property.name == name }) {
|
||||||
throw IllegalArgumentException("Can not ignore key $name because we have property with this 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
|
package ru.dbotthepony.kstarbound.io.json
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import com.google.common.collect.Streams
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonToken
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
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>() {
|
interface IStringSerializable {
|
||||||
private val mapping: ImmutableMap<String, T> = Object2ObjectArrayMap<String, T>().let {
|
fun match(name: String): Boolean
|
||||||
for (value in enum.enumConstants) {
|
fun write(out: JsonWriter)
|
||||||
it[value.name] = value
|
}
|
||||||
it[value.name.uppercase()] = value
|
|
||||||
it[value.name.lowercase()] = value
|
@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 spaced = value.name.replace('_', ' ')
|
||||||
val stitched = value.name.replace("_", "")
|
val stitched = value.name.replace("_", "")
|
||||||
|
|
||||||
it[spaced] = value
|
builder[spaced] = value
|
||||||
it[spaced.uppercase()] = value
|
builder[spaced.uppercase()] = value
|
||||||
it[spaced.lowercase()] = value
|
builder[spaced.lowercase()] = value
|
||||||
|
|
||||||
it[stitched] = value
|
builder[stitched] = value
|
||||||
it[stitched.uppercase()] = value
|
builder[stitched.uppercase()] = value
|
||||||
it[stitched.lowercase()] = value
|
builder[stitched.lowercase()] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableMap.copyOf(it)
|
mapping = ImmutableMap.copyOf(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: T) {
|
override fun write(out: JsonWriter, value: T?) {
|
||||||
out.value(value.name)
|
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()
|
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
|
package ru.dbotthepony.kstarbound.io.json
|
||||||
|
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonToken
|
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)
|
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()
|
reader.beginArray()
|
||||||
@ -233,7 +233,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader = JsonTreeReader(readMap)
|
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()
|
reader.beginObject()
|
||||||
@ -357,6 +357,12 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
|
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
|
||||||
*/
|
*/
|
||||||
class Builder<T : Any>(val clazz: KClass<T>) {
|
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, *>>()
|
private val types = ArrayList<PackedProperty<T, *>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -418,8 +424,8 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
*
|
*
|
||||||
* Список неизменяем (создаётся объект [ImmutableList])
|
* Список неизменяем (создаётся объект [ImmutableList])
|
||||||
*/
|
*/
|
||||||
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>, transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
|
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>, transformer: (V) -> V = { it }): Builder<T> {
|
||||||
types.add(PackedProperty(field, ListAdapter(type).nullSafe(), transformer = transformer))
|
types.add(PackedProperty(field, ListAdapter(type, transformer).nullSafe()))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,8 +434,8 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
*
|
*
|
||||||
* Список неизменяем (создаётся объект [ImmutableList])
|
* Список неизменяем (создаётся объект [ImmutableList])
|
||||||
*/
|
*/
|
||||||
inline fun <reified V : Any> autoList(field: KProperty1<T, List<V>?>, noinline transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
|
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).nullSafe())
|
return add(field, ListAdapter(V::class.java, transformer).nullSafe())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ class String2ObjectAdapter<T>(val adapter: TypeAdapter<T>, val valueTransformer:
|
|||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
|
|
||||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
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()
|
reader.endObject()
|
||||||
|
@ -5,10 +5,11 @@ import ru.dbotthepony.kbox2d.api.FixtureDef
|
|||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
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.defs.item.ItemDefinition
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
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 val movement = object : MovementController<ItemEntity>(this) {
|
||||||
override fun beginContact(contact: AbstractContact) {
|
override fun beginContact(contact: AbstractContact) {
|
||||||
// тут надо код подбора предмета игроком, если мы начинаем коллизию с окружностью подбора
|
// тут надо код подбора предмета игроком, если мы начинаем коллизию с окружностью подбора
|
||||||
|
Loading…
Reference in New Issue
Block a user