Original game tile collision geometry
This commit is contained in:
parent
d782b33970
commit
792e0c6ff6
@ -69,7 +69,7 @@ fun main() {
|
||||
for (chunkX in 0 .. 100) {
|
||||
//for (chunkX in 0 .. 17) {
|
||||
// for (chunkY in 21 .. 21) {
|
||||
for (chunkY in 0 .. 24) {
|
||||
for (chunkY in 18 .. 24) {
|
||||
val data = db.read(byteArrayOf(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte()))
|
||||
val data2 = db.read(byteArrayOf(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte()))
|
||||
|
||||
@ -116,7 +116,7 @@ fun main() {
|
||||
|
||||
val rand = Random()
|
||||
|
||||
for (i in 0 until 0) {
|
||||
for (i in 0 until 128) {
|
||||
val item = ItemEntity(client.world!!, Registries.items.keys.values.random().value)
|
||||
|
||||
item.position = Vector2d(225.0 - i, 785.0)
|
||||
|
@ -167,6 +167,7 @@ class Registry<T : Any>(val name: String) {
|
||||
|
||||
private inner class RefImpl(override val key: Either<String, Int>) : Ref<T>() {
|
||||
override var entry: Entry<T>? = null
|
||||
var references = 0
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other || other is Registry<*>.RefImpl && other.key == key && other.registry == registry
|
||||
@ -192,7 +193,9 @@ class Registry<T : Any>(val name: String) {
|
||||
val ref = RefImpl(Either.left(it as String))
|
||||
ref.entry = keysInternal[it]
|
||||
ref
|
||||
})
|
||||
}).also {
|
||||
it.references++
|
||||
}
|
||||
}
|
||||
|
||||
fun ref(index: Int): Ref<T> = lock.withLock {
|
||||
@ -200,7 +203,9 @@ class Registry<T : Any>(val name: String) {
|
||||
val ref = RefImpl(Either.right(it))
|
||||
ref.entry = idsInternal[it]
|
||||
ref
|
||||
})
|
||||
}).also {
|
||||
it.references++
|
||||
}
|
||||
}
|
||||
|
||||
operator fun contains(index: String) = lock.withLock { index in keysInternal }
|
||||
@ -211,14 +216,14 @@ class Registry<T : Any>(val name: String) {
|
||||
|
||||
keyRefs.values.forEach {
|
||||
if (!it.isPresent) {
|
||||
LOGGER.warn("Registry '$name' reference at '${it.key.left()}' is not bound to value, expect problems")
|
||||
LOGGER.warn("Registry '$name' reference at '${it.key.left()}' is not bound to value, expect problems (referenced ${it.references} times)")
|
||||
any = false
|
||||
}
|
||||
}
|
||||
|
||||
idRefs.values.forEach {
|
||||
if (!it.isPresent) {
|
||||
LOGGER.warn("Registry '$name' reference with ID '${it.key.right()}' is not bound to value, expect problems")
|
||||
LOGGER.warn("Registry '$name' reference with ID '${it.key.right()}' is not bound to value, expect problems (referenced ${it.references} times)")
|
||||
any = false
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ class Image private constructor(
|
||||
private val dataCache: AsyncLoadingCache<IStarboundFile, ByteBuffer> = Caffeine.newBuilder()
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
.weigher<IStarboundFile, ByteBuffer> { key, value -> value.capacity() }
|
||||
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
|
||||
.maximumWeight((Runtime.getRuntime().maxMemory() / 4L).coerceIn(1_024L * 1_024L * 32L /* 32 МиБ */, 1_024L * 1_024L * 256L /* 256 МиБ */))
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.buildAsync(CacheLoader {
|
||||
val getWidth = intArrayOf(0)
|
||||
|
@ -15,11 +15,15 @@ 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.kstarbound.world.physics.getBlockPlatforms
|
||||
import ru.dbotthepony.kstarbound.world.physics.getBlocksMarchingSquares
|
||||
import ru.dbotthepony.kvector.api.IStruct2d
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.util2d.AABBi
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
@ -95,6 +99,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
}
|
||||
}
|
||||
|
||||
// around 30% slower than ArrayChunkMap, but can support insanely large worlds
|
||||
inner class SparseChunkMap : ChunkMap() {
|
||||
private val map = Long2ObjectOpenHashMap<ChunkType>()
|
||||
|
||||
@ -222,62 +227,11 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
for (x in tiles.mins.x .. tiles.maxs.x) {
|
||||
for (y in tiles.mins.y .. tiles.maxs.y) {
|
||||
val cell = getCell(x, y) ?: continue
|
||||
|
||||
result.add(CollisionPoly(
|
||||
BLOCK_POLY + Vector2d(x.toDouble(), y.toDouble()),
|
||||
cell.foreground.material.value.collisionKind,
|
||||
//velocity = Vector2d(EARTH_FREEFALL_ACCELERATION, 0.0)
|
||||
))
|
||||
getBlocksMarchingSquares(x, y, foreground, CollisionType.DYNAMIC, result)
|
||||
getBlockPlatforms(x, y, foreground, CollisionType.PLATFORM, result)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val POLY_VERTICES: ImmutableList<Vector2d> = ImmutableList.of(
|
||||
Vector2d(0.5, 0.5),
|
||||
Vector2d(0.5, 1.0),
|
||||
Vector2d(0.5, 1.5),
|
||||
Vector2d(1.0, 1.5),
|
||||
Vector2d(1.5, 1.5),
|
||||
Vector2d(1.5, 1.0),
|
||||
Vector2d(1.5, 0.5),
|
||||
Vector2d(1.0, 0.5),
|
||||
Vector2d(1.0, 1.0)
|
||||
)
|
||||
|
||||
private val POLY_INDICES: ImmutableList<IntList> = ImmutableList.of(
|
||||
IntList.of(9, 9, 9, 9, 9, 9),
|
||||
IntList.of(1, 2, 3, 9, 9, 9),
|
||||
IntList.of(3, 4, 5, 9, 9, 9),
|
||||
IntList.of(1, 2, 4, 5, 9, 9),
|
||||
IntList.of(7, 5, 6, 9, 9, 9),
|
||||
IntList.of(1, 2, 3, 5, 6, 7),
|
||||
IntList.of(7, 3, 4, 6, 9, 9),
|
||||
IntList.of(1, 2, 4, 6, 7, 9),
|
||||
IntList.of(0, 1, 7, 9, 9, 9),
|
||||
IntList.of(0, 2, 3, 7, 9, 9),
|
||||
IntList.of(0, 1, 3, 4, 5, 7),
|
||||
IntList.of(0, 2, 4, 5, 7, 9),
|
||||
IntList.of(0, 1, 5, 6, 9, 9),
|
||||
IntList.of(0, 2, 3, 5, 6, 9),
|
||||
IntList.of(0, 1, 3, 4, 6, 9),
|
||||
IntList.of(0, 2, 4, 6, 9, 9),
|
||||
// special cases for squared off top corners
|
||||
IntList.of(5, 6, 7, 8, 9, 9), // top left corner
|
||||
IntList.of(0, 1, 8, 7, 9, 9), // top right corner
|
||||
// special cases for hollowed out bottom corners
|
||||
IntList.of(0, 2, 3, 8, 9, 9), // lower left corner part 1
|
||||
IntList.of(0, 8, 5, 6, 9, 9), // lower left corner part 2
|
||||
IntList.of(0, 1, 8, 6, 9, 9), // lower right corner part 1
|
||||
IntList.of(6, 8, 3, 4, 9, 9) // lower right corner part 2
|
||||
)
|
||||
|
||||
private val BLOCK_POLY = Poly(listOf(Vector2d.ZERO, Vector2d(0.0, 1.0), Vector2d(1.0, 1.0), Vector2d(1.0, 0.0)))
|
||||
private val PEAK = Poly(listOf(Vector2d(0.0, 0.0), Vector2d(0.5, 1.0), Vector2d(1.0, 0.0)))
|
||||
private val PX_NY = Poly(listOf(Vector2d(0.0, 0.0), Vector2d(0.0, 1.0), Vector2d(1.0, 0.0)))
|
||||
private val NX_NY = Poly(listOf(Vector2d(-1.0, 0.0), Vector2d(0.0, 1.0), Vector2d(0.0, 0.0)))
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionTypeGetter
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
|
||||
// for getting tiles directly, avoiding manual layer specification
|
||||
interface ITileAccess : ICellAccess {
|
||||
interface ITileAccess : ICellAccess, CollisionTypeGetter {
|
||||
// relative
|
||||
fun getTile(x: Int, y: Int): AbstractTileState?
|
||||
fun getTile(x: Int, y: Int): AbstractTileState
|
||||
fun getTile(pos: IStruct2i) = getTile(pos.component1(), pos.component2())
|
||||
fun getTileDirect(x: Int, y: Int): AbstractTileState?
|
||||
fun getTileDirect(x: Int, y: Int): AbstractTileState
|
||||
fun getTileDirect(pos: IStruct2i) = getTile(pos.component1(), pos.component2())
|
||||
|
||||
override fun collisionType(x: Int, y: Int): CollisionType {
|
||||
return getTile(x, y).material.value.collisionKind
|
||||
}
|
||||
}
|
||||
|
@ -2,22 +2,22 @@ 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 getTile(x: Int, y: Int): AbstractTileState {
|
||||
return getCell(x, y).foreground
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): AbstractTileState? {
|
||||
return getCellDirect(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 getTile(x: Int, y: Int): AbstractTileState {
|
||||
return getCell(x, y).background
|
||||
}
|
||||
|
||||
override fun getTileDirect(x: Int, y: Int): AbstractTileState? {
|
||||
return getCellDirect(x, y)?.background
|
||||
override fun getTileDirect(x: Int, y: Int): AbstractTileState {
|
||||
return getCellDirect(x, y).background
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import ru.dbotthepony.kvector.vector.RGBAColor
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import java.util.EnumSet
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
abstract class Entity(val world: World<*, *>) {
|
||||
var chunk: Chunk<*, *>? = null
|
||||
@ -202,7 +203,7 @@ abstract class Entity(val world: World<*, *>) {
|
||||
// impulse?
|
||||
velocity += max.data.velocity * gravityDot * dt
|
||||
// friction
|
||||
velocity *= 1.0 - gravityDot * 0.08
|
||||
velocity *= 1.0 - gravityDot.absoluteValue * 0.08
|
||||
|
||||
onTouch(response, max.axis, max.data)
|
||||
}
|
||||
|
@ -0,0 +1,182 @@
|
||||
package ru.dbotthepony.kstarbound.world.physics
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
|
||||
private val POLY_VERTICES: ImmutableList<Vector2d> = ImmutableList.of(
|
||||
Vector2d(0.5, 0.5),
|
||||
Vector2d(0.5, 1.0),
|
||||
Vector2d(0.5, 1.5),
|
||||
Vector2d(1.0, 1.5),
|
||||
Vector2d(1.5, 1.5),
|
||||
Vector2d(1.5, 1.0),
|
||||
Vector2d(1.5, 0.5),
|
||||
Vector2d(1.0, 0.5),
|
||||
Vector2d(1.0, 1.0)
|
||||
)
|
||||
|
||||
private val POLIES = listOf(
|
||||
IntList.of(1, 2, 3),
|
||||
IntList.of(3, 4, 5),
|
||||
IntList.of(1, 2, 4, 5),
|
||||
IntList.of(7, 5, 6),
|
||||
IntList.of(1, 2, 3, 5, 6, 7),
|
||||
IntList.of(7, 3, 4, 6),
|
||||
IntList.of(1, 2, 4, 6, 7),
|
||||
IntList.of(0, 1, 7),
|
||||
IntList.of(0, 2, 3, 7),
|
||||
IntList.of(0, 1, 3, 4, 5, 7),
|
||||
IntList.of(0, 2, 4, 5, 7),
|
||||
IntList.of(0, 1, 5, 6),
|
||||
IntList.of(0, 2, 3, 5, 6),
|
||||
IntList.of(0, 1, 3, 4, 6),
|
||||
IntList.of(0, 2, 4, 6),
|
||||
// special cases for squared off top corners
|
||||
IntList.of(5, 6, 7, 8), // top left corner
|
||||
IntList.of(0, 1, 8, 7), // top right corner
|
||||
// special cases for hollowed out bottom corners
|
||||
IntList.of(0, 2, 3, 8), // lower left corner part 1
|
||||
IntList.of(0, 8, 5, 6), // lower left corner part 2
|
||||
IntList.of(0, 1, 8, 6), // lower right corner part 1
|
||||
IntList.of(6, 8, 3, 4) // lower right corner part 2
|
||||
).map { Poly(it.map { POLY_VERTICES[it] }) }
|
||||
|
||||
fun interface CollisionTypeGetter {
|
||||
fun collisionType(x: Int, y: Int): CollisionType
|
||||
}
|
||||
|
||||
private fun MutableList<CollisionPoly>.addBlock(x: Int, y: Int, getter: CollisionTypeGetter, index: Int) {
|
||||
val poly = POLIES.getOrNull(index - 1) ?: return
|
||||
|
||||
add(CollisionPoly(
|
||||
poly + Vector2d(x.toDouble(), y.toDouble()),
|
||||
getter.collisionType(x, y).coerceAtLeast(getter.collisionType(x + 1, y)).coerceAtLeast(getter.collisionType(x + 1, y + 1)).coerceAtLeast(getter.collisionType(x, y + 1))
|
||||
))
|
||||
}
|
||||
|
||||
private fun MutableList<CollisionPoly>.addBlock(poly: List<Vector2d>, kind: CollisionType) {
|
||||
add(CollisionPoly(
|
||||
Poly(poly),
|
||||
kind
|
||||
))
|
||||
}
|
||||
|
||||
fun getBlockPlatforms(x: Int, y: Int, getter: CollisionTypeGetter, kind: CollisionType, target: MutableList<CollisionPoly> = arrayListOf()): MutableList<CollisionPoly> {
|
||||
// This was once simple and elegant and made sense but then I made it
|
||||
// match the actual platform rendering more closely and now it's a big
|
||||
// shitty pile of special cases again. RIP.
|
||||
|
||||
if (getter.collisionType(x, y) != kind) return target
|
||||
|
||||
val right = getter.collisionType(x + 1, y) == kind
|
||||
val left = getter.collisionType(x - 1, y) == kind
|
||||
|
||||
val downRight = getter.collisionType(x + 1, y - 1) == kind && getter.collisionType(x + 1, y) != kind
|
||||
val downLeft = getter.collisionType(x - 1, y - 1) == kind && getter.collisionType(x - 1, y) != kind
|
||||
|
||||
val upRight = getter.collisionType(x + 1, y + 1) == kind && !left && !right
|
||||
val upLeft = getter.collisionType(x - 1, y + 1) == kind && !left && !right
|
||||
|
||||
val above = getter.collisionType(x, y + 1) == kind
|
||||
val below = getter.collisionType(x, y - 1) == kind
|
||||
|
||||
val dx = x.toDouble()
|
||||
val dy = y.toDouble()
|
||||
|
||||
if (downRight && downLeft && upRight && upLeft) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind)
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind)
|
||||
} else if (above && below) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind)
|
||||
} else if (upLeft && downLeft && !upRight && !downRight) {
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind)
|
||||
} else if (upRight && downRight && !upLeft && !upRight) { // TODO: figure out what original starbound devs meant by this condition
|
||||
target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind)
|
||||
} else if (upRight && downLeft) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind)
|
||||
|
||||
// special case block for connecting flat platform above
|
||||
if (above && getter.collisionType(x + 1, y + 1) == kind)
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy + 1), Vector2d(dx + 2, dy + 2)), kind)
|
||||
} else if (upLeft && downRight) {
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind)
|
||||
|
||||
// special case block for connecting flat platform above
|
||||
if (above && getter.collisionType(x - 1, y + 1) == kind)
|
||||
target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx - 1, dy + 2)), kind)
|
||||
} else if (above && !downRight && !downLeft) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind)
|
||||
} else if (upLeft && !upRight) {
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind)
|
||||
} else if (upRight && !upLeft) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind)
|
||||
} else if (downRight && (left || !below)) {
|
||||
target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind)
|
||||
} else if (downLeft && (right || !below)) {
|
||||
target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind)
|
||||
} else {
|
||||
target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind)
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
fun getBlocksMarchingSquares(x: Int, y: Int, getter: CollisionTypeGetter, kind: CollisionType, target: MutableList<CollisionPoly> = arrayListOf()): MutableList<CollisionPoly> {
|
||||
var neighborMask = 0
|
||||
|
||||
if (getter.collisionType(x, y + 1) >= kind) neighborMask = neighborMask or 1
|
||||
if (getter.collisionType(x + 1, y + 1) >= kind) neighborMask = neighborMask or 2
|
||||
if (getter.collisionType(x + 1, y) >= kind) neighborMask = neighborMask or 4
|
||||
if (getter.collisionType(x, y) >= kind) neighborMask = neighborMask or 8
|
||||
|
||||
when (neighborMask) {
|
||||
4 -> {
|
||||
if (getter.collisionType(x + 2, y) >= kind &&
|
||||
getter.collisionType(x + 2, y + 1) < kind &&
|
||||
getter.collisionType(x, y - 1) < kind) {
|
||||
|
||||
target.addBlock(x, y, getter, 16)
|
||||
return target
|
||||
}
|
||||
}
|
||||
|
||||
8 -> {
|
||||
if (getter.collisionType(x - 1, y) >= kind &&
|
||||
getter.collisionType(x - 1, y + 1) < kind &&
|
||||
getter.collisionType(x + 1, y - 1) < kind) {
|
||||
|
||||
target.addBlock(x, y, getter, 17)
|
||||
return target
|
||||
}
|
||||
}
|
||||
|
||||
13 -> {
|
||||
if (getter.collisionType(x, y + 2) >= kind &&
|
||||
getter.collisionType(x + 1, y + 2) < kind &&
|
||||
getter.collisionType(x + 2, y) >= kind) {
|
||||
|
||||
target.addBlock(x, y, getter, 18)
|
||||
target.addBlock(x, y, getter, 19)
|
||||
return target
|
||||
}
|
||||
}
|
||||
|
||||
14 -> {
|
||||
if (getter.collisionType(x, y + 2) < kind &&
|
||||
getter.collisionType(x + 1, y + 2) >= kind &&
|
||||
getter.collisionType(x - 1, y) >= kind) {
|
||||
|
||||
target.addBlock(x, y, getter, 20)
|
||||
target.addBlock(x, y, getter, 21)
|
||||
return target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (neighborMask != 0) {
|
||||
target.addBlock(x, y, getter, neighborMask)
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
@ -22,7 +22,7 @@ import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
private fun calculateEdges(points: List<Vector2d>): ImmutableList<Poly.Edge> {
|
||||
require(points.size >= 3) { "Provided poly is invalid (only ${points.size} points are defined)" }
|
||||
require(points.size >= 2) { "Provided poly is invalid (only ${points.size} points are defined)" }
|
||||
|
||||
val edges = ImmutableList.Builder<Poly.Edge>()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user