genver for World now stores the version the world has generated

This commit is contained in:
minjaesong
2024-08-12 00:19:59 +09:00
parent ff4bf42922
commit 9c9806f622
7 changed files with 127 additions and 12 deletions

View File

@@ -135,6 +135,9 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
open var gameFullyLoaded = false
internal set
var worldGenVer: Long? = null
protected set
val ACTORCONTAINER_INITIAL_SIZE = 64
val actorContainerActive = SortedArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)
val actorContainerInactive = SortedArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)

View File

@@ -20,7 +20,9 @@ import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.FluidCodex
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.ActorWithBody.Companion.METER
import net.torvald.terrarum.gameactors.ActorWithBody.Companion.PHYS_EPSILON_DIST
import net.torvald.terrarum.gameactors.ActorWithBody.Companion.SI_TO_GAME_ACC
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.mouseInInteractableRange
@@ -38,6 +40,7 @@ import net.torvald.terrarum.worlddrawer.LightmapRenderer
import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.util.CircularArray
import org.dyn4j.geometry.Vector2
import kotlin.math.min
import kotlin.system.exitProcess
@@ -786,9 +789,80 @@ object IngameRenderer : Disposable {
}
}
private val cubeSize = 7.0
private val hcubeSize = cubeSize / 2
private val externalV = Vector2()
private val maxStep = 56
private val trajectoryFlow = 30
private fun getSubmergedHeight(gravitation: Vector2, hitbox: Hitbox): Double {
val straightGravity = (gravitation.y > 0)
// TODO reverse gravity
if (!straightGravity) TODO()
val itsY = (hitbox.startY / TILE_SIZED).toInt()
val iteY = (hitbox.endY / TILE_SIZED).toInt()
val txL = (hitbox.startX / TILE_SIZED).floorToInt()
val txR = (hitbox.endX / TILE_SIZED).floorToInt()
var hL = 0.0
var hR = 0.0
val rec = java.util.ArrayList<Double>()
for (ty in itsY..iteY) {
val fL = world.getFluid(txL, ty).amount.coerceAtMost(1f) * TILE_SIZED // 0-16
val fR = world.getFluid(txR, ty).amount.coerceAtMost(1f) * TILE_SIZED // 0-16
// if head
if (ty == itsY) {
val actorHs = hitbox.startY % TILE_SIZED // 0-16
val yp = TILE_SIZED - actorHs // 0-16
hL += min(yp, fL)
hR += min(yp, fR)
rec.add(min(yp, fL))
}
// if tail
else if (ty == iteY) {
val actorHe = hitbox.endY % TILE_SIZED // 0-16
hL += (actorHe - TILE_SIZED + fL).coerceAtLeast(0.0)
hR += (actorHe - TILE_SIZED + fR).coerceAtLeast(0.0)
rec.add((actorHe - TILE_SIZED + fL).coerceAtLeast(0.0))
}
else {
hL += fL
hR += fR
rec.add(fL)
}
}
// returns average of two sides
return (hL + hR) / 2.0
}
private fun forEachOccupyingFluid(hitbox: Hitbox, consumer: (GameWorld.FluidInfo?) -> Unit) {
val hIntTilewiseHitbox = Hitbox(0.0, 0.0, 1.0, 1.0).setFromTwoPoints(
hitbox.startX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble() + 0.5,
hitbox.startY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble() + 0.5,
hitbox.endX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble() + 0.5,
hitbox.endY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble() + 0.5
)
val tileProps = java.util.ArrayList<GameWorld.FluidInfo?>()
for (y in hIntTilewiseHitbox.startY.toInt()..hIntTilewiseHitbox.endY.toInt()) {
for (x in hIntTilewiseHitbox.startX.toInt()..hIntTilewiseHitbox.endX.toInt()) {
tileProps.add(world.getFluid(x, y))
}
}
return tileProps.forEach(consumer)
}
private fun drawTrajectoryForThrowable(frameBuffer: FrameBuffer, batch: SpriteBatch, frameDelta: Float, player: ActorWithBody, world: GameWorld, item: ItemThrowable) {
val ww = world.width * TILE_SIZEF
@@ -809,6 +883,17 @@ object IngameRenderer : Disposable {
// simulate physics
applyGravitation(grav, cubeSize) // TODO use actual value instead of `cubeSize`
val hb = Hitbox(throwPos.x - hcubeSize, throwPos.y - hcubeSize, cubeSize, cubeSize)
var tileDensityFluid = 0
forEachOccupyingFluid(hb) {
// get max density for each tile
if (it?.isFluid() == true && it.getProp().density > tileDensityFluid) {
tileDensityFluid = it.getProp().density
}
}
val submergedHeight = getSubmergedHeight(grav, hb)
val submergedRatio = submergedHeight / cubeSize
applyBuoyancy(grav, item.mass, item.material.density.toDouble(), tileDensityFluid, submergedRatio)
// move the point
throwPos += externalV
// more physics
@@ -818,10 +903,10 @@ object IngameRenderer : Disposable {
// break if colliding with a tile
val hitSolid = listOf(
throwPos + Vector2(-cubeSize/2, -cubeSize/2),
throwPos + Vector2(-cubeSize/2, +cubeSize/2),
throwPos + Vector2(+cubeSize/2, +cubeSize/2),
throwPos + Vector2(+cubeSize/2, -cubeSize/2),
throwPos + Vector2(-hcubeSize, -hcubeSize),
throwPos + Vector2(-hcubeSize, +hcubeSize),
throwPos + Vector2(+hcubeSize, +hcubeSize),
throwPos + Vector2(+hcubeSize, -hcubeSize),
).any {
val wx = (it.x / TILE_SIZED).toInt()
val wy = (it.y / TILE_SIZED).toInt()
@@ -872,6 +957,29 @@ object IngameRenderer : Disposable {
applyForce(getDrag(externalV, gravitation, hitboxWidth))
}
private fun applyBuoyancy(grav: Vector2, mass: Double, density: Double, tileDensityFluid: Int, submergedRatio: Double) {
val rho = tileDensityFluid // kg / m^3
val V_full = mass / density * 2.0 // density = mass / volume, simply rearrange this. Multiplier of 2.0 is a hack!
val V = V_full * submergedRatio
val F_k = grav * mass // F = ma where a is g; TODO add jump-accel into 'a' to allow better jumping under water
val F_bo = grav * (rho * V) // Newtons
// mh'' = mg - rho*gv
// h'' = (mg - rho*gv) / m
// if tileDensity = actorDensity, F_k = F_bo (this will be the case if there was no hack)
// printdbg(this, "F_k=$F_k [N] \t F_bo=${F_bo} [N] \t density=$density")
val F = F_k - F_bo
val acc = F / mass // (kg * m / s^2) / kg = m / s^2
val acc_game = acc.let { Vector2(it.x, it.y.coerceAtMost(0.0)) } * SI_TO_GAME_ACC
applyForce(acc_game)
}
private fun Int.frictionToMult(): Double = this / 16.0
private fun Int.viscosityToMult(): Double = 16.0 / (16.0 + this)

View File

@@ -387,6 +387,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
loadCallback = codices.callbackAfterLoad
worldGenVer = codices.worldGenver
}
/** Load rest of the game with GL context */
@@ -560,6 +561,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
KeyToggler.forceSet(Input.Keys.Q, false)
loadCallback = newGameParams.callbackAfterLoad
worldGenVer = null
}
val ingameController = IngameController(this)

View File

@@ -98,7 +98,7 @@ class QuickSingleplayerWorldSavingThread(
playersList.forEach {
ingame.world.playersLastStatus[it.uuid] = PlayerLastStatus(it, ingame.isMultiplayer)
}
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame.worldGenVer, ingame, time_t, actorsList, playersList))
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
addFile(disk, world); skimmer.appendEntry(world)

View File

@@ -121,7 +121,7 @@ class WorldSavingThread(
// Write World //
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame.worldGenVer, ingame, time_t, actorsList, playersList))
val world = DiskEntry(VDFileID.SAVEGAMEINFO, VDFileID.ROOT, creation_t, time_t, worldMeta)
addFile(disk, world)

View File

@@ -51,15 +51,15 @@ object WriteWorld {
}
// genver must be found on fixed location of the JSON string
operator fun invoke(ingame: TerrarumIngame, time_t: Long, actorsList: List<Actor>, playersList: List<IngamePlayer>): String {
operator fun invoke(oldGenVer: Long?, ingame: TerrarumIngame, time_t: Long, actorsList: List<Actor>, playersList: List<IngamePlayer>): String {
val s = Common.jsoner.toJson(preWrite(ingame, time_t, actorsList, playersList))
return """{"genver":${Common.GENVER},${s.substring(1)}"""
return """{"genver":${oldGenVer ?: Common.GENVER},${s.substring(1)}"""
}
fun encodeToByteArray64(ingame: TerrarumIngame, time_t: Long, actorsList: List<Actor>, playersList: List<IngamePlayer>): ByteArray64 {
fun encodeToByteArray64(oldGenVer: Long?, ingame: TerrarumIngame, time_t: Long, actorsList: List<Actor>, playersList: List<IngamePlayer>): ByteArray64 {
val baw = ByteArray64Writer(Common.CHARSET)
val header = """{"genver":${Common.GENVER}"""
val header = """{"genver":${oldGenVer ?: Common.GENVER}"""
baw.write(header)
Common.jsoner.toJson(preWrite(ingame, time_t, actorsList, playersList), baw)
baw.flush(); baw.close()