358 lines
10 KiB
Kotlin
358 lines
10 KiB
Kotlin
package ru.dbotthepony.kstarbound.defs.dungeon
|
|
|
|
import com.google.common.collect.ImmutableSet
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonArray
|
|
import com.google.gson.JsonObject
|
|
import com.google.gson.JsonSyntaxException
|
|
import com.google.gson.TypeAdapter
|
|
import com.google.gson.annotations.JsonAdapter
|
|
import com.google.gson.stream.JsonReader
|
|
import com.google.gson.stream.JsonWriter
|
|
import org.apache.logging.log4j.LogManager
|
|
import ru.dbotthepony.kommons.gson.contains
|
|
import ru.dbotthepony.kstarbound.defs.tile.isEmptyLiquid
|
|
import ru.dbotthepony.kstarbound.defs.tile.isEmptyTile
|
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
|
|
import ru.dbotthepony.kstarbound.defs.tile.isObjectTile
|
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
|
import ru.dbotthepony.kstarbound.json.stream
|
|
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
|
|
|
@JsonAdapter(DungeonRule.Adapter::class)
|
|
abstract class DungeonRule {
|
|
enum class Type(override val jsonName: String, val factory: (JsonArray) -> DungeonRule) : IStringSerializable {
|
|
NOOP("", { Noop }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
return null
|
|
}
|
|
},
|
|
|
|
MUST_CONTAIN_LIQUID("worldGenMustContainLiquid", { MustContainLiquid }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if ("worldGenMustContainLiquid" in json)
|
|
return MustContainLiquid
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
MUST_NOT_CONTAIN_LIQUID("worldGenMustNotContainLiquid", { MustNotContainLiquid }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if ("worldGenMustNotContainLiquid" in json)
|
|
return MustNotContainLiquid
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
HAVE_SOLID_FOREGROUND("worldGenMustContainSolidForeground", { HaveSolidForeground }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if (json["layer"].asString == "front" && "worldGenMustContainSolid" in json)
|
|
return HaveSolidForeground
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
HAVE_EMPTY_FOREGROUND("worldGenMustContainAirForeground", { HaveEmptyForeground }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if (json["layer"].asString == "front" && "worldGenMustContainAir" in json)
|
|
return HaveEmptyForeground
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
HAVE_SOLID_BACKGROUND("worldGenMustContainSolidBackground", { HaveSolidBackground }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if (json["layer"].asString == "back" && "worldGenMustContainSolid" in json)
|
|
return HaveSolidBackground
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
HAVE_EMPTY_BACKGROUND("worldGenMustContainAirBackground", { HaveEmptyBackground }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if (json["layer"].asString == "back" && "worldGenMustContainAir" in json)
|
|
return HaveEmptyBackground
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
ALLOW_OVERDRAWING("allowOverdrawing", { AllowOverdrawing }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
if ("allowOverdrawing" in json)
|
|
return AllowOverdrawing
|
|
|
|
return null
|
|
}
|
|
},
|
|
|
|
IGNORE_PART_MAXIMUM("ignorePartMaximumRule", { IgnorePartMaximum }) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
return null
|
|
}
|
|
},
|
|
|
|
MAX_SPAWN_COUNT("maxSpawnCount", ::MaxSpawnCount) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
return null
|
|
}
|
|
},
|
|
|
|
DO_NOT_CONNECT_TO_PART("doNotConnectToPart", ::DoNotConnectToPart) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
return null
|
|
}
|
|
},
|
|
|
|
DO_NOT_COMBINE_WITH("doNotCombineWith", ::DoNotCombineWith) {
|
|
override fun readTiled(json: JsonObject, flipX: Boolean, flipY: Boolean): DungeonRule? {
|
|
return null
|
|
}
|
|
};
|
|
|
|
abstract fun readTiled(json: JsonObject, flipX: Boolean = false, flipY: Boolean = false): DungeonRule?
|
|
}
|
|
|
|
open val requiresSolid: Boolean
|
|
get() = false
|
|
|
|
open val requiresLiquid: Boolean
|
|
get() = false
|
|
|
|
open val requiresOpen: Boolean
|
|
get() = false
|
|
|
|
open val allowOverdrawing: Boolean
|
|
get() = false
|
|
|
|
open val ignorePartMaximum: Boolean
|
|
get() = false
|
|
|
|
open fun doesNotConnectToPart(name: String): Boolean {
|
|
return false
|
|
}
|
|
|
|
open fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
return true
|
|
}
|
|
|
|
open fun checkTileCanPlace(x: Int, y: Int, world: ServerWorld): Boolean {
|
|
return true
|
|
}
|
|
|
|
open fun checkPartCombinationsAllowed(placements: Map<String, Int>): Boolean {
|
|
return true
|
|
}
|
|
|
|
open fun allowSpawnCount(currentCount: Int): Boolean {
|
|
return true
|
|
}
|
|
|
|
object Noop : DungeonRule()
|
|
|
|
object MustContainLiquid : DungeonRule() {
|
|
override val requiresLiquid: Boolean
|
|
get() = true
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
val cell = world.parent.template.cellInfo(x, y)
|
|
return cell.oceanLiquid.isNotEmptyLiquid && cell.oceanLiquidLevel > y
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Must contain liquid"
|
|
}
|
|
}
|
|
|
|
object MustNotContainLiquid : DungeonRule() {
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
val cell = world.parent.template.cellInfo(x, y)
|
|
return cell.oceanLiquid.isEmptyLiquid || cell.oceanLiquidLevel <= y
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Must not contain liquid"
|
|
}
|
|
}
|
|
|
|
object HaveSolidForeground : DungeonRule() {
|
|
override val requiresSolid: Boolean
|
|
get() = true
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
if (world.markSurfaceLevel != null)
|
|
return y < world.markSurfaceLevel
|
|
|
|
val cell = world.parent.getCell(x, y)
|
|
|
|
if (cell.foreground.material.isObjectTile && world.isClearingTileEntityAt(x, y))
|
|
return false
|
|
|
|
return cell.foreground.material.isNotEmptyTile && !world.isClearingTileEntityAt(x, y)
|
|
}
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: ServerWorld): Boolean {
|
|
val cell = world.getCell(x, y)
|
|
return cell.foreground.material.isNotEmptyTile
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Solid foreground"
|
|
}
|
|
}
|
|
|
|
object HaveEmptyForeground : DungeonRule() {
|
|
override val requiresOpen: Boolean
|
|
get() = true
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
if (world.markSurfaceLevel != null)
|
|
return y >= world.markSurfaceLevel
|
|
|
|
val cell = world.parent.getCell(x, y)
|
|
return cell.foreground.material.isEmptyTile || cell.foreground.material.isObjectTile && world.isClearingTileEntityAt(x, y)
|
|
}
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: ServerWorld): Boolean {
|
|
val cell = world.getCell(x, y)
|
|
return cell.foreground.material.isEmptyTile
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Empty foreground"
|
|
}
|
|
}
|
|
|
|
object HaveSolidBackground : DungeonRule() {
|
|
override val requiresSolid: Boolean
|
|
get() = true
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
if (world.markSurfaceLevel != null)
|
|
return y < world.markSurfaceLevel
|
|
|
|
val cell = world.parent.getCell(x, y)
|
|
|
|
if (cell.background.material.isObjectTile && world.isClearingTileEntityAt(x, y))
|
|
return false
|
|
|
|
return cell.background.material.isNotEmptyTile
|
|
}
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: ServerWorld): Boolean {
|
|
val cell = world.getCell(x, y)
|
|
return cell.background.material.isNotEmptyTile
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Solid background"
|
|
}
|
|
}
|
|
|
|
object HaveEmptyBackground : DungeonRule() {
|
|
override val requiresOpen: Boolean
|
|
get() = true
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: DungeonWorld): Boolean {
|
|
if (world.markSurfaceLevel != null)
|
|
return y >= world.markSurfaceLevel
|
|
|
|
val cell = world.parent.getCell(x, y)
|
|
return cell.background.material.isEmptyTile || cell.background.material.isObjectTile && world.isClearingTileEntityAt(x, y)
|
|
}
|
|
|
|
override fun checkTileCanPlace(x: Int, y: Int, world: ServerWorld): Boolean {
|
|
val cell = world.getCell(x, y)
|
|
return cell.background.material.isEmptyTile
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Empty background"
|
|
}
|
|
}
|
|
|
|
object AllowOverdrawing : DungeonRule() {
|
|
override val allowOverdrawing: Boolean
|
|
get() = true
|
|
|
|
override fun toString(): String {
|
|
return "Allow overdrawing"
|
|
}
|
|
}
|
|
|
|
object IgnorePartMaximum : DungeonRule() {
|
|
override val ignorePartMaximum: Boolean
|
|
get() = true
|
|
|
|
override fun toString(): String {
|
|
return "Ignore part maximum"
|
|
}
|
|
}
|
|
|
|
data class MaxSpawnCount(val count: Int) : DungeonRule() {
|
|
constructor(json: JsonArray) : this(json[1].asJsonArray[0].asInt)
|
|
|
|
override fun allowSpawnCount(currentCount: Int): Boolean {
|
|
return currentCount < count
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Max spawn count = $count"
|
|
}
|
|
}
|
|
|
|
data class DoNotConnectToPart(val parts: ImmutableSet<String>) : DungeonRule() {
|
|
constructor(json: JsonArray) : this(json[1].asJsonArray.stream().map { it.asString }.collect(ImmutableSet.toImmutableSet()))
|
|
|
|
override fun doesNotConnectToPart(name: String): Boolean {
|
|
return name in parts
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Do not connect to $parts"
|
|
}
|
|
}
|
|
|
|
data class DoNotCombineWith(val parts: ImmutableSet<String>) : DungeonRule() {
|
|
constructor(json: JsonArray) : this(json[1].asJsonArray.stream().map { it.asString }.collect(ImmutableSet.toImmutableSet()))
|
|
|
|
override fun checkPartCombinationsAllowed(placements: Map<String, Int>): Boolean {
|
|
return placements.keys.none { it in parts }
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Do not combine with $parts"
|
|
}
|
|
}
|
|
|
|
class Adapter(gson: Gson) : TypeAdapter<DungeonRule>() {
|
|
private val arrays = gson.getAdapter(JsonArray::class.java)
|
|
private val types = gson.getAdapter(Type::class.java)
|
|
|
|
override fun write(out: JsonWriter, value: DungeonRule) {
|
|
throw UnsupportedOperationException("Dungeon Rules can't be serialized at this moment")
|
|
}
|
|
|
|
override fun read(`in`: JsonReader): DungeonRule {
|
|
val read = arrays.read(`in`)
|
|
|
|
if (read.isEmpty) {
|
|
throw JsonSyntaxException("Empty rule")
|
|
}
|
|
|
|
val type = types.fromJsonTree(read[0])
|
|
return type.factory(read)
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private val LOGGER = LogManager.getLogger()
|
|
}
|
|
}
|