JSON Описание игрока

This commit is contained in:
DBotThePony 2023-02-04 14:47:26 +07:00
parent 1fd4ed6257
commit 7fc4d225a2
Signed by: DBot
GPG Key ID: DCC23B5715498507
28 changed files with 771 additions and 169 deletions

View File

@ -38,6 +38,7 @@ import ru.dbotthepony.kstarbound.defs.item.LiquidItemPrototype
import ru.dbotthepony.kstarbound.defs.item.MaterialItemPrototype
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype
import ru.dbotthepony.kstarbound.defs.player.PlayerDefinition
import ru.dbotthepony.kstarbound.defs.projectile.*
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
@ -59,6 +60,7 @@ import ru.dbotthepony.kstarbound.io.json.builder.JsonImplementationTypeFactory
import ru.dbotthepony.kstarbound.io.json.factory.ArrayListAdapterFactory
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.math.*
import ru.dbotthepony.kstarbound.util.WriteOnce
import java.io.*
import java.text.DateFormat
import java.util.*
@ -180,6 +182,16 @@ object Starbound {
.also(::addStarboundJsonAdapters)
.registerTypeAdapterFactory(RegistryReferenceFactory()
.add(tiles::get)
.add(tileModifiers::get)
.add(liquid::get)
.add(projectiles::get)
.add(parallax::get)
.add(functions::get)
.add(items::get)
)
.create()
@Suppress("unchecked_cast")
@ -278,6 +290,9 @@ object Starbound {
fun getTileDefinition(name: String) = tiles[name]
private val initCallbacks = ArrayList<() -> Unit>()
var playerDefinition: PlayerDefinition by WriteOnce()
private set
private fun loadStage(
callback: (Boolean, Boolean, String) -> Unit,
loader: ((String) -> Unit) -> Unit,
@ -333,6 +348,10 @@ object Starbound {
loadStage(callback, this::loadLiquidDefinitions, "liquid definitions")
loadStage(callback, this::loadItemDefinitions, "item definitions")
assetFolder = "/"
playerDefinition = GSON.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
assetFolder = null
initializing = false
initialized = true
callback(true, false, "Finished loading in ${System.currentTimeMillis() - time}ms")

View File

@ -4,21 +4,13 @@ import com.google.gson.GsonBuilder
import ru.dbotthepony.kstarbound.defs.DamageType
import ru.dbotthepony.kstarbound.defs.JsonFunction
import ru.dbotthepony.kstarbound.defs.MaterialReference
import ru.dbotthepony.kstarbound.defs.SBPattern
import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.defs.item.ArmorItemPrototype
import ru.dbotthepony.kstarbound.defs.item.CurrencyItemPrototype
import ru.dbotthepony.kstarbound.defs.item.IArmorItemDefinition
import ru.dbotthepony.kstarbound.defs.item.IFossilItemDefinition
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
import ru.dbotthepony.kstarbound.defs.item.ItemPrototype
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
import ru.dbotthepony.kstarbound.defs.item.ItemTooltipKind
import ru.dbotthepony.kstarbound.defs.item.LeveledStatusEffect
import ru.dbotthepony.kstarbound.defs.item.LiquidItemPrototype
import ru.dbotthepony.kstarbound.defs.item.MaterialItemPrototype
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype
import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototypeLayer
import ru.dbotthepony.kstarbound.defs.projectile.ActionActions
@ -26,23 +18,10 @@ import ru.dbotthepony.kstarbound.defs.projectile.ActionConfig
import ru.dbotthepony.kstarbound.defs.projectile.ActionLoop
import ru.dbotthepony.kstarbound.defs.projectile.ActionProjectile
import ru.dbotthepony.kstarbound.defs.projectile.ActionSound
import ru.dbotthepony.kstarbound.defs.projectile.ConfigurableProjectile
import ru.dbotthepony.kstarbound.defs.projectile.ProjectilePhysics
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.RenderMatch
import ru.dbotthepony.kstarbound.defs.tile.RenderMatchList
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
import ru.dbotthepony.kstarbound.defs.tile.RenderPiece
import ru.dbotthepony.kstarbound.defs.tile.RenderRuleList
import ru.dbotthepony.kstarbound.defs.tile.RenderTemplate
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.defs.world.SkyColoring
import ru.dbotthepony.kstarbound.defs.world.SkyColoringManifold
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
import ru.dbotthepony.kstarbound.defs.world.SkySatellite
import ru.dbotthepony.kstarbound.defs.world.SkyType
import ru.dbotthepony.kstarbound.defs.world.dungeon.BeamUpRule
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonType
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
import ru.dbotthepony.kstarbound.io.ColorTypeAdapter
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
@ -52,12 +31,12 @@ import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.builder.EnumAdapter
import ru.dbotthepony.kstarbound.io.json.factory.ArrayListAdapterFactory
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.math.PolyTypeAdapter
fun addStarboundJsonAdapters(builder: GsonBuilder) {
with(builder) {
registerTypeAdapterFactory(SBPattern.Companion)
registerTypeAdapter(ColorTypeAdapter.nullSafe())
// математические классы

View File

@ -46,14 +46,14 @@ class BoundSprite(
* Создаёт связку текстуры-атласа + координгат спрайта на ней
*/
fun SpriteReference.bind(texture: GLTexture2D): BoundSprite {
return BoundSprite(sprite, texture)
return BoundSprite(stateless, texture)
}
/**
* Создаёт связку текстуры-атласа, которая загружается через [GLStateTracker.loadNamedTextureSafe] + координгат спрайта на ней
*/
fun SpriteReference.bind(state: GLStateTracker): BoundSprite {
return BoundSprite(sprite, state.loadNamedTextureSafe(image))
return BoundSprite(stateless, state.loadNamedTextureSafe(image))
}
/**

View File

@ -0,0 +1,13 @@
package ru.dbotthepony.kstarbound.defs
interface IScriptable {
/**
* Lua скрипты для выполнения
*/
val scripts: List<String>
/**
* Через какое количество тиков вызывать обновления скриптов
*/
val scriptDelta: Int
}

View File

@ -0,0 +1,74 @@
package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.Interners
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.JsonWriter
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
import java.lang.ref.Reference
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
import java.lang.reflect.ParameterizedType
import java.util.concurrent.ConcurrentHashMap
import java.util.function.Supplier
class RegistryReferenceFactory : TypeAdapterFactory {
private val types = Reference2ObjectArrayMap<Class<*>, (String) -> Nothing?>()
private var isLenient = false
fun lenient(): RegistryReferenceFactory {
isLenient = true
return this
}
fun <T> add(clazz: Class<T>, resolver: (String) -> T?): RegistryReferenceFactory {
check(types.put(clazz, resolver as (String) -> Nothing?) == null) { "Already has resolver for class $clazz!" }
return this
}
inline fun <reified T> add(noinline resolver: (String) -> T?) = add(T::class.java, resolver)
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == RegistryReference::class.java) {
val ptype = type.type as? ParameterizedType ?: return null
val registryType = ptype.actualTypeArguments[0]
val resolver = types[registryType] ?: return if (isLenient) null else throw NoSuchElementException("Can't deserialize registry reference with type $registryType!")
return RegistryReferenceTypeAdapter(resolver, gson.getAdapter(String::class.java)) as TypeAdapter<T>
}
return null
}
}
class RegistryReferenceTypeAdapter<T>(val resolver: (String) -> T?, val strings: TypeAdapter<String>) : TypeAdapter<RegistryReference<T>>() {
override fun write(out: JsonWriter, value: RegistryReference<T>) {
strings.write(out, value.name)
}
override fun read(`in`: JsonReader): RegistryReference<T> {
return RegistryReference(strings.read(`in`) ?: throw JsonSyntaxException("Can't have null as registry name"), resolver)
}
}
data class RegistryReference<T>(val name: String, val resolver: (String) -> T?) : Supplier<T?>, () -> T?, Lazy<T?> {
private val lazy = lazy { resolver.invoke(name) }
override fun get(): T? {
return lazy.value
}
override val value: T?
get() = lazy.value
override fun isInitialized(): Boolean {
return lazy.isInitialized()
}
override fun invoke(): T? {
return lazy.value
}
}

View File

@ -0,0 +1,150 @@
package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
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.JsonWriter
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
/**
* Шаблонизировання строка в стиле Starbound'а
*
* Представляет из себя строки вида:
* * `my.thing`
* * `frame_<FrameNumber>`
* * `<frame><effectDirectives>`
*/
class SBPattern private constructor(
val raw: String,
val params: ImmutableMap<String, String>,
val pieces: ImmutableList<Piece>,
val names: ImmutableSet<String>
) {
val value by lazy { resolve { null } }
val hasNames get() = names.isNotEmpty()
override fun toString(): String {
return "SBPattern[$raw]"
}
override fun equals(other: Any?): Boolean {
return other is SBPattern && other.raw == raw && other.names == names && other.pieces == pieces && other.params == params
}
@Volatile
private var calculatedHash = false
@Volatile
private var hash = 0
override fun hashCode(): Int {
if (!calculatedHash) {
hash = raw.hashCode().xor(params.hashCode()).rotateLeft(12).and(pieces.hashCode()).rotateRight(8).xor(names.hashCode())
calculatedHash = true
}
return hash
}
fun resolve(values: (String) -> String?): String? {
val buffer = ArrayList<String>(pieces.size)
for (piece in pieces) {
buffer.add(piece.resolve(values, params::get) ?: return null)
}
var count = 0
for (piece in buffer) count += piece.length
val builder = StringBuilder(count)
for (piece in buffer) builder.append(piece)
return String(builder)
}
fun resolve(values: Map<String, String>): String? {
return resolve(values::get)
}
fun with(params: Map<String, String>): SBPattern {
val map = Object2ObjectArrayMap<String, String>()
map.putAll(this.params)
for ((key, value) in params.entries)
if (names.contains(key))
map[key] = value
return SBPattern(raw, ImmutableMap.copyOf(map), pieces, names)
}
data class Piece(val name: String? = null, val contents: String? = null) {
init {
check(name != null || contents != null) { "Both name and contents are null" }
check(!(name != null && contents != null)) { "Both name and contents are not null" }
}
fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? {
return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!)
}
fun resolve(map0: (String) -> String?): String? {
return contents ?: map0.invoke(name!!)
}
fun resolve(): String? {
return contents
}
}
companion object : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == SBPattern::class.java) {
return object : TypeAdapter<SBPattern>() {
private val strings = gson.getAdapter(String::class.java)
override fun write(out: JsonWriter, value: SBPattern?) {
strings.write(out, value?.raw)
}
override fun read(`in`: JsonReader): SBPattern? {
return of(strings.read(`in`) ?: return null)
}
} as TypeAdapter<T>
}
return null
}
@JvmStatic
fun of(raw: String): SBPattern {
val pieces = ImmutableList.Builder<Piece>()
var i = 0
while (i < raw.length) {
val open = raw.indexOf('<', startIndex = i)
if (open == -1) {
pieces.add(Piece(contents = raw.substring(i)))
break
} else {
val closing = raw.indexOf('>', startIndex = open + 1)
if (closing == -1) {
throw IllegalArgumentException("Malformed pattern string: $raw")
}
pieces.add(Piece(name = raw.substring(open + 1, closing - 1)))
i = closing + 1
}
}
val built = pieces.build()
return SBPattern(raw, pieces = built, params = ImmutableMap.of(), names = built.stream().map { it.name }.filter { it != null }.collect(ImmutableSet.toImmutableSet()))
}
@JvmStatic
fun raw(raw: String): SBPattern = SBPattern(raw, ImmutableMap.of(), ImmutableList.of(), ImmutableSet.of())
}
}

View File

@ -0,0 +1,18 @@
package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class Species(
val kind: String,
val charCreationTooltip: Tooltip,
val nameGen: ImmutableList<String>,
val ouchNoises: OuchNoises,
) {
@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)
}

View File

@ -0,0 +1,90 @@
package ru.dbotthepony.kstarbound.defs.animation
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@JsonFactory(asReference = true)
data class AnimationDefinition(
val animatedParts: AnimatedParts,
val sounds: ImmutableMap<String, Sound> = ImmutableMap.of(),
val transformationGroups: ImmutableMap<String, TransformConfig> = ImmutableMap.of(),
) {
@JsonFactory
data class TransformConfig(
val interpolated: Boolean? = null
)
@JsonFactory
data class Sound(
// TODO
val sound: String? = null
)
@JsonFactory
data class ParticleEmitter(
val transformationGroups: ImmutableList<String> = ImmutableList.of(),
val particles: ImmutableList<ParticleDefinition>
)
@JsonFactory
data class AnimatedParts(
val stateTypes: ImmutableMap<String, StateType>,
val parts: ImmutableMap<String, Part>,
) {
@JsonFactory
data class StateType(
val default: String,
val states: ImmutableMap<String, State> = ImmutableMap.of(),
) {
@JsonFactory
data class State(
val frames: Int = 0,
val cycle: Double = 0.0,
val mode: Mode? = null,
val transition: TransitionType? = null,
val properties: Properties = Properties()
) {
enum class TransitionType {
NONE
}
enum class Mode {
TRANSITION,
LOOP,
}
@JsonFactory
data class Properties(
val immediateSound: String? = null
)
}
}
@JsonFactory
data class Part(
val properties: Properties = Properties.EMPTY,
val partStates: ImmutableMap<String, ImmutableMap<String, State>> = ImmutableMap.of(),
) {
@JsonFactory
data class Properties(
val fullbright: Boolean? = null,
val centered: Boolean? = null,
val transformationGroups: ImmutableList<String>? = null,
val offset: Vector2d? = null,
val image: SpriteReference? = null
) {
companion object {
val EMPTY = Properties()
}
}
@JsonFactory
data class State(
val properties: Properties = Properties.EMPTY
)
}
}
}

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.kstarbound.defs.animation
enum class DestructionAction {
FADE
}

View File

@ -0,0 +1,46 @@
package ru.dbotthepony.kstarbound.defs.animation
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.ndouble.Vector4d
@JsonFactory
data class ParticleDefinition(
val count: Int = 1,
val offset: Vector2d = Vector2d.ZERO,
val offsetRegion: Vector4d = Vector4d.ZERO,
val particles: ImmutableList<Config>,
) {
@JsonFactory
data class Config(
val type: ParticleType,
val animation: AnimationDefinition? = null,
val image: SpriteReference? = null,
val position: Vector2d = Vector2d.ZERO,
val offsetRegion: Vector4d = Vector4d.ZERO,
val initialVelocity: Vector2d = Vector2d.ZERO,
val finalVelocity: Vector2d = Vector2d.ZERO,
val approach: Vector2d = Vector2d.ZERO,
val angularVelocity: Double = 0.0,
val destructionAction: DestructionAction,
val destructionTime: Double,
val fade: Double = 0.0,
val size: Double = 1.0,
val layer: ParticleLayer,
val timeToLive: Double = 1.0,
val variance: Variance = Variance.EMPTY
)
@JsonFactory
data class Variance(
val initialVelocity: Vector2d = Vector2d.ZERO,
val position: Vector2d = Vector2d.ZERO,
val angularVelocity: Double = 0.0
) {
companion object {
val EMPTY = Variance()
}
}
}

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.kstarbound.defs.animation
enum class ParticleLayer {
FRONT
}

View File

@ -0,0 +1,6 @@
package ru.dbotthepony.kstarbound.defs.animation
enum class ParticleType {
ANIMATED,
TEXTURED
}

View File

@ -1,32 +1,39 @@
package ru.dbotthepony.kstarbound.defs.image
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.registerTypeAdapter
import ru.dbotthepony.kstarbound.defs.SBPattern
/**
* Хранит данные (пару) вида "/example/image.png:sprite.name"
*/
data class SpriteReference(
val image: String,
val sprite: AtlasConfiguration.Sprite
val atlas: AtlasConfiguration,
val sprite: SBPattern
) {
val stateless by lazy { resolve { null } }
fun resolve(with: (String) -> String?): AtlasConfiguration.Sprite {
val resolved = sprite.resolve(with) ?: return atlas.any()
return atlas[resolved] ?: atlas.any()
}
companion object : TypeAdapter<SpriteReference>() {
fun parse(input: String): SpriteReference {
val grid = AtlasConfiguration.get(input.substringBefore(':'))
return when (input.count { it == ':' }) {
0 -> SpriteReference(input, grid.any())
1 -> SpriteReference(input.substringBefore(':'), grid.get(input.substringAfter(':')) ?: throw NoSuchElementException("No such sprite with name ${input.substringAfter(':')} present in frame grid ${grid.name} (atlas ${input.substringBefore(':')})"))
0 -> SpriteReference(input, grid, SBPattern.raw(grid.any().name))
1 -> SpriteReference(input.substringBefore(':'), grid, SBPattern.of(input.substringAfter(':')))
else -> throw IllegalArgumentException("Invalid sprite reference: $input")
}
}
override fun write(out: JsonWriter, value: SpriteReference) {
out.value(value.image + ":" + value.sprite.name)
out.value(value.image + ":" + value.sprite.raw)
}
override fun read(`in`: JsonReader): SpriteReference {

View File

@ -1,13 +1,5 @@
package ru.dbotthepony.kstarbound.defs.item
interface IScriptableItemDefinition : IItemDefinition {
/**
* Lua скрипты для выполнения
*/
val scripts: List<String>
import ru.dbotthepony.kstarbound.defs.IScriptable
/**
* Через какое количество тиков вызывать обновления скриптов
*/
val scriptDelta: Int
}
interface IScriptableItemDefinition : IItemDefinition, IScriptable

View File

@ -0,0 +1,26 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.defs.IScriptable
import ru.dbotthepony.kstarbound.defs.SBPattern
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class CompanionsConfig(
override val scripts: ImmutableList<String>,
override val scriptDelta: Int,
val activePodLimit: Int,
val activeCrewLimit: Int,
val crewLimit: Int,
val recruitDescription: SBPattern,
val crewBenefits: ImmutableMap<String, ImmutableList<String>>,
val shipUpgradeDiminishingReturns: Double,
val uniformSlots: ImmutableMap<String, ImmutableList<String>>,
) : IScriptable

View File

@ -0,0 +1,24 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.defs.IScriptable
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class DeploymentConfig(
override val scripts: ImmutableList<String>,
override val scriptDelta: Int,
val starterMechSet: ImmutableMap<String, RegistryReference<IItemDefinition>>,
val speciesStarterMechBody: ImmutableMap<String, RegistryReference<IItemDefinition>>,
val enemyDetectRadius: Double,
val enemyDetectTypeNames: ImmutableList<String>,
val lowEnergyThreshold: Double,
val lowEnergyFlashTime: Double,
val lowEnergySound: String,
) : IScriptable

View File

@ -0,0 +1,18 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class InventoryConfig(
val customBarGroups: Int,
val customBarIndexes: Int,
val itemBags: ImmutableMap<String, BagConfig>,
) {
@JsonFactory
data class BagConfig(
val priority: Int,
val size: Int,
)
}

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class InventoryFilterConfig(
val typeBlacklist: ImmutableSet<String>? = null,
val typeWhitelist: ImmutableSet<String>? = null,
val tagBlacklist: ImmutableSet<String>? = null,
val categoryBlacklist: ImmutableSet<String>? = null,
) {
fun acceptsType(type: String): Boolean {
if (typeBlacklist != null) {
return !typeBlacklist.contains(type)
} else if (typeWhitelist != null) {
return typeWhitelist.contains(type)
}
return true
}
}

View File

@ -0,0 +1,10 @@
package ru.dbotthepony.kstarbound.defs.player
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class JumpProfile(
val jumpSpeed: Double,
val jumpInitialPercentage: Double,
val jumpHoldTime: Double,
)

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@JsonFactory
data class MatterManipulatorConfig(
val image: String,
val endImages: ImmutableList<String> = ImmutableList.of(),
val handPosition: Vector2d,
val firePosition: Vector2d,
val segmentsPerUnit: Int,
val nearControlPointElasticity: Double,
val farControlPointElasticity: Double,
val nearControlPointDistance: Double,
val targetSegmentRun: Int,
val innerBrightnessScale: Double,
val firstStripeThickness: Double,
val secondStripeThickness: Double,
val minBeamWidth: Double,
val maxBeamWidth: Double,
val maxBeamJitter: Double,
val minBeamJitter: Double,
val minBeamTrans: Double,
val maxBeamTrans: Double,
val minBeamLines: Double,
val maxBeamLines: Double,
)

View File

@ -0,0 +1,11 @@
package ru.dbotthepony.kstarbound.defs.player
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class MovementParameters(
val flySpeed: Double? = null,
val airFriction: Double? = null,
val airJumpProfile: JumpProfile? = null,
val airForce: Double? = null,
)

View File

@ -0,0 +1,78 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
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.SBPattern
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
import ru.dbotthepony.kstarbound.defs.item.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@JsonFactory
data class PlayerDefinition(
val defaultHumanoidIdentity: JsonObject,
val blueprintUnlock: SBPattern,
val blueprintAlreadyKnown: SBPattern,
val collectableUnlock: SBPattern,
val species: ImmutableSet<String>,
val nametagColor: Color,
val ageItemsEvery: Int,
val defaultItems: ImmutableSet<RegistryReference<IItemDefinition>>,
val defaultBlueprints: ImmutableMap<String, ImmutableList<BlueprintUnlock>>,
val defaultCodexes: ImmutableMap<String, ImmutableList<RegistryReference<IItemDefinition>>>,
val metaBoundBox: AABB,
val movementParameters: MovementParameters,
val zeroGMovementParameters: MovementParameters,
val statusControllerSettings: StatusControllerSettings,
val foodLowThreshold: Double,
val foodLowStatusEffects: ImmutableList<String> = ImmutableList.of(),
val foodEmptyStatusEffects: ImmutableList<String> = ImmutableList.of(),
val inCinematicStatusEffects: ImmutableList<String> = ImmutableList.of(),
val footstepTiming: Double,
val footstepSensor: Vector2d,
val vaporTrailTime: Double,
val terminalVelocityDifference: Double,
val initialBeamGunRadius: Double,
val previewGlowBorder: Double,
val objectPreviewInnerAlpha: Double,
val objectPreviewOuterAlpha: Double,
val beamGunConfig: MatterManipulatorConfig,
val underwaterSensor: Vector2d = Vector2d(0.0, 0.0),
val underwaterMinWaterLevel: Double,
val splashConfig: SplashConfig,
val effectsAnimator: AnimationDefinition,
val teleportInTime: Double,
val teleportOutTime: Double,
val deployInTime: Double,
val deployOutTime: Double,
val teleportInStatusEffects: ImmutableList<String>,
val companionsConfig: CompanionsConfig,
val deploymentConfig: DeploymentConfig,
val genericScriptContexts: ImmutableMap<String, String>,
val inventory: InventoryConfig,
val inventoryFilters: ImmutableMap<String, InventoryFilterConfig>,
) {
@JsonFactory
data class BlueprintUnlock(val item: RegistryReference<IItemDefinition>)
}

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.kstarbound.defs.player
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@JsonFactory
data class SplashConfig(
val splashSpeedMin: Double,
val splashMinWaterLevel: Double,
val splashBottomSensor: Vector2d,
val splashTopSensor: Vector2d,
val numSplashParticles: Int,
val splashYVelocityFactor: Double,
val splashParticle: Particle,
val splashParticleVariance: Variance,
) {
@JsonFactory
data class Particle(
val type: String,
val position: Vector2d,
val finalVelocity: Vector2d,
val approach: Vector2d,
val color: Color,
val size: Double,
val timeToLive: Double,
val destructionAction: String,
val destructionTime: Double,
)
@JsonFactory
data class Variance(
val velocity: Vector2d,
val size: Double,
)
}

View File

@ -0,0 +1,48 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@JsonFactory
data class StatusControllerSettings(
val statusProperties: Properties,
val appliesEnvironmentStatusEffects: Boolean = true,
val appliesWeatherStatusEffects: Boolean = true,
val minimumLiquidStatusEffectPercentage: Double = 0.1,
val primaryScriptSources: ImmutableList<String> = ImmutableList.of(),
val primaryScriptDelta: Int,
val stats: ImmutableMap<String, Stat> = ImmutableMap.of(),
val resources: ImmutableMap<String, Resource> = ImmutableMap.of(),
) {
@JsonFactory
data class Properties(
val targetMaterialKind: String,
val mouthPosition: Vector2d,
val breathHealthPenaltyPercentageRate: Double,
val hitInvulnerabilityThreshold: Double,
val hitInvulnerabilityTime: Double,
val hitInvulnerabilityFlash: Double,
val shieldHitInvulnerabilityTime: Double,
val damageFlashOnDirectives: String = "",
val damageFlashOffDirectives: String = ""
)
@JsonFactory
data class Stat(
val baseValue: Double
)
@JsonFactory
data class Resource(
val maxStat: String? = null,
val deltaStat: String? = null,
val deltaValue: Double? = null,
val initialPercentage: Double? = null,
val initialValue: Double? = null,
)
}

View File

@ -11,14 +11,18 @@ import ru.dbotthepony.kvector.vector.nint.Vector2i
object AABBTypeAdapter : TypeAdapter<AABB>() {
override fun write(out: JsonWriter, value: AABB) {
`out`.beginArray()
Vector2dTypeAdapter.write(out, value.mins)
Vector2dTypeAdapter.write(out, value.maxs)
`out`.value(value.mins.x)
`out`.value(value.mins.y)
`out`.value(value.maxs.x)
`out`.value(value.maxs.y)
`out`.endArray()
}
override fun read(`in`: JsonReader): AABB {
val (x1, x2) = Vector2dTypeAdapter.read(`in`)
val (y1, y2) = Vector2dTypeAdapter.read(`in`)
`in`.beginArray()
val (x1, x2) = Vector2d(`in`.nextDouble(), `in`.nextDouble())
val (y1, y2) = Vector2d(`in`.nextDouble(), `in`.nextDouble())
`in`.endArray()
val xMins = x1.coerceAtMost(x2)
val xMaxs = x1.coerceAtLeast(x2)
@ -36,14 +40,18 @@ object AABBTypeAdapter : TypeAdapter<AABB>() {
object AABBiTypeAdapter : TypeAdapter<AABBi>() {
override fun write(out: JsonWriter, value: AABBi) {
`out`.beginArray()
Vector2iTypeAdapter.write(out, value.mins)
Vector2iTypeAdapter.write(out, value.maxs)
`out`.value(value.mins.x)
`out`.value(value.mins.y)
`out`.value(value.maxs.x)
`out`.value(value.maxs.y)
`out`.endArray()
}
override fun read(`in`: JsonReader): AABBi {
val (x1, x2) = Vector2iTypeAdapter.read(`in`)
val (y1, y2) = Vector2iTypeAdapter.read(`in`)
`in`.beginArray()
val (x1, x2) = Vector2i(`in`.nextInt(), `in`.nextInt())
val (y1, y2) = Vector2i(`in`.nextInt(), `in`.nextInt())
`in`.endArray()
val xMins = x1.coerceAtMost(x2)
val xMaxs = x1.coerceAtLeast(x2)

View File

@ -223,7 +223,7 @@ class FactoryAdapter<T : Any> private constructor(
readValues[fieldId] = adapter.read(reader)
presentValues[fieldId] = true
} catch(err: Throwable) {
throw JsonSyntaxException("Reading field ${field.name} for ${bound.qualifiedName}", err)
throw JsonSyntaxException("Reading field \"${field.name}\" near ${reader.path} for ${bound.qualifiedName}", err)
}
fieldId++
@ -275,7 +275,7 @@ class FactoryAdapter<T : Any> private constructor(
readValues[fieldId] = adapter.read(reader)
presentValues[fieldId] = true
} catch(err: Throwable) {
throw JsonSyntaxException("Reading field ${field.name} for ${bound.qualifiedName}", err)
throw JsonSyntaxException("Reading field \"${field.name}\" near ${reader.path} for ${bound.qualifiedName}", err)
}
}
}
@ -290,7 +290,7 @@ class FactoryAdapter<T : Any> private constructor(
readValues[i] = read
}
} catch(err: Throwable) {
throw JsonSyntaxException("Reading flat field ${property.property.name} for ${bound.qualifiedName}", err)
throw JsonSyntaxException("Reading flat field \"${property.property.name}\" near ${reader.path} for ${bound.qualifiedName}", err)
}
}
}
@ -372,7 +372,7 @@ class FactoryAdapter<T : Any> private constructor(
continue
}
throw JsonSyntaxException("Field ${field.name} of ${bound.qualifiedName} does not accept nulls")
throw JsonSyntaxException("Field \"${field.name}\" of ${bound.qualifiedName} does not accept nulls near ${reader.path}")
}
return syntheticFactory.newInstance(*copied)

View File

@ -1,19 +0,0 @@
package ru.dbotthepony.kstarbound.util
class FullJsonPath(val fullPath: String) {
init {
val delimers = fullPath.count { it == ':' }
require(delimers < 2) { "Invalid path: $fullPath" }
}
val path = fullPath.substringBefore(':')
val subpath: JsonPath
init {
if (path.any { it == ':' }) {
subpath = JsonPath(fullPath.substringAfter(':'))
} else {
subpath = JsonPath.EMPTY
}
}
}

View File

@ -1,96 +0,0 @@
package ru.dbotthepony.kstarbound.util
import com.google.common.collect.ImmutableList
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
class JsonPath(val path: String) {
private class Piece(private val value: String) {
private val asNumber = value.toIntOrNull()
fun navigate(element: JsonElement): JsonElement? {
if (element is JsonObject) {
return element[value]
}
if (element is JsonArray) {
if (asNumber == null) {
return null
}
return element[asNumber]
}
return null
}
fun navigate(element: Any): Any? {
if (element is List<*>) {
if (asNumber == null) {
return null
}
return element.getOrNull(asNumber)
}
if (element is Map<*, *>) {
return element[value]
}
return null
}
}
val parts: List<String> = ImmutableList.copyOf(path.split('.'))
private val pieces: List<Piece> = ImmutableList.copyOf(parts.map { Piece(it) })
fun navigate(element: JsonElement): JsonElement? {
var current: JsonElement? = element
for (piece in pieces) {
if (current == null)
return null
current = piece.navigate(current)
}
return current
}
fun navigate(element: List<Any>): Any? {
var current: Any? = element
for (piece in pieces) {
if (current == null)
return null
current = piece.navigate(current)
}
return current
}
fun navigate(element: Map<Any, Any>): Any? {
var current: Any? = element
for (piece in pieces) {
if (current == null)
return null
current = piece.navigate(current)
}
return current
}
val isEmpty: Boolean
get() = path.isEmpty()
val isBlank: Boolean
get() = path.isBlank()
companion object {
val EMPTY = JsonPath("")
}
}