diff --git a/.idea/libraries/lib.xml b/.idea/libraries/lib.xml index 8759a8be1..c4f1a8eef 100644 --- a/.idea/libraries/lib.xml +++ b/.idea/libraries/lib.xml @@ -19,6 +19,7 @@ + diff --git a/COPYING.md b/COPYING.md index b5cad8552..ba19791ee 100644 --- a/COPYING.md +++ b/COPYING.md @@ -66,7 +66,7 @@ limitations under the License. *Accidental Noise Library* Joise is a derivative work based on Josua Tippetts' C++ library: -http://accidentalnoise.sourceforge.net/index.html + Copyright (C) 2011 Joshua Tippetts @@ -90,7 +90,7 @@ Copyright (C) 2011 Joshua Tippetts *Vector2.java*, *Epsilon.java* -Copyright (c) 2010-2015 William Bittle http://www.dyn4j.org/ +Copyright (c) 2010-2015 William Bittle All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted @@ -127,7 +127,7 @@ Ambient sound recordings: crickets_02.ogg Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld -Sound from http://www.freesound.org/people/klankbeeld/ +Sound from ---- @@ -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_. @@ -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 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 -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/lib/prtree.jar b/lib/prtree.jar new file mode 100644 index 000000000..728a32776 Binary files /dev/null and b/lib/prtree.jar differ diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 476035515..5f013d2ea 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -56,7 +56,7 @@ object Terrarum : Disposable { /** * 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!!) // protip: using METER, game unit and SI unit will have same number diff --git a/src/net/torvald/terrarum/gameactors/ActorWBMovable.kt b/src/net/torvald/terrarum/gameactors/ActorWBMovable.kt index e8ec2d487..c96c2fa85 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWBMovable.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWBMovable.kt @@ -73,10 +73,10 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean = */ // got the idea from gl_FragCoord val hIntTilewiseHitbox: Hitbox get() = Hitbox.fromTwoPoints( - hitbox.startX.plus(0.00001).div(TILE_SIZE).floor() + 0.5, - hitbox.startY.plus(0.00001).div(TILE_SIZE).floor() + 0.5, - hitbox.endX.plus(0.00001).div(TILE_SIZE).floor() + 0.5, - hitbox.endY.plus(0.00001).div(TILE_SIZE).floor() + 0.5, + hitbox.startX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5, + hitbox.startY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5, + hitbox.endX.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5, + hitbox.endY.plus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor() + 0.5, true ) @@ -89,8 +89,8 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean = get() = Hitbox.fromTwoPoints( hitbox.startX.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(), - hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), - hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), + hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), + hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), true ) @@ -641,8 +641,8 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean = val newTilewiseHitbox = Hitbox.fromTwoPoints( hitbox.startX.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(), - hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), - hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), + hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), + hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), 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 - if (isColliding(stepBox)) { + if (isColliding(stepBox, vectorSum.y > PHYS_EPSILON_VELO)) { 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!!) 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 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!!) 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 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 */ - private fun isColliding(hitbox: Hitbox): Boolean { + private fun isColliding(hitbox: Hitbox, feet: Boolean = false): Boolean { if (isNoCollideWorld) return false // 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 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( hitbox.startX.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(), - hitbox.endX.minus(0.00001).div(TILE_SIZE).floor(), - hitbox.endY.minus(0.00001).div(TILE_SIZE).floor(), + hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), + hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floor(), true ) // NOT the same as intTilewiseHitbox !! @@ -1659,20 +1659,23 @@ open class ActorWBMovable(renderOrder: RenderOrder, val immobileBody: Boolean = * Constants */ - @Transient val METER = 24.0 + @Transient const val METER = 24.0 /** * [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] */ - @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] */ - @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 /** diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 96e7015d9..fc52ef317 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -484,9 +484,6 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { particlesActive = 0 - ingameController.update(delta) - - if (!paused) { WorldSimulator.resetForThisFrame() @@ -496,9 +493,15 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { /////////////////////////// BlockPropUtil.dynamicLumFuncTickClock() world.updateWorldTime(delta) - WorldSimulator.invoke(actorNowPlaying, delta) - WeatherMixer.update(delta, actorNowPlaying, world) - BlockStats.update() + AppLoader.measureDebugTime("WorldSimulator.update") { + WorldSimulator.invoke(actorNowPlaying, delta) + } + 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) + } + + // 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 terrainChangeQueue.clear() diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt index a3ecb717e..0483b6b97 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt @@ -11,8 +11,8 @@ import net.torvald.terrarum.worlddrawer.CreateTileAtlas object PlayerBuilderTestSubject1 { operator fun invoke(): IngamePlayer { val p: IngamePlayer = IngamePlayer( - ModMgr.getPath("basegame", "sprites/furry_sprite.properties"), - ModMgr.getPath("basegame", "sprites/furry_sprite_glow.properties"), + ModMgr.getPath("basegame", "sprites/test_sprite.properties"), + null,//ModMgr.getPath("basegame", "sprites/test_sprite_glow.properties"), -589141658L // random value thrown ) InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json") @@ -29,7 +29,7 @@ object PlayerBuilderTestSubject1 { p.sprite!!.setRowsAndFrames(2, 4)*/ p.sprite = SpriteAnimation(p) - p.spriteGlow = SpriteAnimation(p) + //p.spriteGlow = SpriteAnimation(p) p.reassembleSprite(p.sprite!!, p.spriteGlow) p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 21, 0) diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt index abb604c82..326dd1ad1 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt @@ -1,21 +1,18 @@ package net.torvald.terrarum.modulebasegame.gameworld import com.badlogic.gdx.Input -import com.badlogic.gdx.graphics.Color -import net.torvald.aa.KDTree -import net.torvald.terrarum.AppLoader -import net.torvald.terrarum.IngameInstance -import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.* import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.BlockCodex 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.gameworld.FluidType import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid -import net.torvald.terrarum.roundInt 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. @@ -57,25 +54,25 @@ object WorldSimulator { /** Bottom-right point */ var updateYTo = 0 - val colourNone = Color(0x808080FF.toInt()) - val colourWater = Color(0x66BBFFFF.toInt()) - private val ingame: IngameInstance get() = Terrarum.ingame!! private val world: GameWorld 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 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) { - // build the kdtree that will be used during a single frame of updating - if (actorsKDTree == null) - actorsKDTree = KDTree(ingame.actorContainerActive.filter { it is ActorWBMovable }) + // build the r-tree that will be used during a single frame of updating + actorsRTree = PRTree(actorMBRConverter, 24) + actorsRTree.load(ingame.actorContainerActive.filter { it is ActorWithBody }) + + //printdbg(this, "============================") @@ -85,12 +82,49 @@ object WorldSimulator { updateXTo = updateXFrom + DOUBLE_RADIUS updateYTo = updateYFrom + DOUBLE_RADIUS } - moveFluids(delta) + //moveFluids(delta) displaceFallables(delta) //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 { + val outList = ArrayList() + actorsRTree.find(startPoint.x, startPoint.y, endPoint.x, endPoint.y, outList) + return outList + } + + fun getActorsAt(worldX: Double, worldY: Double): List { + val outList = ArrayList() + 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> { + 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? { + 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, * 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) } + /** + * 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) : @@ -259,86 +377,6 @@ object WorldSimulator { 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() { for (y in 0 until fluidMap.size) { for (x in 0 until fluidMap[0].size) { @@ -380,5 +418,32 @@ object WorldSimulator { fun Int.isFallable() = BlockCodex[this].maxSupport + private val actorMBRConverter = object : MBRConverter { + 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 { + 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)) + } + } } \ No newline at end of file