boom with particles

This commit is contained in:
minjaesong
2024-02-15 22:05:08 +09:00
parent a4e3474563
commit ae0a2e2564
9 changed files with 103 additions and 85 deletions

View File

@@ -1,6 +1,7 @@
{ {
"ITEM_CALENDAR": "Calendar", "ITEM_CALENDAR": "Calendar",
"ITEM_CHARCOAL": "Charcoal", "ITEM_CHARCOAL": "Charcoal",
"ITEM_CHERRY_BOMB": "Bomb",
"ITEM_COAL_COKE": "Coal Coke", "ITEM_COAL_COKE": "Coal Coke",
"ITEM_DOOR_OAK": "Oak Door", "ITEM_DOOR_OAK": "Oak Door",
"ITEM_DOOR_EBONY": "Ebony Door", "ITEM_DOOR_EBONY": "Ebony Door",

View File

@@ -1,6 +1,7 @@
{ {
"ITEM_CALENDAR": "달력", "ITEM_CALENDAR": "달력",
"ITEM_CHARCOAL": "목탄", "ITEM_CHARCOAL": "목탄",
"ITEM_CHERRY_BOMB": "폭탄",
"ITEM_COAL_COKE": "코크스", "ITEM_COAL_COKE": "코크스",
"ITEM_DOOR_OAK": "나무 문", "ITEM_DOOR_OAK": "나무 문",
"ITEM_DOOR_EBONY": "흑단 문", "ITEM_DOOR_EBONY": "흑단 문",

View File

@@ -642,9 +642,9 @@ open class GameWorld(
} }
/** /**
* @return true if block is broken * @return ItemID of the broken block AND ore if the block is broken, `null` otherwise
*/ */
fun inflictTerrainDamage(x: Int, y: Int, damage: Double): ItemID? { fun inflictTerrainDamage(x: Int, y: Int, damage: Double): Pair<ItemID?, ItemID?> {
if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)") if (damage.isNaN()) throw IllegalArgumentException("Cannot inflict NaN amount of damage at($x, $y)")
val damage = damage.toFloat() val damage = damage.toFloat()
@@ -667,12 +667,13 @@ open class GameWorld(
// remove tile from the world // remove tile from the world
if ((terrainDamages[addr] ?: 0f) >= BlockCodex[getTileFromTerrain(x, y)].strength) { if ((terrainDamages[addr] ?: 0f) >= BlockCodex[getTileFromTerrain(x, y)].strength) {
val tileBroke = getTileFromTerrain(x, y) val tileBroke = getTileFromTerrain(x, y)
val oreBroke = getTileFromOre(x, y)
setTileTerrain(x, y, Block.AIR, false) setTileTerrain(x, y, Block.AIR, false)
terrainDamages.remove(addr) terrainDamages.remove(addr)
return tileBroke return tileBroke.let { if (it == Block.AIR) null else it } to oreBroke.item.let { if (it == Block.AIR) null else it }
} }
return null return null to null
} }
fun getTerrainDamage(x: Int, y: Int): Float = fun getTerrainDamage(x: Int, y: Int): Float =
terrainDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f terrainDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f

View File

@@ -584,7 +584,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
if (currentPenTarget and PENTARGET_WALL != 0) if (currentPenTarget and PENTARGET_WALL != 0)
world.setTileWall(x, y, Block.AIR, true) world.setTileWall(x, y, Block.AIR, true)
if (currentPenTarget and PENTARGET_ORE != 0) if (currentPenTarget and PENTARGET_ORE != 0)
world.setTileOre(x, y, Block.NULL, 0) world.setTileOre(x, y, Block.AIR, 0)
} }
PENMODE_EYEDROPPER -> { PENMODE_EYEDROPPER -> {

View File

@@ -1,11 +1,14 @@
package net.torvald.terrarum.modulebasegame package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.BlockCodex import net.torvald.terrarum.BlockCodex
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.ceilToInt 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.fmod
import net.torvald.terrarum.gameworld.getOffset import net.torvald.terrarum.gameworld.getOffset
import net.torvald.terrarum.modulebasegame.gameitems.PickaxeCore
import net.torvald.unsafe.UnsafeHelper import net.torvald.unsafe.UnsafeHelper
import kotlin.math.* import kotlin.math.*
@@ -13,7 +16,7 @@ import kotlin.math.*
* Created by minjaesong on 2024-02-13. * Created by minjaesong on 2024-02-13.
*/ */
object ExplosionManager { object ExplosionManager {
fun goBoom(world: GameWorld, tx: Int, ty: Int, power: Float, callback: () -> Unit) { fun goBoom(world: GameWorld, tx: Int, ty: Int, power: Float, dropProbNonOre: Float, dropProbOre: Float, callback: () -> Unit) {
val CALC_RADIUS = power.ceilToInt() + 2 val CALC_RADIUS = power.ceilToInt() + 2
val CALC_WIDTH = CALC_RADIUS * 2 + 1 val CALC_WIDTH = CALC_RADIUS * 2 + 1
@@ -27,7 +30,7 @@ object ExplosionManager {
memcpyFromWorldBreakage(CALC_WIDTH, world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, breakmap) memcpyFromWorldBreakage(CALC_WIDTH, world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, breakmap)
} }
createExplosionWorker(CALC_RADIUS, CALC_WIDTH, world, breakmap, tilemap, tx, ty, power, callback).start() createExplosionWorker(CALC_RADIUS, CALC_WIDTH, world, breakmap, tilemap, tx, ty, power, dropProbNonOre, dropProbOre, callback).start()
} }
private fun memcpyFromWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerI16) { private fun memcpyFromWorldTiles(CALC_RADIUS: Int, CALC_WIDTH: Int, world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerI16) {
@@ -119,6 +122,8 @@ object ExplosionManager {
tilemap: BlockLayerI16, tilemap: BlockLayerI16,
tx: Int, ty: Int, tx: Int, ty: Int,
power: Float, power: Float,
dropProbNonOre: Float,
dropProbOre: Float,
callback: () -> Unit): Thread callback: () -> Unit): Thread
{ return Thread { { return Thread {
val mapBoomPow = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // explosion power map val mapBoomPow = UnsafeFloatArray(CALC_WIDTH, CALC_WIDTH) // explosion power map
@@ -146,7 +151,7 @@ object ExplosionManager {
mapBoomPow[x, y] = ambientAccumulator mapBoomPow[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, swipeDiag: Boolean) {
var swipeX = sx var swipeX = sx
var swipeY = sy var swipeY = sy
while (swipeX*dx <= ex*dx && swipeY*dy <= ey*dy) { while (swipeX*dx <= ex*dx && swipeY*dy <= ey*dy) {
@@ -169,31 +174,31 @@ object ExplosionManager {
} }
} }
fun r1(strengthmap: UnsafeFloatArray) { fun r1() {
for (line in 1 until CALC_WIDTH - 1) { for (line in 1 until CALC_WIDTH - 1) {
swipeBoomPower(1, line, CALC_WIDTH - 2, line, 1, 0, strengthmap, false) swipeBoomPower(1, line, CALC_WIDTH - 2, line, 1, 0, false)
} }
} }
fun r2(strengthmap: UnsafeFloatArray) { fun r2() {
for (line in 1 until CALC_WIDTH - 1) { for (line in 1 until CALC_WIDTH - 1) {
swipeBoomPower(line, 1, line, CALC_WIDTH - 2, 0, 1, strengthmap, false) swipeBoomPower(line, 1, line, CALC_WIDTH - 2, 0, 1, false)
} }
} }
fun r3(strengthmap: UnsafeFloatArray) { fun r3() {
for (i in 0 until CALC_WIDTH + CALC_WIDTH - 5) { for (i in 0 until CALC_WIDTH + CALC_WIDTH - 5) {
swipeBoomPower( swipeBoomPower(
max(1, i - CALC_WIDTH + 4), max(1, CALC_WIDTH - 2 - i), max(1, i - CALC_WIDTH + 4), max(1, CALC_WIDTH - 2 - i),
min(CALC_WIDTH - 2, i + 1), min(CALC_WIDTH - 2, (CALC_WIDTH + CALC_WIDTH - 5) - i), min(CALC_WIDTH - 2, i + 1), min(CALC_WIDTH - 2, (CALC_WIDTH + CALC_WIDTH - 5) - i),
1, 1, strengthmap, true 1, 1, true
) )
} }
} }
fun r4(strengthmap: UnsafeFloatArray) { fun r4() {
for (i in 0 until CALC_WIDTH + CALC_WIDTH - 5) { for (i in 0 until CALC_WIDTH + CALC_WIDTH - 5) {
swipeBoomPower( swipeBoomPower(
max(1, i - CALC_WIDTH + 4), min(CALC_WIDTH - 2, i + 1), max(1, i - CALC_WIDTH + 4), min(CALC_WIDTH - 2, i + 1),
min(CALC_WIDTH - 2, i + 1), max(1, (CALC_WIDTH - 2) - (CALC_WIDTH + CALC_WIDTH - 6) + i), min(CALC_WIDTH - 2, i + 1), max(1, (CALC_WIDTH - 2) - (CALC_WIDTH + CALC_WIDTH - 6) + i),
1, -1, strengthmap, true 1, -1, true
) )
} }
} }
@@ -225,45 +230,43 @@ object ExplosionManager {
} }
// simulate explosion like strengthmaprenderer // simulate explosion like strengthmaprenderer
r1(mapBoomPow);r2(mapBoomPow);r3(mapBoomPow);r4(mapBoomPow) r1();r2();r3();r4()
r1(mapBoomPow);r2(mapBoomPow);r3(mapBoomPow);r4(mapBoomPow) r1();r2();r3();r4()
//// just write to the damagemap lol //// just write to the damagemap lol
/*for (rawy in worldYstart until worldYstart + CALC_WIDTH) { for (wy in worldYstart until worldYstart + CALC_WIDTH) {
for (rawx in worldXstart until worldXstart + CALC_WIDTH) { for (wx in worldXstart until worldXstart + CALC_WIDTH) {
val lx = rawx - (tx - CALC_RADIUS - 1) val lx = wx - (tx - CALC_RADIUS - 1)
val ly = rawy - (ty - CALC_RADIUS - 1) val ly = wy - (ty - CALC_RADIUS - 1)
world.inflictTerrainDamage(rawx, rawy, mapBoomPow[lx, ly].blastToDmg().toDouble()) world.inflictTerrainDamage(wx, wy, mapBoomPow[lx, ly].blastToDmg().toDouble()).let { (tile, ore) ->
} if (ore != null) {
}*/ // drop ore
if (Math.random() < dropProbOre) {
PickaxeCore.dropItem(ore, wx, wy)
}
}
else if (tile != null) {
// drop tile
if (Math.random() < dropProbNonOre) {
PickaxeCore.dropItem(tile, wx, wy)
}
// write lightmap to the tilemap and breakmap PickaxeCore.makeDust(tile, wx, wy, 8 + (5 * Math.random()).toInt())
for (y in 0 until CALC_WIDTH) {
for (x in 0 until CALC_WIDTH) {
val boompower = mapBoomPow[x, y].blastToDmg()
val tile = world.tileNumberToNameMap[tilemap.unsafeGetTile(x, y).toLong()]
val tileprop = BlockCodex[tile]
val breakage = breakmap[x, y]
// remove tile // drop random disc
if (tileprop.strength - (breakage + boompower) <= 0f) { val itemprop = ItemCodex[tile]
tilemap.unsafeSetTile(x, y, world.tileNameToNumberMap[Block.AIR]!!) if (Math.random() < (1.0 / 4096.0) * (dropProbNonOre) && // prob: 1/16384
breakmap[x, y] = 0f (itemprop?.hasTag("CULTIVABLE") == true ||
} itemprop?.hasTag("SAND") == true ||
// write new breakage itemprop?.hasTag("GRAVEL") == true)
else { ) {
breakmap[x, y] = breakage + boompower PickaxeCore.dropItem(PickaxeCore.getRandomDisc(), wx, wy)
}
}
} }
} }
} }
// memcpy the tilemap and breakmap to the world
for (line in 0 until CALC_WIDTH) {
memcpyToWorldTiles(CALC_RADIUS, CALC_WIDTH, world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, tilemap)
memcpyToWorldBreakage(CALC_WIDTH, world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, breakmap)
}
// dispose of the tilemap copy // dispose of the tilemap copy
mapBoomPow.destroy() mapBoomPow.destroy()
breakmap.destroy() breakmap.destroy()

View File

@@ -14,8 +14,10 @@ import net.torvald.terrarum.modulebasegame.ExplosionManager
* Created by minjaesong on 2024-02-13. * Created by minjaesong on 2024-02-13.
*/ */
open class ActorPrimedBomb( open class ActorPrimedBomb(
private var explosionPower: Float = 1f, @Transient private var explosionPower: Float = 1f,
private var fuse: Second = 1f, private var fuse: Second = 1f,
@Transient private var dropProbNonOre: Float = 0.25f,
@Transient private var dropProbOre: Float = 0.75f
) : ActorWithBody() { ) : ActorWithBody() {
init { init {
@@ -53,11 +55,10 @@ open class ActorPrimedBomb(
INGAME.world, INGAME.world,
hitbox.centeredX.div(TILE_SIZED).minus(1.0).toInt(), hitbox.centeredX.div(TILE_SIZED).minus(1.0).toInt(),
hitbox.startY.div(TILE_SIZED).minus(1.0).toInt(), hitbox.startY.div(TILE_SIZED).minus(1.0).toInt(),
explosionPower explosionPower,
) { dropProbNonOre,
dropProbOre
) {}
}
} }
} }

View File

@@ -4,11 +4,8 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.BlockCodex
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.*
@@ -56,6 +53,8 @@ open class DroppedItem : ActorWithBody {
if (itemID.isActor()) if (itemID.isActor())
throw RuntimeException("Attempted to create DroppedItem actor of a real actor; the real actor must be dropped instead.") throw RuntimeException("Attempted to create DroppedItem actor of a real actor; the real actor must be dropped instead.")
else if (itemID.isOre())
this.itemID = OreCodex[itemID].item
isVisible = true isVisible = true

View File

@@ -101,7 +101,7 @@ object AxeCore {
INGAME.world.inflictTerrainDamage( INGAME.world.inflictTerrainDamage(
x, y, x, y,
Calculate.hatchetPower(actor, item?.material) * swingDmgToFrameDmg Calculate.hatchetPower(actor, item?.material) * swingDmgToFrameDmg
).let { tileBroken -> ).let { (tileBroken, _) ->
// tile busted // tile busted
if (tileBroken != null) { if (tileBroken != null) {
val drop = BlockCodex[tileBroken].drop val drop = BlockCodex[tileBroken].drop
@@ -129,7 +129,7 @@ object AxeCore {
INGAME.world.inflictTerrainDamage( INGAME.world.inflictTerrainDamage(
x, y, x, y,
Calculate.hatchetPower(actor, item?.material) * swingDmgToFrameDmg Calculate.hatchetPower(actor, item?.material) * swingDmgToFrameDmg
).let { tileBroken -> ).let { (tileBroken, _) ->
// tile busted // tile busted
if (tileBroken != null) { if (tileBroken != null) {
var upCtr = 0 var upCtr = 0

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame.gameitems
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
@@ -89,42 +90,43 @@ object PickaxeCore {
// filter passed, do the job // filter passed, do the job
val actionInterval = actorvalue.getAsDouble(AVKey.ACTION_INTERVAL)!! val actionInterval = actorvalue.getAsDouble(AVKey.ACTION_INTERVAL)!!
val swingDmgToFrameDmg = delta.toDouble() / actionInterval val swingDmgToFrameDmg = delta.toDouble() / actionInterval
val (oreOnTile, _) = INGAME.world.getTileFromOre(x, y)
INGAME.world.inflictTerrainDamage( INGAME.world.inflictTerrainDamage(
x, y, x, y,
Calculate.pickaxePower(actor, item?.material) * swingDmgToFrameDmg Calculate.pickaxePower(actor, item?.material) * swingDmgToFrameDmg
).let { tileBroken -> ).let { (tileBroken, oreBroken) ->
// tile busted
if (tileBroken != null) {
if (Math.random() < dropProbability) {
val drop = if (oreOnTile != Block.AIR)
OreCodex[oreOnTile].item
else
BlockCodex[tileBroken].drop
if (drop.isNotBlank()) { // drop ore
dropItem(drop, x, y) if (oreBroken != null) {
} if (Math.random() < dropProbability) {
makeDust(tile, x, y, 9) val drop = OreCodex[oreBroken].item
makeNoise(actor, tile) if (drop.isNotBlank()) dropItem(drop, x, y)
}
}
// drop tile
else if (tileBroken != null) {
if (Math.random() < dropProbability) {
val drop = BlockCodex[tileBroken].drop
if (drop.isNotBlank()) dropItem(drop, x, y)
} }
// temperary: drop random disc
// temporary: spawn random record on prob 1/4096 when digging dirts/sands/gravels
val itemprop = ItemCodex[tileBroken] val itemprop = ItemCodex[tileBroken]
if (Math.random() < 1.0 / 4096.0 && if (Math.random() < 1.0 / 4096.0 &&
(itemprop?.hasTag("CULTIVABLE") == true || (itemprop?.hasTag("CULTIVABLE") == true ||
itemprop?.hasTag("SAND") == true || itemprop?.hasTag("SAND") == true ||
itemprop?.hasTag("GRAVEL") == true) itemprop?.hasTag("GRAVEL") == true)
) { ) {
val drop = "item@basegame:${32769 + Math.random().times(9).toInt()}" dropItem(getRandomDisc(), x, y)
dropItem(drop, x, y)
} }
} }
// tile not busted
if (Math.random() < actionInterval) { // make dust
if (tileBroken != null || oreBroken != null) {
makeDust(tile, x, y, 9)
makeNoise(actor, tile)
}
else {
makeDust(tile, x, y, 1) makeDust(tile, x, y, 1)
} }
} }
@@ -146,6 +148,16 @@ object PickaxeCore {
) )
} }
fun getRandomDisc() = "item@basegame:${32769 + Math.random().times(9).toInt()}"
private fun makeDustIndices(amount: Int): List<Int> {
val ret = ArrayList<Int>()
repeat((amount / 9f).ceilToInt()) {
ret.addAll((0..8).toList().shuffled())
}
return ret.subList(0, amount)
}
private val pixelOffs = intArrayOf(2, 7, 12) // hard-coded assuming TILE_SIZE=16 private val pixelOffs = intArrayOf(2, 7, 12) // hard-coded assuming TILE_SIZE=16
fun makeDust(tile: ItemID, x: Int, y: Int, density: Int = 9, drawCol: Color = Color.WHITE) { fun makeDust(tile: ItemID, x: Int, y: Int, density: Int = 9, drawCol: Color = Color.WHITE) {
val pw = 3 val pw = 3
@@ -162,7 +174,7 @@ object PickaxeCore {
} }
val tileNum = baseTilenum + representativeTilenum // the particle won't match the visible tile anyway because of the seasons stuff val tileNum = baseTilenum + representativeTilenum // the particle won't match the visible tile anyway because of the seasons stuff
val indices = (0..8).toList().shuffled().subList(0, density) val indices = makeDustIndices(density)
for (it in indices) { for (it in indices) {
val u = pixelOffs[it % 3] val u = pixelOffs[it % 3]
val v = pixelOffs[it / 3] val v = pixelOffs[it / 3]