mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 03:54:06 +09:00
implementing water sim but not actually working
This commit is contained in:
@@ -33,6 +33,7 @@ import java.io.File
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import net.torvald.getcpuname.GetCpuName
|
import net.torvald.getcpuname.GetCpuName
|
||||||
import net.torvald.terrarum.modulebasegame.Ingame
|
import net.torvald.terrarum.modulebasegame.Ingame
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -908,7 +909,7 @@ inline fun Double.abs() = Math.abs(this)
|
|||||||
inline fun Double.sqr() = this * this
|
inline fun Double.sqr() = this * this
|
||||||
inline fun Double.sqrt() = Math.sqrt(this)
|
inline fun Double.sqrt() = Math.sqrt(this)
|
||||||
inline fun Float.sqrt() = FastMath.sqrt(this)
|
inline fun Float.sqrt() = FastMath.sqrt(this)
|
||||||
inline fun Int.abs() = if (this < 0) -this else this
|
inline fun Int.abs() = this.absoluteValue
|
||||||
fun Double.bipolarClamp(limit: Double) =
|
fun Double.bipolarClamp(limit: Double) =
|
||||||
this.coerceIn(-limit, limit)
|
this.coerceIn(-limit, limit)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package net.torvald.terrarum.blockproperties
|
package net.torvald.terrarum.blockproperties
|
||||||
|
|
||||||
|
import net.torvald.terrarum.gameworld.FluidType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2016-08-06.
|
* Created by minjaesong on 2016-08-06.
|
||||||
*/
|
*/
|
||||||
object Fluid {
|
object Fluid {
|
||||||
|
|
||||||
val NULL = 0
|
val NULL = FluidType(0)
|
||||||
|
|
||||||
val WATER = 1
|
val WATER = FluidType(1)
|
||||||
val STATIC_WATER = -1
|
val STATIC_WATER = FluidType(-1)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,11 @@ import com.badlogic.gdx.graphics.Color
|
|||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||||
|
import net.torvald.terrarum.blockproperties.Fluid
|
||||||
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
|
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
|
||||||
import net.torvald.terrarum.serialise.ReadLayerDataLzma
|
import net.torvald.terrarum.serialise.ReadLayerDataLzma
|
||||||
import org.dyn4j.geometry.Vector2
|
import org.dyn4j.geometry.Vector2
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
typealias BlockAddress = Long
|
typealias BlockAddress = Long
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ open class GameWorld {
|
|||||||
|
|
||||||
val wallDamages: HashMap<BlockAddress, Float>
|
val wallDamages: HashMap<BlockAddress, Float>
|
||||||
val terrainDamages: HashMap<BlockAddress, Float>
|
val terrainDamages: HashMap<BlockAddress, Float>
|
||||||
val fluidTypes: HashMap<BlockAddress, Int>
|
val fluidTypes: HashMap<BlockAddress, FluidType>
|
||||||
val fluidFills: HashMap<BlockAddress, Float>
|
val fluidFills: HashMap<BlockAddress, Float>
|
||||||
|
|
||||||
//public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) );
|
//public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) );
|
||||||
@@ -79,7 +81,7 @@ open class GameWorld {
|
|||||||
|
|
||||||
wallDamages = HashMap<BlockAddress, Float>()
|
wallDamages = HashMap<BlockAddress, Float>()
|
||||||
terrainDamages = HashMap<BlockAddress, Float>()
|
terrainDamages = HashMap<BlockAddress, Float>()
|
||||||
fluidTypes = HashMap<BlockAddress, Int>()
|
fluidTypes = HashMap<BlockAddress, FluidType>()
|
||||||
fluidFills = HashMap<BlockAddress, Float>()
|
fluidFills = HashMap<BlockAddress, Float>()
|
||||||
|
|
||||||
// temperature layer: 2x2 is one cell
|
// temperature layer: 2x2 is one cell
|
||||||
@@ -154,8 +156,8 @@ open class GameWorld {
|
|||||||
get() = layerTerrainLowBits.data
|
get() = layerTerrainLowBits.data
|
||||||
|
|
||||||
fun getTileFromWall(x: Int, y: Int): Int? {
|
fun getTileFromWall(x: Int, y: Int): Int? {
|
||||||
val wall: Int? = layerWall.getTile(x fmod width, y)
|
val wall: Int? = layerWall.getTile(x fmod width, y.coerceWorld().coerceWorld())
|
||||||
val wallDamage: Int? = getWallLowBits(x fmod width, y)
|
val wallDamage: Int? = getWallLowBits(x fmod width, y.coerceWorld())
|
||||||
return if (wall == null || wallDamage == null)
|
return if (wall == null || wallDamage == null)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
@@ -163,8 +165,8 @@ open class GameWorld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getTileFromTerrain(x: Int, y: Int): Int? {
|
fun getTileFromTerrain(x: Int, y: Int): Int? {
|
||||||
val terrain: Int? = layerTerrain.getTile(x fmod width, y)
|
val terrain: Int? = layerTerrain.getTile(x fmod width, y.coerceWorld())
|
||||||
val terrainDamage: Int? = getTerrainLowBits(x fmod width, y)
|
val terrainDamage: Int? = getTerrainLowBits(x fmod width, y.coerceWorld())
|
||||||
return if (terrain == null || terrainDamage == null)
|
return if (terrain == null || terrainDamage == null)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
@@ -172,15 +174,15 @@ open class GameWorld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getTileFromWire(x: Int, y: Int): Int? {
|
fun getTileFromWire(x: Int, y: Int): Int? {
|
||||||
return layerWire.getTile(x fmod width, y)
|
return layerWire.getTile(x fmod width, y.coerceWorld())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWallLowBits(x: Int, y: Int): Int? {
|
fun getWallLowBits(x: Int, y: Int): Int? {
|
||||||
return layerWallLowBits.getData(x fmod width, y)
|
return layerWallLowBits.getData(x fmod width, y.coerceWorld())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTerrainLowBits(x: Int, y: Int): Int? {
|
fun getTerrainLowBits(x: Int, y: Int): Int? {
|
||||||
return layerTerrainLowBits.getData(x fmod width, y)
|
return layerTerrainLowBits.getData(x fmod width, y.coerceWorld())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,7 +194,7 @@ open class GameWorld {
|
|||||||
* @param combinedTilenum (tilenum * 16) + damage
|
* @param combinedTilenum (tilenum * 16) + damage
|
||||||
*/
|
*/
|
||||||
fun setTileWall(x: Int, y: Int, combinedTilenum: Int) {
|
fun setTileWall(x: Int, y: Int, combinedTilenum: Int) {
|
||||||
setTileWall(x fmod width, y, (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
setTileWall(x fmod width, y.coerceWorld(), (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -204,23 +206,23 @@ open class GameWorld {
|
|||||||
* @param combinedTilenum (tilenum * 16) + damage
|
* @param combinedTilenum (tilenum * 16) + damage
|
||||||
*/
|
*/
|
||||||
fun setTileTerrain(x: Int, y: Int, combinedTilenum: Int) {
|
fun setTileTerrain(x: Int, y: Int, combinedTilenum: Int) {
|
||||||
setTileTerrain(x fmod width, y, (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
setTileTerrain(x fmod width, y.coerceWorld(), (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) {
|
fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) {
|
||||||
layerWall.setTile(x fmod width, y, tile)
|
layerWall.setTile(x fmod width, y.coerceWorld(), tile)
|
||||||
layerWallLowBits.setData(x fmod width, y, damage)
|
layerWallLowBits.setData(x fmod width, y.coerceWorld(), damage)
|
||||||
wallDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
wallDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
|
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
|
||||||
layerTerrain.setTile(x fmod width, y, tile)
|
layerTerrain.setTile(x fmod width, y.coerceWorld(), tile)
|
||||||
layerTerrainLowBits.setData(x fmod width, y, damage)
|
layerTerrainLowBits.setData(x fmod width, y.coerceWorld(), damage)
|
||||||
terrainDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
terrainDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTileWire(x: Int, y: Int, tile: Byte) {
|
fun setTileWire(x: Int, y: Int, tile: Byte) {
|
||||||
layerWire.setTile(x fmod width, y, tile)
|
layerWire.setTile(x fmod width, y.coerceWorld(), tile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTileFrom(mode: Int, x: Int, y: Int): Int? {
|
fun getTileFrom(mode: Int, x: Int, y: Int): Int? {
|
||||||
@@ -340,13 +342,20 @@ open class GameWorld {
|
|||||||
fun getWallDamage(x: Int, y: Int): Float =
|
fun getWallDamage(x: Int, y: Int): Float =
|
||||||
wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f
|
wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f
|
||||||
|
|
||||||
fun setFluid(x: Int, y: Int, fluidType: Int, fill: Float) {
|
fun setFluid(x: Int, y: Int, fluidType: FluidType, fill: Float) {
|
||||||
val addr = LandUtil.getBlockAddr(this, x, y)
|
val addr = LandUtil.getBlockAddr(this, x, y)
|
||||||
|
// fluid completely drained
|
||||||
if (fill <= WorldSimulator.FLUID_MIN_MASS) {
|
if (fill <= WorldSimulator.FLUID_MIN_MASS) {
|
||||||
fluidTypes.remove(addr)
|
/**********/ fluidTypes.remove(addr)
|
||||||
fluidFills.remove(addr)
|
val oldMap = fluidFills.remove(addr)
|
||||||
setTileTerrain(x, y, 0)
|
|
||||||
|
// oldMap not being null means there actually was a fluid there, so we can put AIR onto it
|
||||||
|
// otherwise, it means it was some solid and therefore we DON'T want to put AIR onto it
|
||||||
|
if (oldMap != null) {
|
||||||
|
setTileTerrain(x, y, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// update the fluid amount
|
||||||
else {
|
else {
|
||||||
fluidTypes[addr] = fluidType
|
fluidTypes[addr] = fluidType
|
||||||
fluidFills[addr] = fill
|
fluidFills[addr] = fill
|
||||||
@@ -354,27 +363,28 @@ open class GameWorld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFluid(x: Int, y: Int): Pair<Int, Float>? {
|
fun getFluid(x: Int, y: Int): FluidInfo {
|
||||||
val addr = LandUtil.getBlockAddr(this, x, y)
|
val addr = LandUtil.getBlockAddr(this, x, y)
|
||||||
val fill = fluidFills[addr]
|
val fill = fluidFills[addr]
|
||||||
val type = fluidTypes[addr]
|
val type = fluidTypes[addr]
|
||||||
|
|
||||||
return if (type == null) null else Pair(type!!, fill!!)
|
return if (type == null) FluidInfo(Fluid.NULL, 0f) else FluidInfo(type, fill!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FluidInfo(val type: FluidType, val amount: Float)
|
||||||
|
|
||||||
|
|
||||||
fun getTemperature(worldTileX: Int, worldTileY: Int): Float? {
|
fun getTemperature(worldTileX: Int, worldTileY: Int): Float? {
|
||||||
return null
|
return null
|
||||||
//return layerThermal.getValue((worldTileX fmod width) / 2, worldTileY / 2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAirPressure(worldTileX: Int, worldTileY: Int): Float? {
|
fun getAirPressure(worldTileX: Int, worldTileY: Int): Float? {
|
||||||
return null
|
return null
|
||||||
//return layerFluidPressure.getValue((worldTileX fmod width) / 4, worldTileY / 8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun Int.coerceWorld() = this.coerceIn(0, height - 1)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Transient val WALL = 0
|
@Transient val WALL = 0
|
||||||
@Transient val TERRAIN = 1
|
@Transient val TERRAIN = 1
|
||||||
@@ -390,3 +400,7 @@ open class GameWorld {
|
|||||||
|
|
||||||
infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
|
infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
|
||||||
infix fun Float.fmod(other: Float) = if (this >= 0f) this % other else (this % other) + other
|
infix fun Float.fmod(other: Float) = if (this >= 0f) this % other else (this % other) + other
|
||||||
|
|
||||||
|
inline class FluidType(val value: Int) {
|
||||||
|
infix fun sameAs(other: FluidType) = this.value.absoluteValue == other.value.absoluteValue
|
||||||
|
}
|
||||||
@@ -598,7 +598,7 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actorNowPlaying = newActor
|
actorNowPlaying = newActor
|
||||||
WorldSimulator(actorNowPlaying, Terrarum.deltaTime)
|
//WorldSimulator(actorNowPlaying, Terrarum.deltaTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changePossession(refid: Int) {
|
private fun changePossession(refid: Int) {
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
import net.torvald.terrarum.roundInt
|
|
||||||
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
|
||||||
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
|
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
|
import net.torvald.terrarum.roundInt
|
||||||
|
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
|
||||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||||
import net.torvald.terrarum.blockproperties.Fluid
|
import net.torvald.terrarum.blockproperties.Fluid
|
||||||
import net.torvald.terrarum.gameworld.FluidCodex
|
import net.torvald.terrarum.gameworld.FluidType
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
|
||||||
import net.torvald.terrarum.modulebasegame.Ingame
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,15 +21,20 @@ object WorldSimulator {
|
|||||||
* In tiles;
|
* In tiles;
|
||||||
* square width/height = field * 2
|
* square width/height = field * 2
|
||||||
*/
|
*/
|
||||||
const val FLUID_UPDATING_SQUARE_RADIUS = 64 // larger value will have dramatic impact on performance
|
const val FLUID_UPDATING_SQUARE_RADIUS = 80 // larger value will have dramatic impact on performance
|
||||||
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
|
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
|
||||||
|
|
||||||
|
// maps are separated as old-new for obvious reason, also it'll allow concurrent modification
|
||||||
private val fluidMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
|
private val fluidMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
|
||||||
private val fluidTypeMap = Array(DOUBLE_RADIUS, { IntArray(DOUBLE_RADIUS) })
|
private val fluidTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
|
||||||
|
private val fluidNewMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
|
||||||
|
private val fluidNewTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
|
||||||
|
|
||||||
const val FLUID_MAX_MASS = 1f // The normal, un-pressurized mass of a full water cell
|
const val FLUID_MAX_MASS = 1f // The normal, un-pressurized mass of a full water cell
|
||||||
const val FLUID_MAX_COMP = 0.02f // How much excess water a cell can store, compared to the cell above it
|
const val FLUID_MAX_COMP = 0.02f // How much excess water a cell can store, compared to the cell above it. A tile of fluid can contain more than MaxMass water.
|
||||||
const val FLUID_MIN_MASS = 0.0001f //Ignore cells that are almost dry
|
const val FLUID_MIN_MASS = 0.0001f //Ignore cells that are almost dry
|
||||||
|
const val minFlow = 0.01f
|
||||||
|
const val maxSpeed = 1f // max units of water moved out of one block to another, per timestamp
|
||||||
|
|
||||||
// END OF FLUID-RELATED STUFFS
|
// END OF FLUID-RELATED STUFFS
|
||||||
|
|
||||||
@@ -68,11 +69,160 @@ object WorldSimulator {
|
|||||||
* TODO multithread
|
* TODO multithread
|
||||||
*/
|
*/
|
||||||
fun moveFluids(delta: Float) {
|
fun moveFluids(delta: Float) {
|
||||||
////////////////////
|
makeFluidMapFromWorld()
|
||||||
// build fluidmap //
|
|
||||||
////////////////////
|
|
||||||
purgeFluidMap()
|
|
||||||
|
|
||||||
|
|
||||||
|
// before data: fluidMap/fluidTypeMap
|
||||||
|
// after data: fluidNewMap/fluidNewTypeMap
|
||||||
|
var flow = 0f
|
||||||
|
var remainingMass = 0f
|
||||||
|
|
||||||
|
for (y in 1 until fluidMap.size - 1) {
|
||||||
|
for (x in 1 until fluidMap[0].size - 1) {
|
||||||
|
val worldX = x + updateXFrom
|
||||||
|
val worldY = y + updateYFrom
|
||||||
|
|
||||||
|
// check solidity
|
||||||
|
if (isSolid(worldX, worldY)) continue
|
||||||
|
// check if the fluid is a same kind
|
||||||
|
//if (!isFlowable(type, worldX, worldY))) continue
|
||||||
|
|
||||||
|
|
||||||
|
// Custom push-only flow
|
||||||
|
flow = 0f
|
||||||
|
remainingMass = fluidMap[y][x]
|
||||||
|
if (remainingMass <= 0) continue
|
||||||
|
|
||||||
|
// The block below this one
|
||||||
|
if (!isSolid(worldX, worldY + 1)) { // TODO use isFlowable
|
||||||
|
flow = getStableStateB(remainingMass + fluidMap[y + 1][x]) - fluidMap[y + 1][x]
|
||||||
|
if (flow > minFlow) {
|
||||||
|
flow *= 0.5f // leads to smoother flow
|
||||||
|
}
|
||||||
|
flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
|
||||||
|
|
||||||
|
fluidNewMap[y][x] -= flow
|
||||||
|
fluidNewMap[y + 1][x] += flow
|
||||||
|
remainingMass -= flow
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMass <= 0) continue
|
||||||
|
|
||||||
|
// Left
|
||||||
|
if (!isSolid(worldX - 1, worldY)) { // TODO use isFlowable
|
||||||
|
// Equalise the amount fo water in this block and its neighbour
|
||||||
|
flow = (fluidMap[y][x] - fluidMap[y][x - 1]) / 4f
|
||||||
|
if (flow > minFlow) {
|
||||||
|
flow *= 0.5f
|
||||||
|
}
|
||||||
|
flow.coerceIn(0f, remainingMass)
|
||||||
|
|
||||||
|
fluidNewMap[y][x] -= flow
|
||||||
|
fluidNewMap[y][x - 1] += flow
|
||||||
|
remainingMass -= flow
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMass <= 0) continue
|
||||||
|
|
||||||
|
// Right
|
||||||
|
if (!isSolid(worldX + 1, worldY)) { // TODO use isFlowable
|
||||||
|
// Equalise the amount fo water in this block and its neighbour
|
||||||
|
flow = (fluidMap[y][x] - fluidMap[y][x + 1]) / 4f
|
||||||
|
if (flow > minFlow) {
|
||||||
|
flow *= 0.5f
|
||||||
|
}
|
||||||
|
flow.coerceIn(0f, remainingMass)
|
||||||
|
|
||||||
|
fluidNewMap[y][x] -= flow
|
||||||
|
fluidNewMap[y][x + 1] += flow
|
||||||
|
remainingMass -= flow
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMass <= 0) continue
|
||||||
|
|
||||||
|
// Up; only compressed water flows upwards
|
||||||
|
if (!isSolid(worldX, worldY - 1)) { // TODO use isFlowable
|
||||||
|
flow = remainingMass - getStableStateB(remainingMass + fluidMap[y - 1][x])
|
||||||
|
if (flow > minFlow) {
|
||||||
|
flow *= 0.5f
|
||||||
|
}
|
||||||
|
flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
|
||||||
|
|
||||||
|
fluidNewMap[y][x] -= flow
|
||||||
|
fluidNewMap[y - 1][x] += flow
|
||||||
|
remainingMass -= flow
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fluidmapToWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isFlowable(type: FluidType, worldX: Int, worldY: Int): Boolean {
|
||||||
|
val targetFluid = world.getFluid(worldX, worldY)
|
||||||
|
|
||||||
|
// true if target's type is the same as mine, or it's NULL (air)
|
||||||
|
return (targetFluid.type sameAs type || targetFluid.type sameAs Fluid.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSolid(worldX: Int, worldY: Int): Boolean {
|
||||||
|
val tile = world.getTileFromTerrain(worldX, worldY)
|
||||||
|
if (tile != Block.FLUID_MARKER) {
|
||||||
|
// check for block properties isSolid
|
||||||
|
return BlockCodex[tile].isSolid
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// check for fluid
|
||||||
|
|
||||||
|
// no STATIC is implement yet, just return true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Explanation of get_stable_state_b (well, kind-of) :
|
||||||
|
|
||||||
|
if x <= 1, all water goes to the lower cell
|
||||||
|
* a = 0
|
||||||
|
* b = 1
|
||||||
|
|
||||||
|
if x > 1 & x < 2*MaxMass + MaxCompress, the lower cell should have MaxMass + (upper_cell/MaxMass) * MaxCompress
|
||||||
|
b = MaxMass + (a/MaxMass)*MaxCompress
|
||||||
|
a = x - b
|
||||||
|
|
||||||
|
->
|
||||||
|
|
||||||
|
b = MaxMass + ((x - b)/MaxMass)*MaxCompress ->
|
||||||
|
b = MaxMass + (x*MaxCompress - b*MaxCompress)/MaxMass
|
||||||
|
b*MaxMass = MaxMass^2 + (x*MaxCompress - b*MaxCompress)
|
||||||
|
b*(MaxMass + MaxCompress) = MaxMass*MaxMass + x*MaxCompress
|
||||||
|
|
||||||
|
* b = (MaxMass*MaxMass + x*MaxCompress)/(MaxMass + MaxCompress)
|
||||||
|
* a = x - b;
|
||||||
|
|
||||||
|
if x >= 2 * MaxMass + MaxCompress, the lower cell should have upper+MaxCompress
|
||||||
|
|
||||||
|
b = a + MaxCompress
|
||||||
|
a = x - b
|
||||||
|
|
||||||
|
->
|
||||||
|
|
||||||
|
b = x - b + MaxCompress ->
|
||||||
|
2b = x + MaxCompress ->
|
||||||
|
|
||||||
|
* b = (x + MaxCompress)/2
|
||||||
|
* a = x - b
|
||||||
|
*/
|
||||||
|
private fun getStableStateB(totalMass: Float): Float {
|
||||||
|
if (totalMass <= 1)
|
||||||
|
return 1f
|
||||||
|
else if (totalMass < 2f * FLUID_MAX_MASS + FLUID_MAX_COMP)
|
||||||
|
return (FLUID_MAX_MASS * FLUID_MAX_MASS + totalMass * FLUID_MAX_COMP) / (FLUID_MAX_MASS + FLUID_MAX_COMP)
|
||||||
|
else
|
||||||
|
return (totalMass + FLUID_MAX_COMP) / 2f
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,11 +259,22 @@ object WorldSimulator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun purgeFluidMap() {
|
private fun makeFluidMapFromWorld() {
|
||||||
for (y in 1..DOUBLE_RADIUS) {
|
for (y in 0 until fluidMap.size) {
|
||||||
for (x in 1..DOUBLE_RADIUS) {
|
for (x in 0 until fluidMap[0].size) {
|
||||||
fluidMap[y - 1][x - 1] = 0f
|
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
|
||||||
fluidTypeMap[y - 1][x - 1] = Fluid.NULL
|
fluidMap[y][x] = fluidData.amount
|
||||||
|
fluidTypeMap[y][x] = fluidData.type
|
||||||
|
fluidNewMap[y][x] = fluidData.amount
|
||||||
|
fluidNewTypeMap[y][x] = fluidData.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fluidmapToWorld() {
|
||||||
|
for (y in 0 until fluidMap.size) {
|
||||||
|
for (x in 0 until fluidMap[0].size) {
|
||||||
|
world.setFluid(x + updateXFrom, y + updateYFrom, fluidNewTypeMap[y][x], fluidNewMap[y][x])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.torvald.terrarum.serialise
|
|||||||
import com.badlogic.gdx.utils.compression.Lzma
|
import com.badlogic.gdx.utils.compression.Lzma
|
||||||
import net.torvald.terrarum.AppLoader.printdbg
|
import net.torvald.terrarum.AppLoader.printdbg
|
||||||
import net.torvald.terrarum.gameworld.BlockAddress
|
import net.torvald.terrarum.gameworld.BlockAddress
|
||||||
|
import net.torvald.terrarum.gameworld.FluidType
|
||||||
import net.torvald.terrarum.gameworld.MapLayer
|
import net.torvald.terrarum.gameworld.MapLayer
|
||||||
import net.torvald.terrarum.gameworld.PairedMapLayer
|
import net.torvald.terrarum.gameworld.PairedMapLayer
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
@@ -156,7 +157,7 @@ internal object ReadLayerDataLzma {
|
|||||||
|
|
||||||
val terrainDamages = HashMap<BlockAddress, Float>()
|
val terrainDamages = HashMap<BlockAddress, Float>()
|
||||||
val wallDamages = HashMap<BlockAddress, Float>()
|
val wallDamages = HashMap<BlockAddress, Float>()
|
||||||
val fluidTypes = HashMap<BlockAddress, Int>()
|
val fluidTypes = HashMap<BlockAddress, FluidType>()
|
||||||
val fluidFills = HashMap<BlockAddress, Float>()
|
val fluidFills = HashMap<BlockAddress, Float>()
|
||||||
|
|
||||||
// parse terrain damages
|
// parse terrain damages
|
||||||
@@ -215,7 +216,7 @@ internal object ReadLayerDataLzma {
|
|||||||
val spawnY: Int,
|
val spawnY: Int,
|
||||||
val wallDamages: HashMap<BlockAddress, Float>,
|
val wallDamages: HashMap<BlockAddress, Float>,
|
||||||
val terrainDamages: HashMap<BlockAddress, Float>,
|
val terrainDamages: HashMap<BlockAddress, Float>,
|
||||||
val fluidTypes: HashMap<BlockAddress, Int>,
|
val fluidTypes: HashMap<BlockAddress, FluidType>,
|
||||||
val fluidFills: HashMap<BlockAddress, Float>
|
val fluidFills: HashMap<BlockAddress, Float>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user