KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/defs/dungeon/DungeonRule.kt

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()
}
}