package ru.dbotthepony.kstarbound.defs import com.google.gson.reflect.TypeToken import ru.dbotthepony.kstarbound.io.readDouble import ru.dbotthepony.kstarbound.io.readNullableDouble import ru.dbotthepony.kstarbound.io.readVector2d import ru.dbotthepony.kstarbound.io.writeDouble import ru.dbotthepony.kstarbound.io.writeNullableDouble import ru.dbotthepony.kstarbound.io.writeStruct2d import ru.dbotthepony.kstarbound.json.builder.DispatchingAdapter import ru.dbotthepony.kstarbound.json.builder.IStringSerializable import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFlat import ru.dbotthepony.kstarbound.math.Line2d import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.network.syncher.legacyCodec import ru.dbotthepony.kstarbound.network.syncher.nativeCodec import ru.dbotthepony.kstarbound.world.physics.Poly import java.io.DataInputStream import java.io.DataOutputStream sealed class PhysicsForceRegion { // ephemeral property from json abstract val enabled: Boolean abstract val filter: PhysicsCategoryFilter abstract fun write(stream: DataOutputStream, isLegacy: Boolean) abstract val type: Type enum class Type(override val jsonName: String, val token: TypeToken) : IStringSerializable { DIRECTIONAL("DirectionalForceRegion", TypeToken.get(Directional::class.java)), RADIAL("RadialForceRegion", TypeToken.get(Radial::class.java)), GRADIENT("GradientForceRegion", TypeToken.get(Gradient::class.java)) } @JsonFactory data class Directional( val region: Poly, val xTargetVelocity: Double?, val yTargetVelocity: Double?, val controlForce: Double, @JsonFlat override val filter: PhysicsCategoryFilter, override val enabled: Boolean = true ) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( Poly.read(stream, isLegacy), stream.readNullableDouble(isLegacy), stream.readNullableDouble(isLegacy), stream.readDouble(isLegacy), PhysicsCategoryFilter(stream, isLegacy) ) override val type: Type get() = Type.DIRECTIONAL override fun write(stream: DataOutputStream, isLegacy: Boolean) { stream.writeByte(0) region.write(stream, isLegacy) stream.writeNullableDouble(xTargetVelocity, isLegacy) stream.writeNullableDouble(yTargetVelocity, isLegacy) stream.writeDouble(controlForce, isLegacy) filter.write(stream, isLegacy) } } @JsonFactory data class Radial( val center: Vector2d = Vector2d.ZERO, val outerRadius: Double, val innerRadius: Double, val targetRadialVelocity: Double, val controlForce: Double, @JsonFlat override val filter: PhysicsCategoryFilter, override val enabled: Boolean = true ) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( stream.readVector2d(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), PhysicsCategoryFilter(stream, isLegacy) ) override val type: Type get() = Type.RADIAL override fun write(stream: DataOutputStream, isLegacy: Boolean) { stream.writeByte(1) stream.writeStruct2d(center, isLegacy) stream.writeDouble(outerRadius, isLegacy) stream.writeDouble(innerRadius, isLegacy) stream.writeDouble(targetRadialVelocity, isLegacy) stream.writeDouble(controlForce, isLegacy) filter.write(stream, isLegacy) } } @JsonFactory data class Gradient( val region: Poly, val gradient: Line2d, val baseTargetVelocity: Double, val baseControlForce: Double, @JsonFlat override val filter: PhysicsCategoryFilter, override val enabled: Boolean = true ) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( Poly.read(stream, isLegacy), Line2d(stream, isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), PhysicsCategoryFilter(stream, isLegacy) ) override val type: Type get() = Type.GRADIENT override fun write(stream: DataOutputStream, isLegacy: Boolean) { stream.writeByte(2) region.write(stream, isLegacy) gradient.write(stream, isLegacy) stream.writeDouble(baseTargetVelocity, isLegacy) stream.writeDouble(baseControlForce, isLegacy) filter.write(stream, isLegacy) } } companion object { val CODEC = nativeCodec(::read, PhysicsForceRegion::write) val LEGACY_CODEC = legacyCodec(::read, PhysicsForceRegion::write) val ADAPTER = DispatchingAdapter("type", { type }, { token }, Type.entries) fun read(stream: DataInputStream, isLegacy: Boolean): PhysicsForceRegion { return when (val type = stream.readUnsignedByte()) { 0 -> Directional(stream, isLegacy) 1 -> Radial(stream, isLegacy) 2 -> Gradient(stream, isLegacy) else -> throw IllegalArgumentException("Unknown force region type $type!") } } } }