throwing in a pr-tree onto the project

This commit is contained in:
minjaesong
2019-07-07 22:13:37 +09:00
parent b66ca70d6c
commit 84e4c82b60
8 changed files with 241 additions and 130 deletions

View File

@@ -19,6 +19,7 @@
<root url="jar://$PROJECT_DIR$/lib/Terrarum_Joise.jar!/" /> <root url="jar://$PROJECT_DIR$/lib/Terrarum_Joise.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/source/gdx-backend-lwjgl-sources.jar!/" /> <root url="jar://$PROJECT_DIR$/lib/source/gdx-backend-lwjgl-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/TerrarumSansBitmap.jar!/" /> <root url="jar://$PROJECT_DIR$/lib/TerrarumSansBitmap.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/prtree.jar!/" />
</SOURCES> </SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" /> <jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" />
<jarDirectory url="file://$PROJECT_DIR$/lib/source" recursive="false" type="SOURCES" /> <jarDirectory url="file://$PROJECT_DIR$/lib/source" recursive="false" type="SOURCES" />

View File

@@ -66,7 +66,7 @@ limitations under the License.
*Accidental Noise Library* *Accidental Noise Library*
Joise is a derivative work based on Josua Tippetts' C++ library: Joise is a derivative work based on Josua Tippetts' C++ library:
http://accidentalnoise.sourceforge.net/index.html <http://accidentalnoise.sourceforge.net/index.html>
Copyright (C) 2011 Joshua Tippetts Copyright (C) 2011 Joshua Tippetts
@@ -90,7 +90,7 @@ Copyright (C) 2011 Joshua Tippetts
*Vector2.java*, *Epsilon.java* *Vector2.java*, *Epsilon.java*
Copyright (c) 2010-2015 William Bittle http://www.dyn4j.org/ Copyright (c) 2010-2015 William Bittle <http://www.dyn4j.org/>
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted Redistribution and use in source and binary forms, with or without modification, are permitted
@@ -127,7 +127,7 @@ Ambient sound recordings:
crickets_02.ogg crickets_02.ogg
Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld
Sound from http://www.freesound.org/people/klankbeeld/ Sound from <http://www.freesound.org/people/klankbeeld/>
---- ----
@@ -155,6 +155,39 @@ THE SOFTWARE.
---- ----
PRTree, a Priority R-Tree, a spatial index for java code
Copyright (c) 2008-2012 Robert Olofsson.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the authors nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
----
Some of the resources were created by _raxod502_. Some of the resources were created by _raxod502_.
@@ -176,4 +209,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

BIN
lib/prtree.jar Normal file

Binary file not shown.

View File

@@ -56,7 +56,7 @@ object Terrarum : Disposable {
/** /**
* To be used with physics simulator. This is a magic number. * To be used with physics simulator. This is a magic number.
*/ */
val PHYS_TIME_FRAME: Double = 26.0 + (2.0 / 3.0) const val PHYS_TIME_FRAME: Double = 26.0 + (2.0 / 3.0)
// 26.0 + (2.0 / 3.0) // lower value == faster gravity response (IT WON'T HOTSWAP!!) // 26.0 + (2.0 / 3.0) // lower value == faster gravity response (IT WON'T HOTSWAP!!)
// protip: using METER, game unit and SI unit will have same number // protip: using METER, game unit and SI unit will have same number

View File

@@ -73,10 +73,10 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
*/ // got the idea from gl_FragCoord */ // got the idea from gl_FragCoord
val hIntTilewiseHitbox: Hitbox val hIntTilewiseHitbox: Hitbox
get() = Hitbox.fromTwoPoints( get() = Hitbox.fromTwoPoints(
hitbox.startX.plus(0.00001).div(TILE_SIZE).floor() + 0.5, hitbox.startX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5,
hitbox.startY.plus(0.00001).div(TILE_SIZE).floor() + 0.5, hitbox.startY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5,
hitbox.endX.plus(0.00001).div(TILE_SIZE).floor() + 0.5, hitbox.endX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5,
hitbox.endY.plus(0.00001).div(TILE_SIZE).floor() + 0.5, hitbox.endY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5,
true true
) )
@@ -89,8 +89,8 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
get() = Hitbox.fromTwoPoints( get() = Hitbox.fromTwoPoints(
hitbox.startX.div(TILE_SIZE).floor(), hitbox.startX.div(TILE_SIZE).floor(),
hitbox.startY.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(),
hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
true true
) )
@@ -641,8 +641,8 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
val newTilewiseHitbox = Hitbox.fromTwoPoints( val newTilewiseHitbox = Hitbox.fromTwoPoints(
hitbox.startX.div(TILE_SIZE).floor(), hitbox.startX.div(TILE_SIZE).floor(),
hitbox.startY.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(),
hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
true true
) )
@@ -690,7 +690,7 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
}*/ }*/
// trying to use same function as the others, in an effort to eliminate the "contradiction" mentioned below // trying to use same function as the others, in an effort to eliminate the "contradiction" mentioned below
if (isColliding(stepBox)) { if (isColliding(stepBox, vectorSum.y > PHYS_EPSILON_VELO)) {
collidingStep = step collidingStep = step
} }
@@ -773,13 +773,13 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
// points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!)
val offendingTileWorldX = if (selfCollisionStatus in listOf(6, 12)) val offendingTileWorldX = if (selfCollisionStatus in listOf(6, 12))
newHitbox.endX.div(TILE_SIZE).floor() * TILE_SIZE - 0.00001 newHitbox.endX.div(TILE_SIZE).floor() * TILE_SIZE - PHYS_EPSILON_DIST
else else
newHitbox.startX.div(TILE_SIZE).ceil() * TILE_SIZE newHitbox.startX.div(TILE_SIZE).ceil() * TILE_SIZE
// points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!)
val offendingTileWorldY = if (selfCollisionStatus in listOf(3, 6)) val offendingTileWorldY = if (selfCollisionStatus in listOf(3, 6))
newHitbox.endY.div(TILE_SIZE).floor() * TILE_SIZE - 0.00001 newHitbox.endY.div(TILE_SIZE).floor() * TILE_SIZE - PHYS_EPSILON_DIST
else else
newHitbox.startY.div(TILE_SIZE).ceil() * TILE_SIZE newHitbox.startY.div(TILE_SIZE).ceil() * TILE_SIZE
@@ -962,7 +962,7 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
/** /**
* @see /work_files/hitbox_collision_detection_compensation.jpg * @see /work_files/hitbox_collision_detection_compensation.jpg
*/ */
private fun isColliding(hitbox: Hitbox): Boolean { private fun isColliding(hitbox: Hitbox, feet: Boolean = false): Boolean {
if (isNoCollideWorld) return false if (isNoCollideWorld) return false
// detectors are inside of the bounding box // detectors are inside of the bounding box
@@ -978,7 +978,7 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
val tyStart = y1.plus(0.5f).div(TILE_SIZE).floorInt() val tyStart = y1.plus(0.5f).div(TILE_SIZE).floorInt()
val tyEnd = y2.plus(0.5f).div(TILE_SIZE).floorInt() val tyEnd = y2.plus(0.5f).div(TILE_SIZE).floorInt()
return isCollidingInternal(txStart, tyStart, txEnd, tyEnd) return isCollidingInternal(txStart, tyStart, txEnd, tyEnd, feet)
} }
/** /**
@@ -1604,8 +1604,8 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
val newTilewiseHitbox = Hitbox.fromTwoPoints( val newTilewiseHitbox = Hitbox.fromTwoPoints(
hitbox.startX.div(TILE_SIZE).floor(), hitbox.startX.div(TILE_SIZE).floor(),
hitbox.startY.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(),
hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(),
true true
) // NOT the same as intTilewiseHitbox !! ) // NOT the same as intTilewiseHitbox !!
@@ -1659,20 +1659,23 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean =
* Constants * Constants
*/ */
@Transient val METER = 24.0 @Transient const val METER = 24.0
/** /**
* [m / s^2] * SI_TO_GAME_ACC -> [px / InternalFrame^2] * [m / s^2] * SI_TO_GAME_ACC -> [px / InternalFrame^2]
*/ */
@Transient val SI_TO_GAME_ACC = METER / (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME) @Transient const val SI_TO_GAME_ACC = METER / (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME)
/** /**
* [m / s] * SI_TO_GAME_VEL -> [px / InternalFrame] * [m / s] * SI_TO_GAME_VEL -> [px / InternalFrame]
*/ */
@Transient val SI_TO_GAME_VEL = METER / Terrarum.PHYS_TIME_FRAME @Transient const val SI_TO_GAME_VEL = METER / Terrarum.PHYS_TIME_FRAME
/** /**
* [px / InternalFrame^2] * GAME_TO_SI_ACC -> [m / s^2] * [px / InternalFrame^2] * GAME_TO_SI_ACC -> [m / s^2]
*/ */
@Transient val GAME_TO_SI_ACC = (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME) / METER @Transient const val GAME_TO_SI_ACC = (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME) / METER
@Transient const val PHYS_EPSILON_DIST = 0.00001
@Transient const val PHYS_EPSILON_VELO = 0.0001
/** /**

View File

@@ -484,9 +484,6 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
particlesActive = 0 particlesActive = 0
ingameController.update(delta)
if (!paused) { if (!paused) {
WorldSimulator.resetForThisFrame() WorldSimulator.resetForThisFrame()
@@ -496,9 +493,15 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
/////////////////////////// ///////////////////////////
BlockPropUtil.dynamicLumFuncTickClock() BlockPropUtil.dynamicLumFuncTickClock()
world.updateWorldTime(delta) world.updateWorldTime(delta)
WorldSimulator.invoke(actorNowPlaying, delta) AppLoader.measureDebugTime("WorldSimulator.update") {
WeatherMixer.update(delta, actorNowPlaying, world) WorldSimulator.invoke(actorNowPlaying, delta)
BlockStats.update() }
AppLoader.measureDebugTime("WeatherMixer.update") {
WeatherMixer.update(delta, actorNowPlaying, world)
}
AppLoader.measureDebugTime("BlockStats.update") {
BlockStats.update()
}
@@ -524,6 +527,12 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
WorldCamera.update(gameworld, actorNowPlaying) WorldCamera.update(gameworld, actorNowPlaying)
}
// world click events (e.g. opening the UI that a fixture has) must go here
ingameController.update(delta)
if (!paused) {
// completely consume block change queues because why not // completely consume block change queues because why not
terrainChangeQueue.clear() terrainChangeQueue.clear()

View File

@@ -11,8 +11,8 @@ import net.torvald.terrarum.worlddrawer.CreateTileAtlas
object PlayerBuilderTestSubject1 { object PlayerBuilderTestSubject1 {
operator fun invoke(): IngamePlayer { operator fun invoke(): IngamePlayer {
val p: IngamePlayer = IngamePlayer( val p: IngamePlayer = IngamePlayer(
ModMgr.getPath("basegame", "sprites/furry_sprite.properties"), ModMgr.getPath("basegame", "sprites/test_sprite.properties"),
ModMgr.getPath("basegame", "sprites/furry_sprite_glow.properties"), null,//ModMgr.getPath("basegame", "sprites/test_sprite_glow.properties"),
-589141658L // random value thrown -589141658L // random value thrown
) )
InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json") InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json")
@@ -29,7 +29,7 @@ object PlayerBuilderTestSubject1 {
p.sprite!!.setRowsAndFrames(2, 4)*/ p.sprite!!.setRowsAndFrames(2, 4)*/
p.sprite = SpriteAnimation(p) p.sprite = SpriteAnimation(p)
p.spriteGlow = SpriteAnimation(p) //p.spriteGlow = SpriteAnimation(p)
p.reassembleSprite(p.sprite!!, p.spriteGlow) p.reassembleSprite(p.sprite!!, p.spriteGlow)
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 21, 0) p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 21, 0)

View File

@@ -1,21 +1,18 @@
package net.torvald.terrarum.modulebasegame.gameworld package net.torvald.terrarum.modulebasegame.gameworld
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color import net.torvald.terrarum.*
import net.torvald.aa.KDTree
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.IngameInstance
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
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.gameactors.ActorWBMovable import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.roundInt
import net.torvald.terrarum.worlddrawer.CreateTileAtlas import net.torvald.terrarum.worlddrawer.CreateTileAtlas
import net.torvald.terrarum.worlddrawer.CreateTileAtlas.TILE_SIZE
import org.khelekore.prtree.*
/** /**
* Created by minjaesong on 2016-08-03. * Created by minjaesong on 2016-08-03.
@@ -57,25 +54,25 @@ object WorldSimulator {
/** Bottom-right point */ /** Bottom-right point */
var updateYTo = 0 var updateYTo = 0
val colourNone = Color(0x808080FF.toInt())
val colourWater = Color(0x66BBFFFF.toInt())
private val ingame: IngameInstance private val ingame: IngameInstance
get() = Terrarum.ingame!! get() = Terrarum.ingame!!
private val world: GameWorld private val world: GameWorld
get() = ingame.world get() = ingame.world
// TODO use R-Tree instead? https://stackoverflow.com/questions/10269179/find-rectangles-that-contain-point-efficient-algorithm#10269695
private var actorsKDTree: KDTree? = null private lateinit var actorsRTree: PRTree<ActorWithBody>
fun resetForThisFrame() { fun resetForThisFrame() {
actorsKDTree = null
} }
/** Must be called BEFORE the actors update -- actors depend on the R-Tree for various things */
operator fun invoke(player: ActorHumanoid?, delta: Float) { operator fun invoke(player: ActorHumanoid?, delta: Float) {
// build the kdtree that will be used during a single frame of updating // build the r-tree that will be used during a single frame of updating
if (actorsKDTree == null) actorsRTree = PRTree(actorMBRConverter, 24)
actorsKDTree = KDTree(ingame.actorContainerActive.filter { it is ActorWBMovable }) actorsRTree.load(ingame.actorContainerActive.filter { it is ActorWithBody })
//printdbg(this, "============================") //printdbg(this, "============================")
@@ -85,12 +82,49 @@ object WorldSimulator {
updateXTo = updateXFrom + DOUBLE_RADIUS updateXTo = updateXFrom + DOUBLE_RADIUS
updateYTo = updateYFrom + DOUBLE_RADIUS updateYTo = updateYFrom + DOUBLE_RADIUS
} }
moveFluids(delta) //moveFluids(delta)
displaceFallables(delta) displaceFallables(delta)
//printdbg(this, "============================") //printdbg(this, "============================")
} }
/**
* @return list of actors under the bounding box given, list may be empty if no actor is under the point.
*/
fun getActorsAt(startPoint: Point2d, endPoint: Point2d): List<ActorWithBody> {
val outList = ArrayList<ActorWithBody>()
actorsRTree.find(startPoint.x, startPoint.y, endPoint.x, endPoint.y, outList)
return outList
}
fun getActorsAt(worldX: Double, worldY: Double): List<ActorWithBody> {
val outList = ArrayList<ActorWithBody>()
actorsRTree.find(worldX, worldY, worldX + 1.0, worldY + 1.0, outList)
return outList
}
/** Will use centre point of the actors
* @return List of DistanceResult, list may be empty */
fun findKNearestActors(from: ActorWithBody, maxHits: Int): List<DistanceResult<ActorWithBody>> {
return actorsRTree.nearestNeighbour(actorDistanceCalculator, null, maxHits, object : PointND {
override fun getDimensions(): Int = 2
override fun getOrd(axis: Int): Double = when(axis) {
0 -> from.hitbox.centeredX
1 -> from.hitbox.centeredY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
})
}
/** Will use centre point of the actors
* @return Pair of: the actor, distance from the actor; null if none found */
fun findNearestActors(from: ActorWithBody): DistanceResult<ActorWithBody>? {
val t = findKNearestActors(from, 1)
return if (t.isNotEmpty())
t[0]
else
null
}
/** /**
* displace fluids. Note that the code assumes the gravity pulls things downward ONLY, * displace fluids. Note that the code assumes the gravity pulls things downward ONLY,
* which means you'll need to modify the code A LOT if you're going to implement zero- or * which means you'll need to modify the code A LOT if you're going to implement zero- or
@@ -119,6 +153,90 @@ object WorldSimulator {
return ((fluid.type sameAs type || fluid.type sameAs Fluid.NULL) && !BlockCodex[tile].isSolid) return ((fluid.type sameAs type || fluid.type sameAs Fluid.NULL) && !BlockCodex[tile].isSolid)
} }
/**
* displace fallable tiles. It is scanned bottom-left first. To achieve the sens ofreal
* falling, each tiles are displaced by ONLY ONE TILE below.
*/
fun displaceFallables(delta: Float) {
/*for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
val tileBelow = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
if (tile.maxSupport()) {
// displace fluid. This statement must precede isSolid()
if (tileBelow.isFluid()) {
// remove tileThis to create air pocket
world.setTileTerrain(x, y, Block.AIR)
pour(x, y, drain(x, y, tileBelow.fluidLevel().toInt()))
// place our tile
world.setTileTerrain(x, y + 1, tile)
}
else if (!tileBelow.isSolid()) {
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + 1, tile)
}
}
}
}*/
// displace fallables (TODO implement blocks with fallable supports e.g. scaffolding)
// only displace SINGLE BOTTOMMOST block on single X-coord (this doesn't mean they must fall only one block)
// so that the "falling" should be visible to the end user
if (!DEBUG_STEPPING_MODE || DEBUG_STEPPING_MODE && KeyToggler.isOn (Input.Keys.PERIOD)) {
for (x in updateXFrom..updateXTo) {
var fallDownCounter = 0
var fallableStackProcessed = false
// one "stack" is a contiguous fallable blocks, regardless of the actual block number
// when you are simulating the gradual falling, it is natural to process all the "stacks" at the same run,
// otherwise you'll get an artefact.
for (y in updateYTo downTo updateYFrom) {
val currentTile = world.getTileFromTerrain(x, y)
val prop = BlockCodex[currentTile]
val isSolid = prop.isSolid
val support = prop.maxSupport
val isFallable = support != -1
// mark the beginnig of the new "stack"
if (fallableStackProcessed && !isFallable) {
fallableStackProcessed = false
} // do not chain with "else if"
// process the gradual falling of the selected "stack"
if (!fallableStackProcessed && fallDownCounter != 0 && isFallable) {
// replace blocks
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + fallDownCounter, currentTile)
fallableStackProcessed = true
}
else if (isSolid) {
fallDownCounter = 0
}
else if (!isSolid && !isFallable && fallDownCounter < FALLABLE_MAX_FALL_SPEED) {
fallDownCounter += 1
}
}
}
if (DEBUG_STEPPING_MODE) {
KeyToggler.forceSet(Input.Keys.PERIOD, false)
}
}
}
fun disperseHeat(delta: Float) {
}
/* /*
Explanation of get_stable_state_b (well, kind-of) : Explanation of get_stable_state_b (well, kind-of) :
@@ -259,86 +377,6 @@ object WorldSimulator {
private val FALLABLE_MAX_FALL_SPEED = 2 private val FALLABLE_MAX_FALL_SPEED = 2
/**
* displace fallable tiles. It is scanned bottom-left first. To achieve the sens ofreal
* falling, each tiles are displaced by ONLY ONE TILE below.
*/
fun displaceFallables(delta: Float) {
/*for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
val tileBelow = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
if (tile.maxSupport()) {
// displace fluid. This statement must precede isSolid()
if (tileBelow.isFluid()) {
// remove tileThis to create air pocket
world.setTileTerrain(x, y, Block.AIR)
pour(x, y, drain(x, y, tileBelow.fluidLevel().toInt()))
// place our tile
world.setTileTerrain(x, y + 1, tile)
}
else if (!tileBelow.isSolid()) {
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + 1, tile)
}
}
}
}*/
// displace fallables (TODO implement blocks with fallable supports e.g. scaffolding)
// only displace SINGLE BOTTOMMOST block on single X-coord (this doesn't mean they must fall only one block)
// so that the "falling" should be visible to the end user
if (!DEBUG_STEPPING_MODE || DEBUG_STEPPING_MODE && KeyToggler.isOn (Input.Keys.PERIOD)) {
for (x in updateXFrom..updateXTo) {
var fallDownCounter = 0
var fallableStackProcessed = false
// one "stack" is a contiguous fallable blocks, regardless of the actual block number
// when you are simulating the gradual falling, it is natural to process all the "stacks" at the same run,
// otherwise you'll get an artefact.
for (y in updateYTo downTo updateYFrom) {
val currentTile = world.getTileFromTerrain(x, y)
val prop = BlockCodex[currentTile]
val isSolid = prop.isSolid
val support = prop.maxSupport
val isFallable = support != -1
// mark the beginnig of the new "stack"
if (fallableStackProcessed && !isFallable) {
fallableStackProcessed = false
} // do not chain with "else if"
// process the gradual falling of the selected "stack"
if (!fallableStackProcessed && fallDownCounter != 0 && isFallable) {
// replace blocks
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + fallDownCounter, currentTile)
fallableStackProcessed = true
}
else if (isSolid) {
fallDownCounter = 0
}
else if (!isSolid && !isFallable && fallDownCounter < FALLABLE_MAX_FALL_SPEED) {
fallDownCounter += 1
}
}
}
if (DEBUG_STEPPING_MODE) {
KeyToggler.forceSet(Input.Keys.PERIOD, false)
}
}
}
fun disperseHeat(delta: Float) {
}
private fun monitorIllegalFluidSetup() { private fun monitorIllegalFluidSetup() {
for (y in 0 until fluidMap.size) { for (y in 0 until fluidMap.size) {
for (x in 0 until fluidMap[0].size) { for (x in 0 until fluidMap[0].size) {
@@ -380,5 +418,32 @@ object WorldSimulator {
fun Int.isFallable() = BlockCodex[this].maxSupport fun Int.isFallable() = BlockCodex[this].maxSupport
private val actorMBRConverter = object : MBRConverter<ActorWithBody> {
override fun getDimensions(): Int = 2
override fun getMin(axis: Int, t: ActorWithBody): Double =
when (axis) {
0 -> t.hitbox.startX
1 -> t.hitbox.startY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
override fun getMax(axis: Int, t: ActorWithBody): Double =
when (axis) {
0 -> t.hitbox.endX
1 -> t.hitbox.endY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
}
// simple euclidean norm, squared
private val actorDistanceCalculator = object : DistanceCalculator<ActorWithBody> {
override fun distanceTo(t: ActorWithBody, p: PointND): Double {
val dist1 = (p.getOrd(0) - t.hitbox.centeredX).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
// ROUNDWORLD implementation
val dist2 = (p.getOrd(0) - (t.hitbox.centeredX - world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
val dist3 = (p.getOrd(0) - (t.hitbox.centeredX + world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
return minOf(dist1, minOf(dist2, dist3))
}
}
} }