169 lines
4.6 KiB
Kotlin
169 lines
4.6 KiB
Kotlin
package ru.dbotthepony.kstarbound.world
|
||
|
||
import ru.dbotthepony.kommons.arrays.Object2DArray
|
||
import ru.dbotthepony.kommons.util.AABB
|
||
import ru.dbotthepony.kommons.vector.Vector2d
|
||
import ru.dbotthepony.kstarbound.network.LegacyNetworkCellState
|
||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||
import ru.dbotthepony.kstarbound.world.api.ImmutableCell
|
||
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
||
import ru.dbotthepony.kstarbound.world.api.TileView
|
||
import java.util.concurrent.CopyOnWriteArraySet
|
||
|
||
/**
|
||
* Чанк мира
|
||
*
|
||
* Хранит в себе тайлы и ентити внутри себя
|
||
*
|
||
* Считается, что один тайл имеет форму квадрата и сторона квадрата примерно равна полуметру,
|
||
* что будет называться Starbound Unit
|
||
*
|
||
* Весь игровой мир будет измеряться в Starbound Unit'ах
|
||
*/
|
||
abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType, This>>(
|
||
val world: WorldType,
|
||
val pos: ChunkPos,
|
||
) : ICellAccess {
|
||
var changeset = 0
|
||
private set
|
||
|
||
var tileChangeset = 0
|
||
private set
|
||
var liquidChangeset = 0
|
||
private set
|
||
var cellChangeset = 0
|
||
private set
|
||
|
||
var foregroundChangeset = 0
|
||
private set
|
||
var backgroundChangeset = 0
|
||
private set
|
||
|
||
val width = (world.geometry.size.x - pos.tileX).coerceAtMost(CHUNK_SIZE)
|
||
val height = (world.geometry.size.y - pos.tileY).coerceAtMost(CHUNK_SIZE)
|
||
|
||
// local cells' tile access
|
||
val localBackgroundView = TileView.Background(this)
|
||
val localForegroundView = TileView.Foreground(this)
|
||
|
||
// relative world cells access (accessing 0, 0 will lookup cell in world, relative to this chunk)
|
||
val worldView = OffsetCellAccess(world, pos.x * CHUNK_SIZE, pos.y * CHUNK_SIZE)
|
||
val worldBackgroundView = TileView.Background(worldView)
|
||
val worldForegroundView = TileView.Foreground(worldView)
|
||
|
||
val aabb = aabbBase + Vector2d(pos.x * CHUNK_SIZE.toDouble(), pos.y * CHUNK_SIZE.toDouble())
|
||
|
||
// TODO: maybe fit them into "width" and "height" variables added recently?
|
||
protected val cells = lazy {
|
||
Object2DArray(CHUNK_SIZE, CHUNK_SIZE, AbstractCell.NULL)
|
||
}
|
||
|
||
protected val tileHealthForeground = lazy {
|
||
Object2DArray(CHUNK_SIZE, CHUNK_SIZE) { _, _ -> TileHealth.Tile() }
|
||
}
|
||
|
||
protected val tileHealthBackground = lazy {
|
||
Object2DArray(CHUNK_SIZE, CHUNK_SIZE) { _, _ -> TileHealth.Tile() }
|
||
}
|
||
|
||
fun loadCells(source: Object2DArray<out AbstractCell>) {
|
||
val ours = cells.value
|
||
source.checkSizeEquals(ours)
|
||
|
||
for (x in 0 until CHUNK_SIZE) {
|
||
for (y in 0 until CHUNK_SIZE) {
|
||
ours[x, y] = source[x, y].immutable()
|
||
}
|
||
}
|
||
}
|
||
|
||
override fun getCell(x: Int, y: Int): AbstractCell {
|
||
if (!cells.isInitialized())
|
||
return AbstractCell.NULL
|
||
|
||
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 old = if (cells.isInitialized()) cells.value[x, y] else AbstractCell.NULL
|
||
val new = cell.immutable()
|
||
|
||
if (old != new) {
|
||
cells.value[x, y] = new
|
||
|
||
if (old.foreground != new.foreground) {
|
||
foregroundChanges(x, y, new)
|
||
}
|
||
|
||
if (old.background != new.background) {
|
||
backgroundChanges(x, y, new)
|
||
}
|
||
|
||
if (old.liquid != new.liquid) {
|
||
liquidChanges(x, y, new)
|
||
}
|
||
|
||
cellChanges(x, y, new)
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
protected open fun foregroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||
cellChanges(x, y, cell)
|
||
tileChangeset++
|
||
foregroundChangeset++
|
||
}
|
||
|
||
protected open fun backgroundChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||
cellChanges(x, y, cell)
|
||
tileChangeset++
|
||
backgroundChangeset++
|
||
}
|
||
|
||
protected open fun liquidChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||
cellChanges(x, y, cell)
|
||
liquidChangeset++
|
||
}
|
||
|
||
protected open fun cellChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||
changeset++
|
||
cellChangeset++
|
||
}
|
||
|
||
protected inline fun forEachNeighbour(block: (This) -> Unit) {
|
||
world.chunkMap[pos.left]?.let(block)
|
||
world.chunkMap[pos.right]?.let(block)
|
||
world.chunkMap[pos.top]?.let(block)
|
||
world.chunkMap[pos.bottom]?.let(block)
|
||
world.chunkMap[pos.topLeft]?.let(block)
|
||
world.chunkMap[pos.topRight]?.let(block)
|
||
world.chunkMap[pos.bottomLeft]?.let(block)
|
||
world.chunkMap[pos.bottomRight]?.let(block)
|
||
}
|
||
|
||
override fun toString(): String {
|
||
return "${this::class.simpleName}(pos=$pos, world=$world)"
|
||
}
|
||
|
||
open fun remove() {
|
||
|
||
}
|
||
|
||
open fun tick() {
|
||
|
||
}
|
||
|
||
companion object {
|
||
private val aabbBase = AABB(
|
||
Vector2d.ZERO,
|
||
Vector2d(CHUNK_SIZE.toDouble(), CHUNK_SIZE.toDouble()),
|
||
)
|
||
}
|
||
}
|