bomb go boom but better

This commit is contained in:
minjaesong
2024-02-15 04:08:37 +09:00
parent f224d0718b
commit 2363a5c51f
6 changed files with 59 additions and 57 deletions

View File

@@ -152,9 +152,9 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
var actorsRTree: PRTree<ActorWithBody> = PRTree(actorMBRConverter, 24) // no lateinit! var actorsRTree: PRTree<ActorWithBody> = PRTree(actorMBRConverter, 24) // no lateinit!
protected set protected set
val terrainChangeQueue = ArrayList<BlockChangeQueueItem>() val terrainChangeQueue = ArrayList<BlockChangeQueueItem?>() // keep it nullable to deal with the concurrentmodification better
val wallChangeQueue = ArrayList<BlockChangeQueueItem>() val wallChangeQueue = ArrayList<BlockChangeQueueItem?>()
val wireChangeQueue = ArrayList<BlockChangeQueueItem>() // if 'old' is set and 'new' is blank, it's a wire cutter val wireChangeQueue = ArrayList<BlockChangeQueueItem?>() // if 'old' is set and 'new' is blank, it's a wire cutter
val modifiedChunks = Array(16) { TreeSet<Int>() } val modifiedChunks = Array(16) { TreeSet<Int>() }

View File

@@ -109,11 +109,13 @@ object WorldSimulator {
fun buryGrassImmediately() { fun buryGrassImmediately() {
ingame.terrainChangeQueue.toList().forEach { ingame.terrainChangeQueue.toList().forEach {
val blockProp = BlockCodex[it.new] if (it != null) {
if (blockProp.isSolid && !blockProp.isActorBlock) { val blockProp = BlockCodex[it.new]
if (world.getTileFromTerrain(it.posX, it.posY + 1) == Block.GRASS) { if (blockProp.isSolid && !blockProp.isActorBlock) {
//grassPlacedByPlayer.add(it) if (world.getTileFromTerrain(it.posX, it.posY + 1) == Block.GRASS) {
world.setTileTerrain(it.posX, it.posY + 1, Block.DIRT, true) //grassPlacedByPlayer.add(it)
world.setTileTerrain(it.posX, it.posY + 1, Block.DIRT, true)
}
} }
} }
} }

View File

@@ -1,35 +1,35 @@
package net.torvald.terrarum.modulebasegame package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.BlockCodex import net.torvald.terrarum.BlockCodex
import net.torvald.terrarum.ceilToInt
import net.torvald.terrarum.gameworld.BlockLayerI16 import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.getOffset import net.torvald.terrarum.gameworld.getOffset
import net.torvald.terrarum.tryDispose import net.torvald.terrarum.tryDispose
import net.torvald.unsafe.UnsafeHelper import net.torvald.unsafe.UnsafeHelper
import kotlin.math.max import java.lang.Math.pow
import kotlin.math.min import kotlin.math.*
/** /**
* Created by minjaesong on 2024-02-13. * Created by minjaesong on 2024-02-13.
*/ */
object ExplosionManager { object ExplosionManager {
private const val CALC_RADIUS = 127
private const val CALC_WIDTH = CALC_RADIUS * 2 + 1
fun goBoom(world: GameWorld, tx: Int, ty: Int, power: Float, callback: () -> Unit) { fun goBoom(world: GameWorld, tx: Int, ty: Int, power: Float, callback: () -> Unit) {
val CALC_RADIUS = power.ceilToInt() + 2
val CALC_WIDTH = CALC_RADIUS * 2 + 1
// create a copy of the tilemap // create a copy of the tilemap
val tilemap = BlockLayerI16(CALC_WIDTH, CALC_WIDTH) val tilemap = BlockLayerI16(CALC_WIDTH, CALC_WIDTH)
// fill in the tilemap copy // fill in the tilemap copy
for (line in 0 until CALC_WIDTH) { for (line in 0 until CALC_WIDTH) {
memcpyFromWorld(world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, tilemap) memcpyFromWorld(CALC_RADIUS, CALC_WIDTH, world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, tilemap)
} }
createExplosionWorker(world, tilemap, tx, ty, power, world, callback).start() createExplosionWorker(CALC_RADIUS, CALC_WIDTH, world, tilemap, tx, ty, power, world, callback).start()
} }
private fun memcpyFromWorld(world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerI16) { private fun memcpyFromWorld(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerI16) {
// if the bounding box must wrap around // if the bounding box must wrap around
if (xStart > world.width - CALC_RADIUS) { if (xStart > world.width - CALC_RADIUS) {
val lenLeft = world.width - xStart val lenLeft = world.width - xStart
@@ -67,33 +67,31 @@ object ExplosionManager {
* @param ty tilewise centre-y of the explosive from the world * @param ty tilewise centre-y of the explosive from the world
* @param outWorld world object to write the result to * @param outWorld world object to write the result to
*/ */
private fun createExplosionWorker(world: GameWorld, tilemap: BlockLayerI16, tx: Int, ty: Int, power: Float, outWorld: GameWorld, callback: () -> Unit): Thread { return Thread { private fun createExplosionWorker(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, tilemap: BlockLayerI16, tx: Int, ty: Int, power: Float, outWorld: GameWorld, callback: () -> Unit): Thread { return Thread {
val lightmap = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // explosion power map val mapBoomPow = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // explosion power map
val _mapThisTileOpacity = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // the tile strengths val mapTileStr = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // the tile strengths
val _mapThisTileOpacity2 = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // the tile strengths val mapTileStr2 = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // the tile strengths
val _mapLightLevelThis = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // the explosion powers var boomPow = 0f // ???
var _thisTileLuminosity = 0f // ??? var ambientAccumulator = 0f // ambient explosion power
var _ambientAccumulator = 0f // ambient explosion power var tileStr = 0f
var _thisTileOpacity = 0f var tileStr2 = 0f
var _thisTileOpacity2 = 0f
fun _swipeTask(x: Int, y: Int, x2: Int, y2: Int, swipeDiag: Boolean) {//, distFromLightSrc: Ivec4) { fun _swipeTask(x: Int, y: Int, xOld: Int, yOld: Int, swipeDiag: Boolean) {//, distFromLightSrc: Ivec4) {
if (x2 < 0 || y2 < 0 || x2 >= CALC_WIDTH || y2 >= CALC_WIDTH) return if (xOld < 0 || yOld < 0 || xOld >= CALC_WIDTH || yOld >= CALC_WIDTH) return
_ambientAccumulator = _mapLightLevelThis[x, y] ambientAccumulator = mapBoomPow[x, y]
if (!swipeDiag) { if (!swipeDiag) {
_thisTileOpacity = _mapThisTileOpacity[x, y] tileStr = mapTileStr[x, y]
_ambientAccumulator = maxOf(_ambientAccumulator, lightmap[x2, y2] - _thisTileOpacity) ambientAccumulator = maxOf(ambientAccumulator, mapBoomPow[xOld, yOld] - tileStr)
} }
else { else {
_thisTileOpacity2 = _mapThisTileOpacity2[x, y] tileStr2 = mapTileStr2[x, y]
_ambientAccumulator = maxOf(_ambientAccumulator, lightmap[x2, y2] - _thisTileOpacity2) ambientAccumulator = maxOf(ambientAccumulator, mapBoomPow[xOld, yOld] - tileStr2)
} }
_mapLightLevelThis[x, y] = _ambientAccumulator mapBoomPow[x, y] = ambientAccumulator
lightmap[x, y] = _ambientAccumulator
} }
fun swipeBoomPower(sx: Int, sy: Int, ex: Int, ey: Int, dx: Int, dy: Int, strengthmap: UnsafeFloatArray, swipeDiag: Boolean) { fun swipeBoomPower(sx: Int, sy: Int, ex: Int, ey: Int, dx: Int, dy: Int, strengthmap: UnsafeFloatArray, swipeDiag: Boolean) {
var swipeX = sx var swipeX = sx
@@ -157,47 +155,50 @@ object ExplosionManager {
val ly = rawy - (ty - CALC_RADIUS - 1) val ly = rawy - (ty - CALC_RADIUS - 1)
val (worldX, worldY) = world.coerceXY(rawx, rawy) val (worldX, worldY) = world.coerceXY(rawx, rawy)
val _thisTerrain = world.getTileFromTerrainRaw(worldX, worldY) val thisTerrain = world.getTileFromTerrainRaw(worldX, worldY)
val _thisTerrainProp = BlockCodex[world.tileNumberToNameMap[_thisTerrain.toLong()]] val thisTerrainProp = BlockCodex[world.tileNumberToNameMap[thisTerrain.toLong()]]
val thisTerrainDmg = world.getTerrainDamage(worldX, worldY)
// create tile strength map // create tile strength map
_mapThisTileOpacity.set(lx, ly, _thisTerrainProp.strength.toFloat()) mapTileStr[lx, ly] = thisTerrainProp.strength.toFloat().minus(thisTerrainDmg).toBlastResistance()
_mapThisTileOpacity2.set(lx, ly, _thisTerrainProp.strength.toFloat() * 1.41421356f) mapTileStr2[lx, ly] = thisTerrainProp.strength.toFloat().minus(thisTerrainDmg).times(1.4142135f.blastToDmg()).toBlastResistance()
// initialise explosion power map with the explosive // initialise explosion power map with the explosive
if (tx == worldX && ty == worldY) { if (tx == worldX && ty == worldY) {
_thisTileLuminosity = maxOf(_thisTileLuminosity, power) boomPow = maxOf(boomPow, power)
_mapLightLevelThis.max(lx, ly, _thisTileLuminosity) mapBoomPow.max(lx, ly, boomPow)
} }
} }
} }
// simulate explosion like strengthmaprenderer // simulate explosion like strengthmaprenderer
r1(lightmap);r2(lightmap);r3(lightmap);r4(lightmap) r1(mapBoomPow);r2(mapBoomPow);r3(mapBoomPow);r4(mapBoomPow)
r1(lightmap);r2(lightmap);r3(lightmap);r4(lightmap) r1(mapBoomPow);r2(mapBoomPow);r3(mapBoomPow);r4(mapBoomPow)
// write lightmap to the tilemap // write lightmap to the tilemap
for (rawy in worldYstart until worldYstart + CALC_WIDTH) { for (rawy in worldYstart until worldYstart + CALC_WIDTH) {
for (rawx in worldXstart until worldXstart + CALC_WIDTH) { for (rawx in worldXstart until worldXstart + CALC_WIDTH) {
val lx = rawx - (tx - CALC_RADIUS - 1) val lx = rawx - (tx - CALC_RADIUS - 1)
val ly = rawy - (ty - CALC_RADIUS - 1) val ly = rawy - (ty - CALC_RADIUS - 1)
world.inflictTerrainDamage(rawx, rawy, lightmap[lx, ly].toDouble()) world.inflictTerrainDamage(rawx, rawy, mapBoomPow[lx, ly].blastToDmg().toDouble())
} }
} }
// memcpy the tilemap to the world // memcpy the tilemap to the world
// dispose of the tilemap copy // dispose of the tilemap copy
lightmap.destroy() mapBoomPow.destroy()
_mapThisTileOpacity.destroy() mapTileStr.destroy()
_mapThisTileOpacity2.destroy() mapTileStr2.destroy()
_mapLightLevelThis.destroy()
tilemap.tryDispose() tilemap.tryDispose()
callback() callback()
} } } }
private val q = 2.828427f
private fun Float.toBlastResistance() = this.pow(1f / q)
private fun Float.blastToDmg() = this.pow(q)
private class UnsafeFloatArray(val width: Int, val height: Int) { private class UnsafeFloatArray(val width: Int, val height: Int) {

View File

@@ -1358,21 +1358,21 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
if (it is CuedByTerrainChange) { if (it is CuedByTerrainChange) {
terrainChangeQueue.toList().forEach { cue -> terrainChangeQueue.toList().forEach { cue ->
// printdbg(this, "Ingame actors terrainChangeCue: ${cue}") // printdbg(this, "Ingame actors terrainChangeCue: ${cue}")
it.updateForTerrainChange(cue) if (cue != null) it.updateForTerrainChange(cue)
} }
} }
if (it is CuedByWallChange) { if (it is CuedByWallChange) {
wallChangeQueue.toList().forEach { cue -> wallChangeQueue.toList().forEach { cue ->
// printdbg(this, "Ingame actors wallChangeCue: ${cue}") // printdbg(this, "Ingame actors wallChangeCue: ${cue}")
it.updateForWallChange(cue) if (cue != null) it.updateForWallChange(cue)
} }
} }
if (it is CuedByWireChange) { if (it is CuedByWireChange) {
wireChangeQueue.toList().forEach { cue -> wireChangeQueue.toList().forEach { cue ->
// printdbg(this, "Ingame actors wireChangeCue: ${cue}") // printdbg(this, "Ingame actors wireChangeCue: ${cue}")
it.updateForWireChange(cue) if (cue != null) it.updateForWireChange(cue)
} }
} }
} }

View File

@@ -65,7 +65,7 @@ open class ActorPrimedBomb(
/** /**
* Created by minjaesong on 2024-02-14. * Created by minjaesong on 2024-02-14.
*/ */
class ActorCherryBomb : ActorPrimedBomb(100f, 4.5f) { class ActorCherryBomb : ActorPrimedBomb(32f, 4.5f) { // 14
init { init {
val itemImage = CommonResourcePool.getAsItemSheet("basegame.items").get(0,13) val itemImage = CommonResourcePool.getAsItemSheet("basegame.items").get(0,13)

View File

@@ -17,7 +17,6 @@ import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Lightbox import net.torvald.terrarum.gameactors.Lightbox
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.isBlock import net.torvald.terrarum.gameitems.isBlock
import net.torvald.terrarum.gameitems.isWall
import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.IngameRenderer
@@ -606,18 +605,18 @@ object LightmapRenderer {
} }
private const val giScale = 0.35f private const val giScale = 0.35f
private fun _swipeTask(x: Int, y: Int, x2: Int, y2: Int, lightmap: UnsafeCvecArray, swipeDiag: Boolean) {//, distFromLightSrc: Ivec4) { private fun _swipeTask(x: Int, y: Int, xOld: Int, yOld: Int, lightmap: UnsafeCvecArray, swipeDiag: Boolean) {//, distFromLightSrc: Ivec4) {
if (x2 < 0 || y2 < 0 || x2 >= LIGHTMAP_WIDTH || y2 >= LIGHTMAP_HEIGHT) return if (xOld < 0 || yOld < 0 || xOld >= LIGHTMAP_WIDTH || yOld >= LIGHTMAP_HEIGHT) return
_mapLightLevelThis.getAndSet(_ambientAccumulator, x, y) _mapLightLevelThis.getAndSet(_ambientAccumulator, x, y)
if (!swipeDiag) { if (!swipeDiag) {
_mapThisTileOpacity.getAndSet(_thisTileOpacity, x, y) _mapThisTileOpacity.getAndSet(_thisTileOpacity, x, y)
_ambientAccumulator.maxAndAssign(darkenColoured(x2, y2, _thisTileOpacity, lightmap)) _ambientAccumulator.maxAndAssign(darkenColoured(xOld, yOld, _thisTileOpacity, lightmap))
} }
else { else {
_mapThisTileOpacity2.getAndSet(_thisTileOpacity2, x, y) _mapThisTileOpacity2.getAndSet(_thisTileOpacity2, x, y)
_ambientAccumulator.maxAndAssign(darkenColoured(x2, y2, _thisTileOpacity2, lightmap)) _ambientAccumulator.maxAndAssign(darkenColoured(xOld, yOld, _thisTileOpacity2, lightmap))
} }
_mapLightLevelThis.setVec(x, y, _ambientAccumulator) _mapLightLevelThis.setVec(x, y, _ambientAccumulator)