From c57558af205ab05af70aca24ff4fd8d84c3e5567 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 4 Feb 2023 16:32:34 +0700 Subject: [PATCH] species --- .../ru/dbotthepony/kstarbound/Starbound.kt | 44 ++++++++--- .../kstarbound/StarboundJsonAdapters.kt | 5 ++ .../kstarbound/defs/ColorReplacements.kt | 61 +++++++++++++++ .../ru/dbotthepony/kstarbound/defs/Species.kt | 34 +++++++- .../defs/player/BlueprintLearnList.kt | 78 +++++++++++++++++++ .../defs/player/PlayerDefinition.kt | 10 +-- 6 files changed, 214 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/defs/ColorReplacements.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/BlueprintLearnList.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index fab92644..a1100dfb 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -124,6 +124,7 @@ object Starbound { private val projectiles = Object2ObjectOpenHashMap() private val parallax = Object2ObjectOpenHashMap() private val functions = Object2ObjectOpenHashMap() + private val species = Object2ObjectOpenHashMap() private val items = Object2ObjectOpenHashMap() @@ -141,17 +142,18 @@ object Starbound { val STRING_INTERNER: Interner = Interners.newWeakInterner() val STRING_ADAPTER: TypeAdapter = object : TypeAdapter() { - override fun write(out: JsonWriter, value: String) { - out.value(value) + override fun write(out: JsonWriter, value: String?) { + if (value == null) + out.nullValue() + else + out.value(value) } - override fun read(`in`: JsonReader): String { - return STRING_INTERNER.intern(TypeAdapters.STRING.read(`in`)) + override fun read(`in`: JsonReader): String? { + return STRING_INTERNER.intern(TypeAdapters.STRING.read(`in`) ?: return null) } } - val NULLABLE_STRING_ADAPTER: TypeAdapter = STRING_ADAPTER.nullSafe() - val GSON: Gson = GsonBuilder() .enableComplexMapKeySerialization() .serializeNulls() @@ -160,7 +162,7 @@ object Starbound { .setPrettyPrinting() // чтоб строки всегда intern'ились - .registerTypeAdapter(NULLABLE_STRING_ADAPTER) + .registerTypeAdapter(STRING_ADAPTER) // Обработчик @JsonImplementation .registerTypeAdapterFactory(JsonImplementationTypeFactory) @@ -190,6 +192,7 @@ object Starbound { .add(parallax::get) .add(functions::get) .add(items::get) + .add(species::get) ) .create() @@ -199,11 +202,11 @@ object Starbound { return when (type) { Float::class.java -> TypeAdapters.FLOAT as TypeAdapter Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter - String::class.java -> NULLABLE_STRING_ADAPTER as TypeAdapter + String::class.java -> STRING_ADAPTER as TypeAdapter Int::class.java -> TypeAdapters.INTEGER as TypeAdapter Long::class.java -> TypeAdapters.LONG as TypeAdapter Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter - else -> GSON.getAdapter(type) + else -> GSON.getAdapter(type) as TypeAdapter } } @@ -347,6 +350,7 @@ object Starbound { loadStage(callback, this::loadMaterialModifiers, "material modifier definitions") loadStage(callback, this::loadLiquidDefinitions, "liquid definitions") loadStage(callback, this::loadItemDefinitions, "item definitions") + loadStage(callback, this::loadSpecies, "species") assetFolder = "/" playerDefinition = GSON.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java) @@ -531,6 +535,28 @@ object Starbound { assetFolder = null } + private fun loadSpecies(callback: (String) -> Unit) { + for (fs in fileSystems) { + for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".species") }) { + try { + callback("Loading $listedFile") + + assetFolder = listedFile.computeDirectory() + val def = GSON.fromJson(listedFile.reader(), Species::class.java) + check(species.put(def.kind, def) == null) { "Already has liquid with name ${def.kind} loaded!" } + } catch (err: Throwable) { + LOGGER.error("Loading species definition file $listedFile", err) + } + + if (terminateLoading) { + return + } + } + } + + assetFolder = null + } + private fun loadItemDefinitions(callback: (String) -> Unit) { val files = linkedMapOf( ".item" to ItemPrototype::class.java, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt index 0035a702..56088941 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.kstarbound import com.google.gson.GsonBuilder +import ru.dbotthepony.kstarbound.defs.ColorReplacements import ru.dbotthepony.kstarbound.defs.DamageType import ru.dbotthepony.kstarbound.defs.JsonFunction import ru.dbotthepony.kstarbound.defs.MaterialReference @@ -13,6 +14,7 @@ import ru.dbotthepony.kstarbound.defs.item.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.LeveledStatusEffect import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototypeLayer +import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList import ru.dbotthepony.kstarbound.defs.projectile.ActionActions import ru.dbotthepony.kstarbound.defs.projectile.ActionConfig import ru.dbotthepony.kstarbound.defs.projectile.ActionLoop @@ -37,6 +39,9 @@ fun addStarboundJsonAdapters(builder: GsonBuilder) { with(builder) { registerTypeAdapterFactory(SBPattern.Companion) + registerTypeAdapter(ColorReplacements.Companion) + registerTypeAdapterFactory(BlueprintLearnList.Companion) + registerTypeAdapter(ColorTypeAdapter.nullSafe()) // математические классы diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/ColorReplacements.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/ColorReplacements.kt new file mode 100644 index 00000000..c1505a80 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/ColorReplacements.kt @@ -0,0 +1,61 @@ +package ru.dbotthepony.kstarbound.defs + +import com.google.gson.JsonSyntaxException +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap + +class ColorReplacements private constructor(private val mapping: Int2IntOpenHashMap) { + constructor(mapping: Map) : this(Int2IntOpenHashMap(mapping)) + + fun replace(color: Int): Int { + return mapping.getOrDefault(color, color) + } + + companion object : TypeAdapter() { + val EMPTY = ColorReplacements(Int2IntOpenHashMap()) + + override fun write(out: JsonWriter, value: ColorReplacements?) { + if (value == null) + out.nullValue() + else { + out.beginObject() + + for ((k, v) in value.mapping) { + out.name(k.toString(16)) + out.value(v.toString(16)) + } + + out.endObject() + } + } + + override fun read(`in`: JsonReader): ColorReplacements? { + if (`in`.peek() == JsonToken.NULL) + return null + else if (`in`.peek() == JsonToken.STRING) { + if (`in`.nextString() != "") + throw JsonSyntaxException("Invalid color replacement definition near ${`in`.path}") + + return ColorReplacements.EMPTY + } + + val mapping = Int2IntOpenHashMap() + + `in`.beginObject() + + while (`in`.peek() != JsonToken.END_OBJECT) { + val k = `in`.nextName() + val v = `in`.nextString() + + mapping[k.toInt(16)] = v.toInt(16) + } + + `in`.endObject() + + return ColorReplacements(mapping) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/Species.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/Species.kt index b03b6b5e..e501c41f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/Species.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/Species.kt @@ -1,6 +1,10 @@ package ru.dbotthepony.kstarbound.defs import com.google.common.collect.ImmutableList +import com.google.common.collect.ImmutableSet +import ru.dbotthepony.kstarbound.defs.image.SpriteReference +import ru.dbotthepony.kstarbound.defs.item.IItemDefinition +import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory @JsonFactory @@ -8,11 +12,35 @@ data class Species( val kind: String, val charCreationTooltip: Tooltip, val nameGen: ImmutableList, - val ouchNoises: OuchNoises, + val ouchNoises: ImmutableList, + val charGenTextLabels: ImmutableList, + val skull: String, + val defaultBlueprints: BlueprintLearnList, + val headOptionAsFacialhair: Boolean = false, + val altOptionAsUndyColor: Boolean = false, + val altOptionAsHairColor: Boolean = false, + val bodyColorAsFacialMaskSubColor: Boolean = false, + val hairColorAsBodySubColor: Boolean = false, + val bodyColor: ImmutableList, + val undyColor: ImmutableList, + val hairColor: ImmutableList, + val genders: ImmutableList, ) { @JsonFactory data class Tooltip(val title: String, val subTitle: String, val description: String) - @JsonFactory(asList = true) - data class OuchNoises(val male: String, val female: String) + @JsonFactory + data class Gender( + val name: String, + val image: SpriteReference, + val characterImage: SpriteReference, + val hairGroup: String? = null, + val hair: ImmutableSet, + val shirt: ImmutableSet>, + val pants: ImmutableSet>, + val facialHairGroup: String? = null, + val facialHair: ImmutableSet = ImmutableSet.of(), + val facialMaskGroup: String? = null, + val facialMask: ImmutableList = ImmutableList.of(), + ) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/BlueprintLearnList.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/BlueprintLearnList.kt new file mode 100644 index 00000000..2a404731 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/BlueprintLearnList.kt @@ -0,0 +1,78 @@ +package ru.dbotthepony.kstarbound.defs.player + +import com.google.common.collect.ImmutableList +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap +import ru.dbotthepony.kstarbound.defs.RegistryReference +import ru.dbotthepony.kstarbound.defs.item.IItemDefinition +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory + +class BlueprintLearnList private constructor(private val tiers: Int2ObjectArrayMap>) { + constructor(tiers: Map>) : this(Int2ObjectArrayMap>().also { for ((k, v) in tiers.entries) it.put(k, ImmutableList.copyOf(v)) }) + + @JsonFactory + data class Entry(val item: RegistryReference) + + operator fun get(tier: Int): List { + return tiers.getOrDefault(tier, ImmutableList.of()) + } + + companion object : TypeAdapterFactory { + private val token = TypeToken.getParameterized(ImmutableList::class.java, Entry::class.java) + + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (type.rawType == BlueprintLearnList::class.java) { + return object : TypeAdapter() { + private val listCodec = gson.getAdapter(token) as TypeAdapter> + + override fun write(out: JsonWriter, value: BlueprintLearnList?) { + if (value == null) + out.nullValue() + else { + out.beginObject() + + for ((tier, list) in value.tiers) { + out.name("tier$tier") + listCodec.write(out, list) + } + + out.endObject() + } + } + + override fun read(`in`: JsonReader): BlueprintLearnList? { + if (`in`.peek() == JsonToken.NULL) + return null + + val tiers = Int2ObjectArrayMap>() + + `in`.beginObject() + + while (`in`.peek() != JsonToken.END_OBJECT) { + val name = `in`.nextName() + + if (name.startsWith("tier")) { + val tier = name.substring(4).toIntOrNull() ?: throw JsonSyntaxException("Invalid tier: $name") + tiers[tier] = listCodec.read(`in`) + } else { + throw JsonSyntaxException("Invalid tier: $name") + } + } + + `in`.endObject() + return BlueprintLearnList(tiers) + } + } as TypeAdapter + } + + return null + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/PlayerDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/PlayerDefinition.kt index 063dba22..91f747ab 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/PlayerDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/player/PlayerDefinition.kt @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableSet import com.google.gson.JsonObject import ru.dbotthepony.kstarbound.defs.RegistryReference +import ru.dbotthepony.kstarbound.defs.Species import ru.dbotthepony.kstarbound.util.SBPattern import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition import ru.dbotthepony.kstarbound.defs.item.IItemDefinition @@ -20,12 +21,12 @@ data class PlayerDefinition( val blueprintAlreadyKnown: SBPattern, val collectableUnlock: SBPattern, - val species: ImmutableSet, + val species: ImmutableSet>, val nametagColor: Color, val ageItemsEvery: Int, val defaultItems: ImmutableSet>, - val defaultBlueprints: ImmutableMap>, + val defaultBlueprints: BlueprintLearnList, val defaultCodexes: ImmutableMap>>, val metaBoundBox: AABB, @@ -72,7 +73,4 @@ data class PlayerDefinition( val genericScriptContexts: ImmutableMap, val inventory: InventoryConfig, val inventoryFilters: ImmutableMap, -) { - @JsonFactory - data class BlueprintUnlock(val item: RegistryReference) -} +)