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, This : Chunk>( 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) { 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()), ) } }