package ru.dbotthepony.kstarbound.world import ru.dbotthepony.kstarbound.math.roundByAbsoluteValue import ru.dbotthepony.kstarbound.world.api.CHUNK_SIZE_BITS import ru.dbotthepony.kvector.api.IStruct2d import ru.dbotthepony.kvector.api.IStruct2i import ru.dbotthepony.kvector.vector.Vector2i private fun circulate(value: Int, bounds: Int): Int { require(bounds > 0) { "Bounds must be positive ($bounds given)" } if (value >= bounds) { return value % bounds } else if (value < 0) { return bounds + value % bounds } return value } /** * Сетка чанков идёт как и сетка тайлов. * * * Вправо у нас положительный X * * Влево у нас отрицательный X * * Вверх у нас положительный Y * * Вниз у нас отрицательный Y */ class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> { constructor(pos: IStruct2i) : this(pos.component1(), pos.component2()) val firstBlock get() = Vector2i(tileX, tileY) val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SIZE_BITS) - 1, ((y + 1) shl CHUNK_SIZE_BITS) - 1) /** * Координата тайла на 0 позиции по оси X внутри чанка в мире */ val tileX: Int get() = x shl CHUNK_SIZE_BITS /** * Координата тайла на 0 позиции по оси Y внутри чанка в мире */ val tileY: Int get() = y shl CHUNK_SIZE_BITS val top: ChunkPos get() { return ChunkPos(x, y + 1) } val bottom: ChunkPos get() { return ChunkPos(x, y - 1) } val left: ChunkPos get() { return ChunkPos(x - 1, y) } val topLeft: ChunkPos get() { return ChunkPos(x - 1, y + 1) } val topRight: ChunkPos get() { return ChunkPos(x + 1, y + 1) } val bottomLeft: ChunkPos get() { return ChunkPos(x - 1, y - 1) } val bottomRight: ChunkPos get() { return ChunkPos(x + 1, y - 1) } val right: ChunkPos get() { return ChunkPos(x + 1, y) } override fun equals(other: Any?): Boolean { if (other is ChunkPos) return other.x == x && other.y == y return false } override fun hashCode(): Int { return (y shl 16) xor x } override fun toString(): String { return "ChunkPos[$x $y]" } override fun compareTo(other: ChunkPos): Int { if (x > other.x) { return 1 } else if (x < other.x) { return -1 } return y.compareTo(other.y) } fun circular(xWrap: Int): ChunkPos { val x = circulate(x, xWrap) if (x == this.x) { return this } return ChunkPos(x, y) } fun toLong(): Long { return toLong(x, y) } companion object { val ZERO = ChunkPos(0, 0) fun toLong(x: Int, y: Int): Long { return x.toLong() or (y.toLong() shl 32) } fun longFromPosition(x: Int, y: Int): Long { return toLong(component(x), component(y)) } fun fromPosition(input: IStruct2i): ChunkPos { val (x, y) = input return ChunkPos(component(x), component(y)) } fun fromPosition(input: IStruct2d): ChunkPos { val (x, y) = input return fromPosition(x, y) } fun fromPosition(x: Int, y: Int): ChunkPos { return ChunkPos(component(x), component(y)) } fun fromPosition(x: Int, y: Int, xWrap: Int): ChunkPos { return ChunkPos(circulate(component(x), xWrap), component(y)) } fun fromPosition(x: Double, y: Double): ChunkPos { return ChunkPos( component(roundByAbsoluteValue(x)), component(roundByAbsoluteValue(y)) ) } fun fromPosition(x: Double, y: Double, xWrap: Int): ChunkPos { return ChunkPos( circulate(component(roundByAbsoluteValue(x)), xWrap), component(roundByAbsoluteValue(y)) ) } fun component(value: Int): Int { if (value < 0) { return -((-value) shr CHUNK_SIZE_BITS) - 1 } return value shr CHUNK_SIZE_BITS } } }