package ru.dbotthepony.kstarbound.defs import com.google.common.collect.ImmutableMap import com.github.benmanes.caffeine.cache.Interner import com.google.gson.Gson import com.google.gson.TypeAdapter import com.google.gson.TypeAdapterFactory import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter import ru.dbotthepony.kstarbound.io.json.builder.JsonImplementation @JsonImplementation(ThingDescription::class) 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 /** * Полное описание штуки для определённых рас * * Пример для Microwave Oven: * Apex: A type of oven. * Avian: A bizarre cooking device. * Floran: Floran likess raw meat, sssometimes cooked meat is good too. * Glitch: Irked. It is encrusted with spattered food, who left it in this state? * Human: A microwave. Gotta get me some jacket potatoes. * Hylotl: A strange, spinning oven. * Novakid: This lil' rotatin' oven cooks food at speed! * * Как это выглядит в JSON структуре: * ```json * { * ... * * "apexDescription" : "A type of oven.", * "avianDescription" : "A bizarre cooking device.", * "floranDescription" : "Floran likess raw meat, sssometimes cooked meat is good too.", * "glitchDescription" : "Irked. It is encrusted with spattered food, who left it in this state?", * "humanDescription" : "A microwave. Gotta get me some jacket potatoes.", * "hylotlDescription" : "A strange, spinning oven.", * "novakidDescription" : "This lil' rotatin' oven cooks food at speed!", * * ... * } * ``` * * Для получения описания для определённой расы необходимо знать ID расы, как показано выше (`apex`, `avian`, ...) */ val racialDescription: Map get() = mapOf() /** * Кратное описание штуки для определённых рас. В оригинальной игре не встречается */ val racialShortDescription: Map get() = mapOf() } data class ThingDescription( override val shortdescription: String = "...", override val description: String = "...", override val racialDescription: Map, override val racialShortDescription: Map, ) : IThingWithDescription { class Factory(val interner: Interner = Interner { it }) : TypeAdapterFactory { override fun create(gson: Gson, type: TypeToken): TypeAdapter? { if (type.rawType == ThingDescription::class.java) { return object : TypeAdapter() { override fun write(out: JsonWriter, value: ThingDescription?) { if (value == null) out.nullValue() else { out.beginObject() out.name("shortdescription") out.value(value.shortdescription) out.name("description") out.value(value.description) for ((k, v) in value.racialDescription) { out.name("${k}Description") out.value(v) } for ((k, v) in value.racialShortDescription) { out.name("${k}Shortdescription") out.value(v) } out.endObject() } } override fun read(`in`: JsonReader): ThingDescription? { if (`in`.peek() == JsonToken.NULL) return null `in`.beginObject() var shortdescription = "..." var description = "..." val racial = ImmutableMap.Builder() val racialShort = ImmutableMap.Builder() while (`in`.peek() !== JsonToken.END_OBJECT) { when (val name = `in`.nextName()) { "shortdescription" -> shortdescription = `in`.nextString() "description" -> description = `in`.nextString() else -> { try { if (name.endsWith("shortdescription") || name.endsWith("shortDescription") || name.endsWith("Shortdescription") || name.endsWith("ShortDescription")) { racialShort.put(interner.intern(name.substring(0, name.length - "shortdescription".length)), interner.intern(`in`.nextString())) } else if (name.endsWith("description") || name.endsWith("Description")) { racial.put(interner.intern(name.substring(0, name.length - "description".length)), interner.intern(`in`.nextString())) } else { `in`.skipValue() } } catch (_: IllegalStateException) { `in`.skipValue() } } } } `in`.endObject() return ThingDescription( shortdescription = shortdescription, description = description, racialDescription = racial.build(), racialShortDescription = racialShort.build() ) } } as TypeAdapter } return null } } }