Immutable chunk cells
This commit is contained in:
parent
ba0d792e83
commit
93deb62d4b
@ -1,8 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.Version
|
||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||
@ -13,14 +11,13 @@ import ru.dbotthepony.kstarbound.player.Avatar
|
||||
import ru.dbotthepony.kstarbound.player.QuestDescriptor
|
||||
import ru.dbotthepony.kstarbound.player.QuestInstance
|
||||
import ru.dbotthepony.kstarbound.util.JVMTimeSource
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
|
||||
import ru.dbotthepony.kstarbound.io.json.VersionedJson
|
||||
import ru.dbotthepony.kstarbound.io.readVarInt
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import ru.dbotthepony.kstarbound.world.api.MutableCell
|
||||
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
@ -30,7 +27,6 @@ import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.Inflater
|
||||
import java.util.zip.InflaterInputStream
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
@ -82,13 +78,12 @@ fun main() {
|
||||
var reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data), Inflater())))
|
||||
reader.skipBytes(3)
|
||||
|
||||
for (y in 0 .. 31) {
|
||||
for (x in 0 .. 31) {
|
||||
val cell = client.world!!.getCellDirect(chunkX * 32 + x, chunkY * 32 + y)
|
||||
if (cell == null) {
|
||||
IChunkCell.skip(reader)
|
||||
} else {
|
||||
cell.read(reader)
|
||||
val chunk = client.world!!.chunkMap.compute(chunkX - 2, chunkY)
|
||||
|
||||
if (chunk != null) {
|
||||
for (y in 0 .. 31) {
|
||||
for (x in 0 .. 31) {
|
||||
check(chunk.setCell(x, y, MutableCell().read(reader)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ import ru.dbotthepony.kstarbound.util.forEachValid
|
||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||
import ru.dbotthepony.kvector.arrays.Matrix3fStack
|
||||
@ -662,12 +662,16 @@ class StarboundClient : Closeable {
|
||||
val settings = ClientSettings()
|
||||
|
||||
val viewportCells: ICellAccess = object : ICellAccess {
|
||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||
return world?.getCell(x + viewportCellX, y + viewportCellY)
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
return world!!.getCell(x + viewportCellX, y + viewportCellY)
|
||||
}
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): IChunkCell? {
|
||||
return world?.getCellDirect(x + viewportCellX, y + viewportCellY)
|
||||
override fun getCellDirect(x: Int, y: Int): AbstractCell {
|
||||
return world!!.getCellDirect(x + viewportCellX, y + viewportCellY)
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
return world!!.setCell(x + viewportCellX, y + viewportCellY, cell)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
|
||||
enum class RenderLayer {
|
||||
@ -69,7 +69,7 @@ enum class RenderLayer {
|
||||
}
|
||||
}
|
||||
|
||||
fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: ITileState): Point {
|
||||
fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: AbstractTileState): Point {
|
||||
if (isModifier) {
|
||||
return tileLayer(isBackground, true, tile.modifier?.renderParameters?.zLevel ?: 0L, tile.modifier?.modId?.toLong() ?: 0L, tile.modifierHueShift)
|
||||
} else {
|
||||
|
@ -13,7 +13,7 @@ import ru.dbotthepony.kstarbound.client.gl.shader.UberShader
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.*
|
||||
import ru.dbotthepony.kstarbound.defs.tile.*
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
@ -94,13 +94,13 @@ class TileRenderers(val client: StarboundClient) {
|
||||
}
|
||||
|
||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||
override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean {
|
||||
override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean {
|
||||
return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift
|
||||
}
|
||||
}
|
||||
|
||||
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
|
||||
override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean {
|
||||
override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean {
|
||||
return otherTile?.modifier == definition
|
||||
}
|
||||
}
|
||||
@ -125,7 +125,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
val bakedBackgroundProgramState = texture?.let { renderers.background(it) }
|
||||
// private var notifiedDepth = false
|
||||
|
||||
private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileAccess, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
||||
private fun tesselateAt(self: AbstractTileState, piece: RenderPiece, getter: ITileAccess, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
||||
val fx = pos.x.toFloat()
|
||||
val fy = pos.y.toFloat()
|
||||
|
||||
@ -173,7 +173,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
}
|
||||
|
||||
private fun tesselatePiece(
|
||||
self: ITileState,
|
||||
self: AbstractTileState,
|
||||
matchPiece: RenderMatch,
|
||||
getter: ITileAccess,
|
||||
meshBuilder: LayeredRenderer,
|
||||
@ -227,7 +227,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
*
|
||||
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
|
||||
*/
|
||||
fun tesselate(self: ITileState, getter: ITileAccess, meshBuilder: LayeredRenderer, pos: Vector2i, isBackground: Boolean = false, isModifier: Boolean = false) {
|
||||
fun tesselate(self: AbstractTileState, getter: ITileAccess, meshBuilder: LayeredRenderer, pos: Vector2i, isBackground: Boolean = false, isModifier: Boolean = false) {
|
||||
if (texture == null) return
|
||||
|
||||
// если у нас нет renderTemplate
|
||||
|
@ -2,28 +2,29 @@ package ru.dbotthepony.kstarbound.client.world
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.Chunk
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.api.ImmutableCell
|
||||
|
||||
class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk>(world, pos){
|
||||
override fun foregroundChanges(cell: Cell) {
|
||||
super.foregroundChanges(cell)
|
||||
override fun foregroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
super.foregroundChanges(x, y, cell)
|
||||
|
||||
world.forEachRenderRegion(cell) {
|
||||
world.forEachRenderRegion(pos.tile(x, y)) {
|
||||
it.foreground.isDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun backgroundChanges(cell: Cell) {
|
||||
super.backgroundChanges(cell)
|
||||
override fun backgroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
super.backgroundChanges(x, y, cell)
|
||||
|
||||
world.forEachRenderRegion(cell) {
|
||||
world.forEachRenderRegion(pos.tile(x, y)) {
|
||||
it.background.isDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun liquidChanges(cell: Cell) {
|
||||
super.liquidChanges(cell)
|
||||
override fun liquidChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
super.liquidChanges(x, y, cell)
|
||||
|
||||
world.forEachRenderRegion(cell) {
|
||||
world.forEachRenderRegion(pos.tile(x, y)) {
|
||||
it.liquidIsDirty = true
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import ru.dbotthepony.kstarbound.defs.image.Image
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.util.WriteOnce
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
|
||||
@JsonFactory
|
||||
@ -21,7 +21,7 @@ data class RenderPiece(
|
||||
)
|
||||
|
||||
fun interface EqualityRuleTester {
|
||||
fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean
|
||||
fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean
|
||||
}
|
||||
|
||||
@JsonFactory
|
||||
|
@ -1,16 +1,10 @@
|
||||
package ru.dbotthepony.kstarbound.world
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.ILiquidState
|
||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||
import ru.dbotthepony.kstarbound.world.api.ImmutableCell
|
||||
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
import ru.dbotthepony.kstarbound.world.api.TileView
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
||||
@ -33,10 +27,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
val pos: ChunkPos,
|
||||
) : ICellAccess {
|
||||
var changeset = 0
|
||||
private set(value) {
|
||||
field = value
|
||||
promote()
|
||||
}
|
||||
private set
|
||||
|
||||
var tileChangeset = 0
|
||||
private set
|
||||
@ -50,32 +41,49 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
var backgroundChangeset = 0
|
||||
private set
|
||||
|
||||
private var isEmpty = true
|
||||
|
||||
fun promote() {
|
||||
if (isEmpty) {
|
||||
isEmpty = false
|
||||
world.chunkMap.promote(this as This)
|
||||
}
|
||||
}
|
||||
|
||||
protected val cells = lazy {
|
||||
Object2DArray.nulls<Cell>(CHUNK_SIZE, CHUNK_SIZE)
|
||||
Object2DArray(CHUNK_SIZE, CHUNK_SIZE, AbstractCell.NULL)
|
||||
}
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell {
|
||||
var get = cells.value[x, y]
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
if (!cells.isInitialized())
|
||||
return AbstractCell.NULL
|
||||
|
||||
if (get == null) {
|
||||
get = Cell(x, y)
|
||||
cells.value[x, y] = get
|
||||
return cells.value[x, y]
|
||||
}
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): AbstractCell {
|
||||
return getCell(x, y)
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
val ix = x and CHUNK_SIZE_FF
|
||||
val iy = y and CHUNK_SIZE_FF
|
||||
|
||||
if (ix != x || iy != y) return false
|
||||
|
||||
val old = if (cells.isInitialized()) cells.value[ix, iy] else AbstractCell.NULL
|
||||
val new = cell.immutable()
|
||||
|
||||
if (old != new) {
|
||||
cells.value[ix, iy] = new
|
||||
|
||||
if (old.foreground != new.foreground) {
|
||||
foregroundChanges(ix, iy, new)
|
||||
}
|
||||
|
||||
if (old.background != new.background) {
|
||||
backgroundChanges(ix, iy, new)
|
||||
}
|
||||
|
||||
if (old.liquid != new.liquid) {
|
||||
liquidChanges(ix, iy, new)
|
||||
}
|
||||
|
||||
cellChanges(ix, iy, new)
|
||||
}
|
||||
|
||||
return get
|
||||
}
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): IChunkCell {
|
||||
return getCell(x, y)
|
||||
return true
|
||||
}
|
||||
|
||||
// local cells' tile access
|
||||
@ -89,24 +97,24 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
|
||||
val aabb = aabbBase + Vector2d(pos.x * CHUNK_SIZE.toDouble(), pos.y * CHUNK_SIZE.toDouble())
|
||||
|
||||
protected open fun foregroundChanges(cell: Cell) {
|
||||
cellChanges(cell)
|
||||
protected open fun foregroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
cellChanges(x, y, cell)
|
||||
tileChangeset++
|
||||
foregroundChangeset++
|
||||
}
|
||||
|
||||
protected open fun backgroundChanges(cell: Cell) {
|
||||
cellChanges(cell)
|
||||
protected open fun backgroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
cellChanges(x, y, cell)
|
||||
tileChangeset++
|
||||
backgroundChangeset++
|
||||
}
|
||||
|
||||
protected open fun liquidChanges(cell: Cell) {
|
||||
cellChanges(cell)
|
||||
protected open fun liquidChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
cellChanges(x, y, cell)
|
||||
liquidChangeset++
|
||||
}
|
||||
|
||||
protected open fun cellChanges(cell: Cell) {
|
||||
protected open fun cellChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
changeset++
|
||||
cellChangeset++
|
||||
}
|
||||
@ -122,130 +130,6 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
world.chunkMap[pos.bottomRight]?.let(block)
|
||||
}
|
||||
|
||||
inner class Cell(x: Int, y: Int) : IChunkCell {
|
||||
override val x: Int = x + pos.x * CHUNK_SIZE
|
||||
override val y: Int = y + pos.y * CHUNK_SIZE
|
||||
|
||||
inner class Tile(private val foreground: Boolean) : ITileState {
|
||||
private fun change() {
|
||||
if (foreground) {
|
||||
foregroundChanges(this@Cell)
|
||||
} else {
|
||||
backgroundChanges(this@Cell)
|
||||
}
|
||||
}
|
||||
|
||||
override var material: TileDefinition = BuiltinMetaMaterials.NULL
|
||||
set(value) {
|
||||
if (field !== value) {
|
||||
field = value
|
||||
change()
|
||||
}
|
||||
}
|
||||
override var modifier: MaterialModifier? = null
|
||||
set(value) {
|
||||
if (field !== value) {
|
||||
field = value
|
||||
change()
|
||||
}
|
||||
}
|
||||
|
||||
override var color: TileColor = TileColor.DEFAULT
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
change()
|
||||
}
|
||||
}
|
||||
|
||||
override var hueShift: Float = 0f
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value.coerceIn(0f, 360f)
|
||||
change()
|
||||
}
|
||||
}
|
||||
|
||||
override var modifierHueShift: Float = 0f
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value.coerceIn(0f, 360f)
|
||||
change()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class Liquid : ILiquidState {
|
||||
override var def: LiquidDefinition? = null
|
||||
set(value) {
|
||||
if (field !== value) {
|
||||
field = value
|
||||
liquidChanges(this@Cell)
|
||||
}
|
||||
}
|
||||
|
||||
override var level: Float = 0f
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
liquidChanges(this@Cell)
|
||||
}
|
||||
}
|
||||
|
||||
override var pressure: Float = 0f
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
liquidChanges(this@Cell)
|
||||
}
|
||||
}
|
||||
|
||||
override var isInfinite: Boolean = false
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
liquidChanges(this@Cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val foreground = Tile(true)
|
||||
override val background = Tile(false)
|
||||
override val liquid = Liquid()
|
||||
|
||||
override var dungeonId: Int = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
cellChanges(this)
|
||||
}
|
||||
}
|
||||
|
||||
override var biome: Int = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
cellChanges(this)
|
||||
}
|
||||
}
|
||||
|
||||
override var envBiome: Int = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
cellChanges(this)
|
||||
}
|
||||
}
|
||||
|
||||
override var isIndestructible: Boolean = false
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
cellChanges(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun randomLongFor(x: Int, y: Int): Long {
|
||||
return world.randomLongFor(x or pos.x shl CHUNK_SIZE_BITS, y or pos.y shl CHUNK_SIZE_BITS)
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ data class ChunkPos(val x: Int, val y: Int) : IStruct2i, Comparable<ChunkPos> {
|
||||
val tileY = y shl CHUNK_SIZE_BITS
|
||||
val tile = Vector2i(tileX, tileY)
|
||||
|
||||
fun tile(x: Int, y: Int): Vector2i {
|
||||
return Vector2i(tileX + x, tileY + y)
|
||||
}
|
||||
|
||||
val top: ChunkPos get() {
|
||||
return ChunkPos(x, y + 1)
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
package ru.dbotthepony.kstarbound.world
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.METRES_IN_STARBOUND_UNIT
|
||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
import kotlin.collections.ArrayList
|
||||
@ -20,7 +19,7 @@ data class RayCastResult(
|
||||
) {
|
||||
constructor(startPos: Vector2d, direction: Vector2d) : this(listOf(), null, 0.0, startPos, startPos, direction)
|
||||
|
||||
data class HitCell(val pos: Vector2i, val normal: Direction, val borderCross: Vector2d, val cell: IChunkCell)
|
||||
data class HitCell(val pos: Vector2i, val normal: Direction, val borderCross: Vector2d, val cell: AbstractCell)
|
||||
}
|
||||
|
||||
enum class RayFilterResult(val hit: Boolean, val write: Boolean) {
|
||||
@ -44,7 +43,7 @@ fun interface TileRayFilter {
|
||||
/**
|
||||
* [x] and [y] are wrapped around positions
|
||||
*/
|
||||
fun test(cell: IChunkCell, fraction: Double, x: Int, y: Int, normal: Direction, borderX: Double, borderY: Double): RayFilterResult
|
||||
fun test(cell: AbstractCell, fraction: Double, x: Int, y: Int, normal: Direction, borderX: Double, borderY: Double): RayFilterResult
|
||||
}
|
||||
|
||||
val NeverFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.CONTINUE }
|
||||
|
@ -10,12 +10,11 @@ import ru.dbotthepony.kstarbound.math.*
|
||||
import ru.dbotthepony.kstarbound.util.MailboxExecutorService
|
||||
import ru.dbotthepony.kstarbound.util.ParallelPerform
|
||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kstarbound.world.api.TileView
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import ru.dbotthepony.kvector.api.IStruct2d
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
@ -23,8 +22,6 @@ import ru.dbotthepony.kvector.arrays.Object2DArray
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
import java.lang.ref.ReferenceQueue
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.random.RandomGenerator
|
||||
@ -54,33 +51,31 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
final override fun randomLongFor(x: Int, y: Int) = super.randomLongFor(x, y) xor seed
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): IChunkCell? {
|
||||
if (!this.x.inBoundsCell(x) || !this.y.inBoundsCell(y)) return null
|
||||
override fun getCellDirect(x: Int, y: Int): AbstractCell {
|
||||
if (!this.x.inBoundsCell(x) || !this.y.inBoundsCell(y)) return AbstractCell.NULL
|
||||
return getCell(x, y)
|
||||
}
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
return chunkMap.getCell(x, y)
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
return chunkMap.setCell(x, y, cell)
|
||||
}
|
||||
|
||||
abstract inner class ChunkMap {
|
||||
abstract operator fun get(x: Int, y: Int): ChunkType?
|
||||
abstract fun compute(x: Int, y: Int): ChunkType?
|
||||
|
||||
abstract fun promote(self: ChunkType)
|
||||
abstract fun purge()
|
||||
abstract fun remove(x: Int, y: Int)
|
||||
|
||||
abstract fun getCell(x: Int, y: Int): IChunkCell?
|
||||
abstract fun getCell(x: Int, y: Int): AbstractCell
|
||||
abstract fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean
|
||||
|
||||
protected val queue = ReferenceQueue<ChunkType>()
|
||||
operator fun get(pos: ChunkPos) = get(pos.x, pos.y)
|
||||
|
||||
protected inner class Ref(chunk: ChunkType) : WeakReference<ChunkType>(chunk, queue) {
|
||||
val pos = chunk.pos
|
||||
}
|
||||
|
||||
protected fun create(x: Int, y: Int): ChunkType {
|
||||
purge()
|
||||
val pos = ChunkPos(x, y)
|
||||
val chunk = chunkFactory(pos)
|
||||
val orphanedInThisChunk = ArrayList<Entity>()
|
||||
@ -103,68 +98,41 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
// hash chunk map is around 30% slower than rectangular one
|
||||
inner class HashChunkMap : ChunkMap() {
|
||||
private val map = Long2ObjectOpenHashMap<Any>()
|
||||
private val map = Long2ObjectOpenHashMap<ChunkType>()
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return null
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return AbstractCell.NULL
|
||||
val ix = this@World.x.cell(x)
|
||||
val iy = this@World.y.cell(y)
|
||||
return this[this@World.x.chunkFromCell(ix), this@World.y.chunkFromCell(iy)]?.getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK)
|
||||
return this[this@World.x.chunkFromCell(ix), this@World.y.chunkFromCell(iy)]?.getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK) ?: AbstractCell.NULL
|
||||
}
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
override fun get(x: Int, y: Int): ChunkType? {
|
||||
if (!this@World.x.isValidChunkIndex(x) || !this@World.y.isValidChunkIndex(y)) return null
|
||||
|
||||
val x = this@World.x.chunk(x)
|
||||
val y = this@World.y.chunk(y)
|
||||
|
||||
return map[ChunkPos.toLong(x, y)]?.let {
|
||||
if (it is World<*, *>.ChunkMap.Ref) {
|
||||
it.get() as ChunkType?
|
||||
} else {
|
||||
it as ChunkType?
|
||||
}
|
||||
} ?: create(x, y).also { map[ChunkPos.toLong(x, y)] = Ref(it) }
|
||||
return map[ChunkPos.toLong(x, y)]
|
||||
}
|
||||
|
||||
override fun purge() {
|
||||
var next = queue.poll() as World<*, *>.ChunkMap.Ref?
|
||||
|
||||
while (next != null) {
|
||||
val k = ChunkPos.toLong(next.pos.x, next.pos.y)
|
||||
val get = map[k]
|
||||
|
||||
if (get === next)
|
||||
map.remove(k)
|
||||
|
||||
next = queue.poll() as World<*, *>.ChunkMap.Ref?
|
||||
}
|
||||
override fun compute(x: Int, y: Int): ChunkType? {
|
||||
if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null
|
||||
return map[ChunkPos.toLong(x, y)] ?: create(x, y).also { map[ChunkPos.toLong(x, y)] = it }
|
||||
}
|
||||
|
||||
override fun promote(self: ChunkType) {
|
||||
val (x, y) = self.pos
|
||||
val ref = map[ChunkPos.toLong(x, y)]
|
||||
|
||||
if (ref !is World<*, *>.ChunkMap.Ref) {
|
||||
throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but there is $ref")
|
||||
}
|
||||
|
||||
if (!(ref as World<*, ChunkType>.ChunkMap.Ref).refersTo(self)) {
|
||||
throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but it doesn't refer to valid object (self: $self, referent: ${ref.get()})")
|
||||
}
|
||||
|
||||
map[ChunkPos.toLong(x, y)] = self
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return false
|
||||
val ix = this@World.x.cell(x)
|
||||
val iy = this@World.y.cell(y)
|
||||
val cx = this@World.x.chunkFromCell(ix)
|
||||
val cy = this@World.y.chunkFromCell(iy)
|
||||
return (map[ChunkPos.toLong(cx, cy)] ?: create(cx, cy).also { map[ChunkPos.toLong(cx, cy)] = it }).setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell)
|
||||
}
|
||||
|
||||
override fun remove(x: Int, y: Int) {
|
||||
val x = this@World.x.chunk(x)
|
||||
val y = this@World.y.chunk(y)
|
||||
|
||||
val ref = map.remove(ChunkPos.toLong(x, y))
|
||||
|
||||
if (ref is World<*, *>.ChunkMap.Ref) {
|
||||
ref.clear()
|
||||
}
|
||||
map.remove(ChunkPos.toLong(this@World.x.chunk(x), this@World.y.chunk(y)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,64 +140,38 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
val width = size!!.x
|
||||
val height = size!!.y
|
||||
|
||||
private val map = Object2DArray.nulls<Any>(divideUp(width, CHUNK_SIZE), divideUp(height, CHUNK_SIZE))
|
||||
private val map = Object2DArray.nulls<ChunkType>(divideUp(width, CHUNK_SIZE), divideUp(height, CHUNK_SIZE))
|
||||
|
||||
private fun getRaw(x: Int, y: Int): ChunkType {
|
||||
return map[x, y]?.let {
|
||||
if (it is World<*, *>.ChunkMap.Ref) {
|
||||
it.get() as ChunkType?
|
||||
} else {
|
||||
it as ChunkType?
|
||||
}
|
||||
} ?: create(x, y).also {
|
||||
map[x, y] = Ref(it)
|
||||
}
|
||||
return map[x, y] ?: create(x, y).also { map[x, y] = it }
|
||||
}
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return null
|
||||
override fun compute(x: Int, y: Int): ChunkType? {
|
||||
if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null
|
||||
return getRaw(x, y)
|
||||
}
|
||||
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return AbstractCell.NULL
|
||||
val ix = this@World.x.cell(x)
|
||||
val iy = this@World.y.cell(y)
|
||||
|
||||
return getRaw(ix ushr CHUNK_SIZE_BITS, iy ushr CHUNK_SIZE_BITS).getCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK)
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
if (!this@World.x.isValidCellIndex(x) || !this@World.y.isValidCellIndex(y)) return false
|
||||
val ix = this@World.x.cell(x)
|
||||
val iy = this@World.y.cell(y)
|
||||
return getRaw(ix ushr CHUNK_SIZE_BITS, iy ushr CHUNK_SIZE_BITS).setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell)
|
||||
}
|
||||
|
||||
override fun get(x: Int, y: Int): ChunkType? {
|
||||
if (!this@World.x.isValidChunkIndex(x) || !this@World.y.isValidChunkIndex(y)) return null
|
||||
return getRaw(this@World.x.chunk(x), this@World.y.chunk(y))
|
||||
}
|
||||
|
||||
override fun purge() {
|
||||
while (queue.poll() != null) {}
|
||||
}
|
||||
|
||||
override fun promote(self: ChunkType) {
|
||||
val (x, y) = self.pos
|
||||
val ref = map[x, y]
|
||||
|
||||
if (ref !is World<*, *>.ChunkMap.Ref) {
|
||||
throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but there is $ref")
|
||||
}
|
||||
|
||||
if (!(ref as World<*, ChunkType>.ChunkMap.Ref).refersTo(self)) {
|
||||
throw IllegalStateException("Tried to promote chunk from weak to strong storage at $x, $y; but it doesn't refer to valid object (self: $self, referent: ${ref.get()})")
|
||||
}
|
||||
|
||||
map[x, y] = self
|
||||
}
|
||||
|
||||
override fun remove(x: Int, y: Int) {
|
||||
val x = this@World.x.chunk(x)
|
||||
val y = this@World.y.chunk(y)
|
||||
|
||||
val old = map[x, y]
|
||||
|
||||
if (old is World<*, *>.ChunkMap.Ref) {
|
||||
old.clear()
|
||||
}
|
||||
|
||||
map[x, y] = null
|
||||
map[this@World.x.chunk(x), this@World.y.chunk(y)] = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import java.io.DataInputStream
|
||||
|
||||
sealed class AbstractCell {
|
||||
abstract val foreground: AbstractTileState
|
||||
abstract val background: AbstractTileState
|
||||
abstract val liquid: AbstractLiquidState
|
||||
|
||||
abstract val dungeonId: Int
|
||||
abstract val biome: Int
|
||||
abstract val envBiome: Int
|
||||
abstract val isIndestructible: Boolean
|
||||
|
||||
abstract fun immutable(): ImmutableCell
|
||||
abstract fun mutable(): MutableCell
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
AbstractTileState.skip(stream)
|
||||
AbstractTileState.skip(stream)
|
||||
AbstractLiquidState.skip(stream)
|
||||
stream.skipNBytes(1 + 2 + 1 + 1 + 1 + 1)
|
||||
}
|
||||
|
||||
val EMPTY = ImmutableCell(AbstractTileState.EMPTY, AbstractTileState.EMPTY, AbstractLiquidState.EMPTY, 0, 0, 0, false)
|
||||
val NULL = ImmutableCell(AbstractTileState.NULL, AbstractTileState.NULL, AbstractLiquidState.EMPTY, 0, 0, 0, false)
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import java.io.DataInputStream
|
||||
|
||||
sealed class AbstractLiquidState {
|
||||
abstract val def: LiquidDefinition?
|
||||
abstract val level: Float
|
||||
abstract val pressure: Float
|
||||
abstract val isInfinite: Boolean
|
||||
|
||||
abstract fun mutable(): MutableLiquidState
|
||||
abstract fun immutable(): ImmutableLiquidState
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
stream.skipNBytes(1 + 4 + 4 + 1)
|
||||
}
|
||||
|
||||
val EMPTY = ImmutableLiquidState()
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import java.io.DataInputStream
|
||||
|
||||
sealed class AbstractTileState {
|
||||
abstract val material: TileDefinition
|
||||
abstract val modifier: MaterialModifier?
|
||||
abstract val color: TileColor
|
||||
abstract val hueShift: Float
|
||||
abstract val modifierHueShift: Float
|
||||
|
||||
abstract fun immutable(): ImmutableTileState
|
||||
abstract fun mutable(): MutableTileState
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
stream.skipNBytes(2 + 1 + 1 + 2 + 1)
|
||||
}
|
||||
|
||||
val EMPTY = ImmutableTileState(BuiltinMetaMaterials.EMPTY)
|
||||
val NULL = ImmutableTileState(BuiltinMetaMaterials.NULL)
|
||||
}
|
||||
}
|
@ -5,19 +5,22 @@ import ru.dbotthepony.kvector.vector.Vector2i
|
||||
|
||||
interface ICellAccess {
|
||||
/**
|
||||
* non-null - valid cell (maybe wrapped around)
|
||||
* null - invalid cell (outside world bounds)
|
||||
* with wrap-around
|
||||
*/
|
||||
fun getCell(x: Int, y: Int): IChunkCell?
|
||||
fun getCell(x: Int, y: Int): AbstractCell
|
||||
fun getCell(pos: IStruct2i) = getCell(pos.component1(), pos.component2())
|
||||
|
||||
/**
|
||||
* non-null - valid cell and not wrapped around
|
||||
* null - invalid cell (outside world bounds)
|
||||
* without wrap-around
|
||||
*/
|
||||
fun getCellDirect(x: Int, y: Int): IChunkCell?
|
||||
fun getCellDirect(x: Int, y: Int): AbstractCell
|
||||
fun getCellDirect(pos: IStruct2i) = getCellDirect(pos.component1(), pos.component2())
|
||||
|
||||
/**
|
||||
* whenever cell was set
|
||||
*/
|
||||
fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean
|
||||
|
||||
/**
|
||||
* Возвращает псевдослучайное Long для заданной позиции
|
||||
*
|
||||
@ -45,19 +48,3 @@ interface ICellAccess {
|
||||
fun randomDoubleFor(pos: Vector2i) = randomDoubleFor(pos.x, pos.y)
|
||||
}
|
||||
|
||||
class OffsetCellAccess(private val parent: ICellAccess, var x: Int, var y: Int) : ICellAccess {
|
||||
constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2())
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||
return parent.getCell(x + this.x, y + this.y)
|
||||
}
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): IChunkCell? {
|
||||
return parent.getCellDirect(x + this.x, y + this.y)
|
||||
}
|
||||
|
||||
override fun randomLongFor(x: Int, y: Int) = parent.randomLongFor(x + this.x, y + this.y)
|
||||
override fun randomDoubleFor(x: Int, y: Int) = parent.randomDoubleFor(x + this.x, y + this.y)
|
||||
override fun randomLongFor(pos: Vector2i) = parent.randomLongFor(pos.x + this.x, pos.y + this.y)
|
||||
override fun randomDoubleFor(pos: Vector2i) = parent.randomDoubleFor(pos.x + this.x, pos.y + this.y)
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.RegistryObject
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import java.io.DataInputStream
|
||||
|
||||
private val rect = Poly(listOf(Vector2d.ZERO, Vector2d(0.0, 1.0), Vector2d(1.0, 1.0), Vector2d(1.0, 0.0)))
|
||||
|
||||
interface IChunkCell : IStruct2i {
|
||||
/**
|
||||
* absolute (in world)
|
||||
*/
|
||||
val x: Int
|
||||
|
||||
/**
|
||||
* absolute (in world)
|
||||
*/
|
||||
val y: Int
|
||||
|
||||
override fun component1(): Int {
|
||||
return x
|
||||
}
|
||||
|
||||
override fun component2(): Int {
|
||||
return y
|
||||
}
|
||||
|
||||
val foreground: ITileState
|
||||
val background: ITileState
|
||||
val liquid: ILiquidState
|
||||
|
||||
var dungeonId: Int
|
||||
var biome: Int
|
||||
var envBiome: Int
|
||||
var isIndestructible: Boolean
|
||||
|
||||
fun read(stream: DataInputStream) {
|
||||
foreground.read(stream)
|
||||
background.read(stream)
|
||||
liquid.read(stream)
|
||||
|
||||
stream.skipNBytes(1) // collisionMap
|
||||
|
||||
dungeonId = stream.readUnsignedShort()
|
||||
biome = stream.readUnsignedByte()
|
||||
envBiome = stream.readUnsignedByte()
|
||||
isIndestructible = stream.readBoolean()
|
||||
|
||||
stream.skipNBytes(1) // unknown
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
ITileState.skip(stream)
|
||||
ITileState.skip(stream)
|
||||
ILiquidState.skip(stream)
|
||||
stream.skipNBytes(1 + 2 + 1 + 1 + 1 + 1)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.RegistryObject
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import java.io.DataInputStream
|
||||
|
||||
interface ILiquidState {
|
||||
var def: LiquidDefinition?
|
||||
var level: Float
|
||||
var pressure: Float
|
||||
var isInfinite: Boolean
|
||||
|
||||
fun read(stream: DataInputStream) {
|
||||
def = Registries.liquid[stream.readUnsignedByte()]?.value
|
||||
level = stream.readFloat()
|
||||
pressure = stream.readFloat()
|
||||
isInfinite = stream.readBoolean()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
stream.skipNBytes(1 + 4 + 4 + 1)
|
||||
}
|
||||
}
|
||||
}
|
@ -5,30 +5,8 @@ import ru.dbotthepony.kvector.api.IStruct2i
|
||||
// for getting tiles directly, avoiding manual layer specification
|
||||
interface ITileAccess : ICellAccess {
|
||||
// relative
|
||||
fun getTile(x: Int, y: Int): ITileState?
|
||||
fun getTile(x: Int, y: Int): AbstractTileState?
|
||||
fun getTile(pos: IStruct2i) = getTile(pos.component1(), pos.component2())
|
||||
fun getTileDirect(x: Int, y: Int): ITileState?
|
||||
fun getTileDirect(x: Int, y: Int): AbstractTileState?
|
||||
fun getTileDirect(pos: IStruct2i) = getTile(pos.component1(), pos.component2())
|
||||
}
|
||||
|
||||
sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent {
|
||||
class Foreground(parent: ICellAccess) : TileView(parent) {
|
||||
override fun getTile(x: Int, y: Int): ITileState? {
|
||||
return getCell(x, y)?.foreground
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): ITileState? {
|
||||
return getCellDirect(x, y)?.foreground
|
||||
}
|
||||
}
|
||||
|
||||
class Background(parent: ICellAccess) : TileView(parent) {
|
||||
override fun getTile(x: Int, y: Int): ITileState? {
|
||||
return getCell(x, y)?.background
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): ITileState? {
|
||||
return getCellDirect(x, y)?.background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
data class ImmutableCell(
|
||||
override val foreground: ImmutableTileState = AbstractTileState.EMPTY,
|
||||
override val background: ImmutableTileState = AbstractTileState.EMPTY,
|
||||
override val liquid: ImmutableLiquidState = AbstractLiquidState.EMPTY,
|
||||
|
||||
override val dungeonId: Int = 0,
|
||||
override val biome: Int = 0,
|
||||
override val envBiome: Int = 0,
|
||||
override val isIndestructible: Boolean = false,
|
||||
) : AbstractCell() {
|
||||
override fun immutable(): ImmutableCell {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun mutable(): MutableCell {
|
||||
return MutableCell(foreground.mutable(), background.mutable(), liquid.mutable(), dungeonId, biome, envBiome, isIndestructible)
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
|
||||
data class ImmutableLiquidState(
|
||||
override val def: LiquidDefinition? = null,
|
||||
override val level: Float = 0f,
|
||||
override val pressure: Float = 0f,
|
||||
override val isInfinite: Boolean = false,
|
||||
) : AbstractLiquidState() {
|
||||
override fun mutable(): MutableLiquidState {
|
||||
return MutableLiquidState(def, level, pressure, isInfinite)
|
||||
}
|
||||
|
||||
override fun immutable(): ImmutableLiquidState {
|
||||
return this
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
|
||||
data class ImmutableTileState(
|
||||
override var material: TileDefinition = BuiltinMetaMaterials.NULL,
|
||||
override var modifier: MaterialModifier? = null,
|
||||
override var color: TileColor = TileColor.DEFAULT,
|
||||
override var hueShift: Float = 0f,
|
||||
override var modifierHueShift: Float = 0f,
|
||||
) : AbstractTileState() {
|
||||
override fun immutable(): ImmutableTileState {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun mutable(): MutableTileState {
|
||||
return MutableTileState(material, modifier, color, hueShift, modifierHueShift)
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import java.io.DataInputStream
|
||||
|
||||
data class MutableCell(
|
||||
override var foreground: MutableTileState = MutableTileState(),
|
||||
override var background: MutableTileState = MutableTileState(),
|
||||
override var liquid: MutableLiquidState = MutableLiquidState(),
|
||||
|
||||
override var dungeonId: Int = 0,
|
||||
override var biome: Int = 0,
|
||||
override var envBiome: Int = 0,
|
||||
override var isIndestructible: Boolean = false,
|
||||
) : AbstractCell() {
|
||||
fun read(stream: DataInputStream): MutableCell {
|
||||
foreground.read(stream)
|
||||
background.read(stream)
|
||||
liquid.read(stream)
|
||||
|
||||
stream.skipNBytes(1) // collisionMap
|
||||
|
||||
dungeonId = stream.readUnsignedShort()
|
||||
biome = stream.readUnsignedByte()
|
||||
envBiome = stream.readUnsignedByte()
|
||||
isIndestructible = stream.readBoolean()
|
||||
|
||||
stream.skipNBytes(1) // unknown
|
||||
return this
|
||||
}
|
||||
|
||||
override fun immutable(): ImmutableCell {
|
||||
return ImmutableCell(foreground.immutable(), background.immutable(), liquid.immutable(), dungeonId, biome, envBiome, isIndestructible)
|
||||
}
|
||||
|
||||
override fun mutable(): MutableCell {
|
||||
return this
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import java.io.DataInputStream
|
||||
|
||||
data class MutableLiquidState(
|
||||
override var def: LiquidDefinition? = null,
|
||||
override var level: Float = 0f,
|
||||
override var pressure: Float = 0f,
|
||||
override var isInfinite: Boolean = false,
|
||||
) : AbstractLiquidState() {
|
||||
fun read(stream: DataInputStream): MutableLiquidState {
|
||||
def = Registries.liquid[stream.readUnsignedByte()]?.value
|
||||
level = stream.readFloat()
|
||||
pressure = stream.readFloat()
|
||||
isInfinite = stream.readBoolean()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun mutable(): MutableLiquidState {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun immutable(): ImmutableLiquidState {
|
||||
return ImmutableLiquidState(def, level, pressure, isInfinite)
|
||||
}
|
||||
}
|
@ -1,20 +1,30 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.RegistryObject
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import java.io.DataInputStream
|
||||
|
||||
interface ITileState {
|
||||
var material: TileDefinition
|
||||
var modifier: MaterialModifier?
|
||||
var color: TileColor
|
||||
var hueShift: Float
|
||||
var modifierHueShift: Float
|
||||
data class MutableTileState(
|
||||
override var material: TileDefinition = BuiltinMetaMaterials.NULL,
|
||||
override var modifier: MaterialModifier? = null,
|
||||
override var color: TileColor = TileColor.DEFAULT,
|
||||
override var hueShift: Float = 0f,
|
||||
override var modifierHueShift: Float = 0f,
|
||||
) : AbstractTileState() {
|
||||
override fun immutable(): ImmutableTileState {
|
||||
val result = ImmutableTileState(material, modifier, color, hueShift, modifierHueShift)
|
||||
if (result == NULL) return NULL
|
||||
if (result == EMPTY) return EMPTY
|
||||
return result
|
||||
}
|
||||
|
||||
fun setHueShift(value: Int) {
|
||||
override fun mutable(): MutableTileState {
|
||||
return this
|
||||
}
|
||||
|
||||
fun setHueShift(value: Int): MutableTileState {
|
||||
if (value < 0) {
|
||||
hueShift = 0f
|
||||
} else if (value >= 255) {
|
||||
@ -22,9 +32,11 @@ interface ITileState {
|
||||
} else {
|
||||
hueShift = value / 255f * 360f
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun setModHueShift(value: Int) {
|
||||
fun setModHueShift(value: Int): MutableTileState {
|
||||
if (value < 0) {
|
||||
modifierHueShift = 0f
|
||||
} else if (value >= 255) {
|
||||
@ -32,19 +44,16 @@ interface ITileState {
|
||||
} else {
|
||||
modifierHueShift = value / 255f * 360f
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun read(stream: DataInputStream) {
|
||||
fun read(stream: DataInputStream): MutableTileState {
|
||||
material = Registries.tiles[stream.readUnsignedShort()]?.value ?: BuiltinMetaMaterials.EMPTY
|
||||
setHueShift(stream.read())
|
||||
color = TileColor.of(stream.read())
|
||||
modifier = Registries.tileModifiers[stream.readUnsignedShort()]?.value
|
||||
setModHueShift(stream.read())
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun skip(stream: DataInputStream) {
|
||||
stream.skipNBytes(2 + 1 + 1 + 2 + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
|
||||
class OffsetCellAccess(private val parent: ICellAccess, var x: Int, var y: Int) : ICellAccess {
|
||||
constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2())
|
||||
|
||||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||||
return parent.getCell(x + this.x, y + this.y)
|
||||
}
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): AbstractCell {
|
||||
return parent.getCellDirect(x + this.x, y + this.y)
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
return parent.setCell(x, y, cell)
|
||||
}
|
||||
|
||||
override fun randomLongFor(x: Int, y: Int) = parent.randomLongFor(x + this.x, y + this.y)
|
||||
override fun randomDoubleFor(x: Int, y: Int) = parent.randomDoubleFor(x + this.x, y + this.y)
|
||||
override fun randomLongFor(pos: Vector2i) = parent.randomLongFor(pos.x + this.x, pos.y + this.y)
|
||||
override fun randomDoubleFor(pos: Vector2i) = parent.randomDoubleFor(pos.x + this.x, pos.y + this.y)
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent {
|
||||
class Foreground(parent: ICellAccess) : TileView(parent) {
|
||||
override fun getTile(x: Int, y: Int): AbstractTileState? {
|
||||
return getCell(x, y)?.foreground
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): AbstractTileState? {
|
||||
return getCellDirect(x, y)?.foreground
|
||||
}
|
||||
}
|
||||
|
||||
class Background(parent: ICellAccess) : TileView(parent) {
|
||||
override fun getTile(x: Int, y: Int): AbstractTileState? {
|
||||
return getCell(x, y)?.background
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): AbstractTileState? {
|
||||
return getCellDirect(x, y)?.background
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user