mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-14 04:24:05 +09:00
finally working buoyancy sim
This commit is contained in:
@@ -25,6 +25,7 @@ PrintWorld
|
|||||||
Seed
|
Seed
|
||||||
SetAV
|
SetAV
|
||||||
SetBulletin
|
SetBulletin
|
||||||
|
SetDensity
|
||||||
SetScale
|
SetScale
|
||||||
SetSol
|
SetSol
|
||||||
SetTurb
|
SetTurb
|
||||||
|
|||||||
|
@@ -173,8 +173,8 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
|
|||||||
(SAMPLING_RATED*0.5) / (24.0 * (distFromActor / distFalloff).sqr() + 1.0)
|
(SAMPLING_RATED*0.5) / (24.0 * (distFromActor / distFalloff).sqr() + 1.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
val sourceVec = (trackingTarget as ActorWithBody).let { it.externalV + (it.controllerV ?: Vector2()) }
|
val sourceVec = (trackingTarget as ActorWithBody).let { it.externalV + it.controllerV }
|
||||||
val listenerVec = App.audioMixer.actorNowPlaying!!.let { it.externalV + (it.controllerV ?: Vector2()) }
|
val listenerVec = App.audioMixer.actorNowPlaying!!.let { it.externalV + it.controllerV }
|
||||||
val distFromActorNext = distBetweenPoints(
|
val distFromActorNext = distBetweenPoints(
|
||||||
App.audioMixer.actorNowPlaying!!.centrePosVector + listenerVec,
|
App.audioMixer.actorNowPlaying!!.centrePosVector + listenerVec,
|
||||||
(trackingTarget as ActorWithBody).centrePosVector + sourceVec
|
(trackingTarget as ActorWithBody).centrePosVector + sourceVec
|
||||||
|
|||||||
@@ -535,9 +535,7 @@ open class ActorWithBody : Actor {
|
|||||||
|
|
||||||
val oldHitbox = hitbox.clone()
|
val oldHitbox = hitbox.clone()
|
||||||
|
|
||||||
if (this is IngamePlayer) {
|
if (density < 100.0) density = 100.0
|
||||||
density = 980.0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUpdate && !flagDespawn) {
|
if (isUpdate && !flagDespawn) {
|
||||||
if (!assertPrinted) assertInit()
|
if (!assertPrinted) assertInit()
|
||||||
@@ -568,6 +566,7 @@ open class ActorWithBody : Actor {
|
|||||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
||||||
if (!isNoSubjectToGrav) {
|
if (!isNoSubjectToGrav) {
|
||||||
applyGravitation()
|
applyGravitation()
|
||||||
|
applyBuoyancy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -580,17 +579,7 @@ open class ActorWithBody : Actor {
|
|||||||
// Codes that (SHOULD) displaces hitbox directly //
|
// Codes that (SHOULD) displaces hitbox directly //
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
// if (this is IngamePlayer) {
|
val vecSum = externalV + controllerV
|
||||||
// printdbg(this, "BodyViscosity=$bodyViscosity FeetViscosity=$feetViscosity BodyFriction=$bodyFriction FeetFriction=$feetFriction")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// externalV.applyViscoseDrag()
|
|
||||||
|
|
||||||
controllerV?.let {
|
|
||||||
it.applyViscoseDrag()
|
|
||||||
}
|
|
||||||
|
|
||||||
val vecSum = (externalV + (controllerV ?: Vector2(0.0, 0.0)))
|
|
||||||
/**
|
/**
|
||||||
* solveCollision()?
|
* solveCollision()?
|
||||||
* If and only if:
|
* If and only if:
|
||||||
@@ -651,15 +640,6 @@ open class ActorWithBody : Actor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --> Apply more forces <-- //
|
|
||||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
|
||||||
val buoyancy = applyBuoyancy()
|
|
||||||
if (!isNoSubjectToGrav) {
|
|
||||||
hitbox.translate(buoyancy)
|
|
||||||
clampHitbox()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// update all the other variables //
|
// update all the other variables //
|
||||||
|
|
||||||
@@ -1250,7 +1230,7 @@ open class ActorWithBody : Actor {
|
|||||||
IMPORTANT AF NOTE: things are ASYMMETRIC!
|
IMPORTANT AF NOTE: things are ASYMMETRIC!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val canUseStairs = option and COLLIDING_LR != 0 && (externalV + (controllerV ?: Vector2())).y.absoluteValue < 1.0
|
val canUseStairs = option and COLLIDING_LR != 0 && (externalV + controllerV).y.absoluteValue < 1.0
|
||||||
|
|
||||||
if (option.popcnt() == 1) {
|
if (option.popcnt() == 1) {
|
||||||
val (x1, x2, y1, y2) = hitbox.getWallDetection(option)
|
val (x1, x2, y1, y2) = hitbox.getWallDetection(option)
|
||||||
@@ -1518,22 +1498,25 @@ open class ActorWithBody : Actor {
|
|||||||
private fun getSubmergedHeight(): Double {
|
private fun getSubmergedHeight(): Double {
|
||||||
if (world == null) return 0.0
|
if (world == null) return 0.0
|
||||||
val straightGravity = (world!!.gravitation.y > 0)
|
val straightGravity = (world!!.gravitation.y > 0)
|
||||||
|
// TODO reverse gravity
|
||||||
if (!straightGravity) TODO()
|
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 txL = (hitbox.startX / TILE_SIZED).floorToInt()
|
||||||
val txR = (hitbox.endX / TILE_SIZED).floorToInt()
|
val txR = (hitbox.endX / TILE_SIZED).floorToInt()
|
||||||
|
|
||||||
var hL = 0.0
|
var hL = 0.0
|
||||||
var hR = 0.0
|
var hR = 0.0
|
||||||
|
|
||||||
val rec = ArrayList<Double>()
|
val rec = ArrayList<Double>()
|
||||||
|
|
||||||
for (ty in intTilewiseHitbox.startY.toInt()..intTilewiseHitbox.endY.toInt()) {
|
for (ty in itsY..iteY) {
|
||||||
val fL = world!!.getFluid(txL, ty).amount.coerceAtMost(1f) * TILE_SIZED // 0-16
|
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
|
val fR = world!!.getFluid(txR, ty).amount.coerceAtMost(1f) * TILE_SIZED // 0-16
|
||||||
|
|
||||||
// if head
|
// if head
|
||||||
if (ty == intTilewiseHitbox.startY.toInt()) {
|
if (ty == itsY) {
|
||||||
val actorHs = hitbox.startY % TILE_SIZED // 0-16
|
val actorHs = hitbox.startY % TILE_SIZED // 0-16
|
||||||
val yp = TILE_SIZED - actorHs // 0-16
|
val yp = TILE_SIZED - actorHs // 0-16
|
||||||
|
|
||||||
@@ -1543,7 +1526,7 @@ open class ActorWithBody : Actor {
|
|||||||
rec.add(min(yp, fL))
|
rec.add(min(yp, fL))
|
||||||
}
|
}
|
||||||
// if tail
|
// if tail
|
||||||
else if (ty == intTilewiseHitbox.endY.toInt()) {
|
else if (ty == iteY) {
|
||||||
val actorHe = hitbox.endY % TILE_SIZED // 0-16
|
val actorHe = hitbox.endY % TILE_SIZED // 0-16
|
||||||
|
|
||||||
hL += (actorHe - TILE_SIZED + fL).coerceAtLeast(0.0)
|
hL += (actorHe - TILE_SIZED + fL).coerceAtLeast(0.0)
|
||||||
@@ -1563,47 +1546,6 @@ open class ActorWithBody : Actor {
|
|||||||
return (hL + hR) / 2.0
|
return (hL + hR) / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun xxxxgetContactingAreaFluid(side: Int, translateX: Int = 0, translateY: Int = 0): Int {
|
|
||||||
if (world == null) return 0
|
|
||||||
|
|
||||||
var contactAreaCounter = 0
|
|
||||||
for (i in 0 until (if (side % 2 == 0) hitbox.width else hitbox.height).roundToInt()) {
|
|
||||||
// set tile positions
|
|
||||||
val tileX: Int
|
|
||||||
val tileY: Int
|
|
||||||
if (side == COLLIDING_LEFT) {
|
|
||||||
tileX = div16TruncateToMapWidth(hitbox.hitboxStart.x.roundToInt()
|
|
||||||
+ i + translateX)
|
|
||||||
tileY = div16TruncateToMapHeight(hitbox.hitboxEnd.y.roundToInt() + translateY)
|
|
||||||
}
|
|
||||||
else if (side == COLLIDING_TOP) {
|
|
||||||
tileX = div16TruncateToMapWidth(hitbox.hitboxStart.x.roundToInt()
|
|
||||||
+ i + translateX)
|
|
||||||
tileY = div16TruncateToMapHeight(hitbox.hitboxStart.y.roundToInt() + translateY)
|
|
||||||
}
|
|
||||||
else if (side == COLLIDING_RIGHT) {
|
|
||||||
tileX = div16TruncateToMapWidth(hitbox.hitboxEnd.x.roundToInt() + translateX)
|
|
||||||
tileY = div16TruncateToMapHeight(hitbox.hitboxStart.y.roundToInt()
|
|
||||||
+ i + translateY)
|
|
||||||
}
|
|
||||||
else if (side == COLLIDING_LEFT) {
|
|
||||||
tileX = div16TruncateToMapWidth(hitbox.hitboxStart.x.roundToInt() + translateX)
|
|
||||||
tileY = div16TruncateToMapHeight(hitbox.hitboxStart.y.roundToInt()
|
|
||||||
+ i + translateY)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw IllegalArgumentException(side.toString() + ": Wrong side input")
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate
|
|
||||||
if (world!!.getFluid(tileX, tileY).isFluid()) {
|
|
||||||
contactAreaCounter += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return contactAreaCounter
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getTileFriction(tile: ItemID) =
|
private fun getTileFriction(tile: ItemID) =
|
||||||
if (physProp.immobileBody && tile == Block.AIR)
|
if (physProp.immobileBody && tile == Block.AIR)
|
||||||
BlockCodex[Block.AIR].friction.frictionToMult().div(500)
|
BlockCodex[Block.AIR].friction.frictionToMult().div(500)
|
||||||
@@ -1677,11 +1619,11 @@ open class ActorWithBody : Actor {
|
|||||||
*
|
*
|
||||||
* @return the resultant buoyant force, F(k) + F(bo)
|
* @return the resultant buoyant force, F(k) + F(bo)
|
||||||
*/
|
*/
|
||||||
private fun applyBuoyancy(): Vector2 {
|
private fun applyBuoyancy() {
|
||||||
if (world == null) return Vector2()
|
if (world == null) return
|
||||||
|
|
||||||
val rho = tileDensityFluid // kg / m^3
|
val rho = tileDensityFluid // kg / m^3
|
||||||
val V_full = mass / density // density = mass / volume, simply rearrange this
|
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 V = V_full * submergedRatio
|
||||||
val g = world!!.gravitation // m / s^2
|
val g = world!!.gravitation // m / s^2
|
||||||
val F_k = g * mass // Newtons = kg * m / s^2
|
val F_k = g * mass // Newtons = kg * m / s^2
|
||||||
@@ -1690,28 +1632,19 @@ open class ActorWithBody : Actor {
|
|||||||
// mh'' = mg - rho*gv
|
// mh'' = mg - rho*gv
|
||||||
// h'' = (mg - rho*gv) / m
|
// h'' = (mg - rho*gv) / m
|
||||||
|
|
||||||
// on density=1000, F_k = F_bo
|
// on density=1000, 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")
|
// printdbg(this, "F_k=$F_k [N] \t F_bo=${F_bo} [N] \t density=$density")
|
||||||
|
|
||||||
val F = F_k - F_bo
|
val F = F_k - F_bo
|
||||||
|
|
||||||
val acc = F / mass // (kg * m / s^2) / kg = m / s^2
|
val acc = F / mass // (kg * m / s^2) / kg = m / s^2
|
||||||
return acc.let { Vector2(it.x, it.y.coerceAtMost(0.0)) } * SI_TO_GAME_ACC
|
val acc_game = acc.let { Vector2(it.x, it.y.coerceAtMost(0.0)) } * SI_TO_GAME_ACC
|
||||||
|
|
||||||
|
applyAcceleration(acc_game)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Vector2.ff(): Vector2 {
|
|
||||||
val x0 = this.x
|
|
||||||
val y0 = this.y
|
|
||||||
|
|
||||||
val x1 = x0.pow(2) * x0.sign
|
|
||||||
val y1 = y0.pow(2) * y0.sign
|
|
||||||
|
|
||||||
return Vector2(x1, y1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transient private var submergedRatio: Double = 0.0
|
@Transient private var submergedRatio: Double = 0.0
|
||||||
/** unit: m^3 */
|
|
||||||
// @Transient private var submergedVolume: Double = 0.0
|
|
||||||
/** unit : pixels */
|
/** unit : pixels */
|
||||||
@Transient private var submergedHeight: Double = 0.0
|
@Transient private var submergedHeight: Double = 0.0
|
||||||
|
|
||||||
@@ -1722,14 +1655,6 @@ open class ActorWithBody : Actor {
|
|||||||
// going down the platform won't show abnormal slowing (it's because of the friction prop!)
|
// going down the platform won't show abnormal slowing (it's because of the friction prop!)
|
||||||
internal inline val bodyFriction: Double
|
internal inline val bodyFriction: Double
|
||||||
get() {
|
get() {
|
||||||
/*var friction = 0.0
|
|
||||||
forEachOccupyingTileNum {
|
|
||||||
// get max friction
|
|
||||||
if (getTileFriction(it ?: Block.AIR) > friction)
|
|
||||||
friction = getTileFriction(it ?: Block.AIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
return friction*/
|
|
||||||
return getTileFriction(Block.AIR)
|
return getTileFriction(Block.AIR)
|
||||||
}
|
}
|
||||||
// after all, feet friction is what it matters
|
// after all, feet friction is what it matters
|
||||||
@@ -1828,23 +1753,6 @@ open class ActorWithBody : Actor {
|
|||||||
return density
|
return density
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get highest density (specific gravity) value from tiles that the body occupies.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
/*private inline val tileDensity: Int
|
|
||||||
get() {
|
|
||||||
var density = 0
|
|
||||||
forEachOccupyingTile {
|
|
||||||
// get max density for each tile
|
|
||||||
if (it?.density ?: 0 > density) {
|
|
||||||
density = it?.density ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return density
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private fun clampHitbox() {
|
private fun clampHitbox() {
|
||||||
if (world == null) return
|
if (world == null) return
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ internal object SetAV : ConsoleCommand {
|
|||||||
var inputval: Any
|
var inputval: Any
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputval = Integer(arg) // try for integer
|
inputval = arg.toInt() // try for integer
|
||||||
}
|
}
|
||||||
catch (e: NumberFormatException) {
|
catch (e: NumberFormatException) {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.console
|
||||||
|
|
||||||
|
import net.torvald.terrarum.*
|
||||||
|
import net.torvald.terrarum.console.ConsoleCommand
|
||||||
|
import net.torvald.terrarum.console.Echo
|
||||||
|
import net.torvald.terrarum.console.EchoError
|
||||||
|
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||||
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2024-07-21.
|
||||||
|
*/
|
||||||
|
internal object SetDensity : ConsoleCommand {
|
||||||
|
|
||||||
|
override fun printUsage() {
|
||||||
|
Echo("${ccW}Set density of specific target to desired value.")
|
||||||
|
Echo("${ccW}Usage: ${ccY}setdensity $ccG(id) <val>")
|
||||||
|
Echo("${ccW}blank ID for player. Any density less than 100 will be clamped.")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(args: Array<String>) {
|
||||||
|
|
||||||
|
// setdensity <id, or blank for player> <av> <val>
|
||||||
|
if (args.size != 3 && args.size != 2) {
|
||||||
|
printUsage()
|
||||||
|
}
|
||||||
|
else if (args.size == 2) {
|
||||||
|
val newValue = args[1].toDouble()
|
||||||
|
|
||||||
|
val player = (Terrarum.ingame!! as TerrarumIngame).actorNowPlaying
|
||||||
|
if (player == null) {
|
||||||
|
EchoError("Player does not exist")
|
||||||
|
println("[SetDensity] Player does not exist")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
player.density = newValue
|
||||||
|
Echo("${ccW}Set ${ccM}density ${ccW}for ${ccY}player ${ccW}to $ccG$newValue")
|
||||||
|
println("[SetDensity] set density '${args[1]}' for player to '$newValue'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (args.size == 3) {
|
||||||
|
try {
|
||||||
|
val id = args[1].toInt()
|
||||||
|
val newValue = args[2].toDouble()
|
||||||
|
val actor = INGAME.getActorByID(id)
|
||||||
|
|
||||||
|
if (actor is ActorWithBody) {
|
||||||
|
actor.density = newValue
|
||||||
|
Echo("${ccW}Set ${ccM}density ${ccW}for $ccY$id ${ccW}to $ccG$newValue")
|
||||||
|
println("[SetDensity] set density '${args[2]}' for $actor to '$newValue'.")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EchoError("Actor ${args[1]} is not physical")
|
||||||
|
System.err.println("[SetDensity] Actor ${args[1]} is not physical")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IllegalArgumentException) {
|
||||||
|
EchoError("${args[1]}: no actor with this ID.")
|
||||||
|
System.err.println("[SetDensity] ${args[1]}: no actor with this ID.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.isNum(): Boolean {
|
||||||
|
try {
|
||||||
|
this.toInt()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (e: NumberFormatException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,7 +115,7 @@ object WorldCamera {
|
|||||||
val newX = if (Math.abs(newX1 - oldX) < Math.abs(newX2 - oldX)) newX1 else newX2
|
val newX = if (Math.abs(newX1 - oldX) < Math.abs(newX2 - oldX)) newX1 else newX2
|
||||||
val newY = player.hitbox.centeredY - (height / 2)
|
val newY = player.hitbox.centeredY - (height / 2)
|
||||||
|
|
||||||
val pVecMagn = (player.externalV + (player.controllerV ?: nullVec)).magnitude
|
val pVecMagn = (player.externalV + player.controllerV).magnitude
|
||||||
val cVecMagn = Math.sqrt((newX - oldX).sqr() + (newY - oldY).sqr()) * fpsRatio
|
val cVecMagn = Math.sqrt((newX - oldX).sqr() + (newY - oldY).sqr()) * fpsRatio
|
||||||
|
|
||||||
// println("$cVecMagn\t$pVecMagn\t${cVecMagn / pVecMagn}")
|
// println("$cVecMagn\t$pVecMagn\t${cVecMagn / pVecMagn}")
|
||||||
|
|||||||
Reference in New Issue
Block a user