package ru.dbotthepony.kstarbound.defs import com.google.common.collect.ImmutableSet import ru.dbotthepony.kommons.io.readCollection import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kommons.io.writeCollection import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.io.readDouble import ru.dbotthepony.kstarbound.io.readInternedString 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.math.Line2d 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 import java.util.function.Predicate sealed class PhysicsForceRegion { abstract fun write(stream: DataOutputStream, isLegacy: Boolean) // enum class Type int32_t // type -> isBlacklist class Filter(val isBlacklist: Boolean, val categories: Set) : Predicate { constructor(stream: DataInputStream, isLegacy: Boolean) : this(if (isLegacy) stream.readInt() > 0 else stream.readBoolean(), ImmutableSet.copyOf(stream.readCollection { readInternedString() })) fun write(stream: DataOutputStream, isLegacy: Boolean) { if (isLegacy) stream.writeInt(if (isBlacklist) 1 else 0) else stream.writeBoolean(isBlacklist) stream.writeCollection(categories) { writeBinaryString(it) } } fun test(categories: Collection): Boolean { if (isBlacklist) { return categories.any { it in this.categories } } else { return categories.any { it in this.categories } } } override fun test(t: String): Boolean { if (isBlacklist) { return t !in this.categories } else { return t in this.categories } } } data class Directional(val region: Poly, val xTargetVelocity: Double?, val yTargetVelocity: Double?, val controlForce: Double, val filter: Filter) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( Poly.read(stream, isLegacy), stream.readNullableDouble(isLegacy), stream.readNullableDouble(isLegacy), stream.readDouble(isLegacy), Filter(stream, isLegacy) ) 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) } } data class Radial(val center: Vector2d, val outerRadius: Double, val innerRadius: Double, val targetRadialVelocity: Double, val controlForce: Double, val filter: Filter) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( stream.readVector2d(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), Filter(stream, isLegacy) ) 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) } } data class Gradient(val region: Poly, val gradient: Line2d, val baseTargetVelocity: Double, val baseControlForce: Double, val filter: Filter) : PhysicsForceRegion() { constructor(stream: DataInputStream, isLegacy: Boolean) : this( Poly.read(stream, isLegacy), Line2d(stream, isLegacy), stream.readDouble(isLegacy), stream.readDouble(isLegacy), Filter(stream, isLegacy) ) 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) 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!") } } } }