new gem on worldgen

This commit is contained in:
minjaesong
2024-02-13 23:42:43 +09:00
parent 6e1af36a63
commit e5ac560966
27 changed files with 263 additions and 82 deletions

View File

@@ -623,8 +623,9 @@ object ModMgr {
val scale = rec.get("scale").toDouble()
val ratio = rec.get("ratio").toDouble()
val tiling = rec.get("tiling")
val blockTagNonGrata = rec.get("blocktagnongrata").split(',').map { it.trim().toUpperCase() }.toHashSet()
Worldgen.registerOre(OregenParams(tile, freq, power, scale, ratio, tiling))
Worldgen.registerOre(OregenParams(tile, freq, power, scale, ratio, tiling, blockTagNonGrata))
}
}
catch (e: IOException) {

View File

@@ -15,19 +15,20 @@ import java.util.regex.Pattern
internal object CommandInterpreter {
private val commandsNoAuth = arrayOf(
"auth",
"qqq",
"setlocale",
"getlocale",
"help",
"version",
"tips",
"screenshot",
"resize",
"echo",
"error",
"seed",
"quicksave"
"auth",
"qqq",
"setlocale",
"getlocale",
"help",
"version",
"tips",
"screenshot",
"resize",
"echo",
"error",
"seed",
"quicksave",
"uuid"
)
internal fun execute(command: String) {

View File

@@ -293,6 +293,31 @@ class SavegameCracker(
if (worldIndex.isNotBlank()) println("${ccNoun}World UUID: $ccNoun2$worldIndex")
}
}
@Command("Removes the specified chunk(s) completely", "IDs")
fun discardchunk(args: List<String>) {
letdisk { disk ->
val ids = args[1]
val range = if (ids.matches(Regex("""[0-9]+-[0-9]+""")))
ids.substringBefore('-').toLong()..ids.substringAfter('-').toLong()
else
ids.toLong()..ids.toLong()
var rms = 0
range.forEach {
// TODO update according to the savegame format
val fileIDs = (0..15).map { layer -> 4294967296L + layer.shl(24) or it }
fileIDs.forEach {
if (disk.entries.containsKey(it)) rms += 1
disk.entries.remove(it)
VDUtil.getAsDirectory(disk, 0).remove(it)
}
}
println("${cc0}$rms entries removed")
}
}
}
internal annotation class Command(val help: String = "", val synopsis: String = "")

View File

@@ -7,9 +7,15 @@ import com.badlogic.gdx.utils.Disposable
*/
interface BlockLayer : Disposable {
val width: Int
val height: Int
val bytesPerBlock: Long
fun unsafeToBytes(x: Int, y: Int): ByteArray
fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray)
fun unsafeGetTile(x: Int, y: Int): Int
}
inline fun BlockLayer.getOffset(x: Int, y: Int): Long {
return this.bytesPerBlock * (y * this.width + x)
}

View File

@@ -18,13 +18,13 @@ import net.torvald.unsafe.UnsafePtr
*
* Note to self: refrain from using shorts--just do away with two bytes: different system have different endianness
*/
open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
open class BlockLayerI16(override val width: Int, override val height: Int) : BlockLayer {
override val bytesPerBlock = BYTES_PER_BLOCK
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK)
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock)
val ptrDestroyed: Boolean
get() = ptr.destroyed
@@ -50,7 +50,7 @@ open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
return iteratorCount < width * height * bytesPerBlock
}
override fun next(): Byte {
iteratorCount += 1
@@ -60,7 +60,7 @@ open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeGetTile(x: Int, y: Int): Int {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
@@ -68,12 +68,12 @@ open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeToBytes(x: Int, y: Int): ByteArray {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
return byteArrayOf(ptr[offset + 1], ptr[offset + 0])
}
internal fun unsafeSetTile(x: Int, y: Int, tile: Int) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
@@ -90,7 +90,7 @@ open class BlockLayerI16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
ptr[offset] = bytes[1]
ptr[offset + 1] = bytes[0]
}

View File

@@ -16,13 +16,13 @@ import net.torvald.util.Float16
*
* Created by minjaesong on 2023-10-10.
*/
class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
class BlockLayerI16F16(override val width: Int, override val height: Int) : BlockLayer {
override val bytesPerBlock = BYTES_PER_BLOCK
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK)
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock)
val ptrDestroyed: Boolean
get() = ptr.destroyed
@@ -48,7 +48,7 @@ class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
return iteratorCount < width * height * bytesPerBlock
}
override fun next(): Byte {
iteratorCount += 1
@@ -58,7 +58,7 @@ class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeGetTile(x: Int, y: Int): Int {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val hbits = (ptr[offset + 2].toUint() or ptr[offset + 3].toUint().shl(8)).toShort()
@@ -68,7 +68,7 @@ class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
}
internal fun unsafeGetTile1(x: Int, y: Int): Pair<Int, Float> {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val hbits = (ptr[offset + 2].toUint() or ptr[offset + 3].toUint().shl(8)).toShort()
@@ -78,12 +78,12 @@ class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeToBytes(x: Int, y: Int): ByteArray {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
return byteArrayOf(ptr[offset + 1], ptr[offset + 0], ptr[offset + 3], ptr[offset + 2])
}
internal fun unsafeSetTile(x: Int, y: Int, tile0: Int, fill: Float) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val hbits = Float16.fromFloat(fill).toInt().and(0xFFFF)
val tile = if (fill < FLUID_MIN_MASS) 0 else tile0
@@ -102,7 +102,7 @@ class BlockLayerI16F16(val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
ptr[offset] = bytes[1]
ptr[offset + 1] = bytes[0]
ptr[offset + 2] = bytes[3]

View File

@@ -13,13 +13,13 @@ import net.torvald.unsafe.UnsafePtr
* where a_n is a tile number, p_n is a placement index
* Created by minjaesong on 2023-10-10.
*/
class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
class BlockLayerI16I8 (override val width: Int, override val height: Int) : BlockLayer {
override val bytesPerBlock = BYTES_PER_BLOCK
// for some reason, all the efforts of saving the memory space were futile.
// using unsafe pointer gets you 100 fps, whereas using directbytebuffer gets you 90
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * BYTES_PER_BLOCK)
internal val ptr: UnsafePtr = UnsafeHelper.allocate(width * height * bytesPerBlock)
val ptrDestroyed: Boolean
get() = ptr.destroyed
@@ -45,7 +45,7 @@ class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
return object : Iterator<Byte> {
private var iteratorCount = 0L
override fun hasNext(): Boolean {
return iteratorCount < width * height * BYTES_PER_BLOCK
return iteratorCount < width * height * bytesPerBlock
}
override fun next(): Byte {
iteratorCount += 1
@@ -55,7 +55,7 @@ class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeGetTile(x: Int, y: Int): Int {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val placement = ptr[offset + 2]
@@ -64,7 +64,7 @@ class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
}
internal fun unsafeGetTile1(x: Int, y: Int): Pair<Int, Int> {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = ptr[offset]
val msb = ptr[offset + 1]
val placement = ptr[offset + 2]
@@ -73,12 +73,12 @@ class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeToBytes(x: Int, y: Int): ByteArray {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
return byteArrayOf(ptr[offset + 1], ptr[offset + 0], ptr[offset + 2])
}
internal fun unsafeSetTile(x: Int, y: Int, tile: Int, placement: Int) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()
@@ -96,14 +96,14 @@ class BlockLayerI16I8 (val width: Int, val height: Int) : BlockLayer {
}
override fun unsafeSetTile(x: Int, y: Int, bytes: ByteArray) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
ptr[offset] = bytes[1]
ptr[offset + 1] = bytes[0]
ptr[offset + 2] = bytes[2]
}
internal fun unsafeSetTileKeepPlacement(x: Int, y: Int, tile: Int) {
val offset = BYTES_PER_BLOCK * (y * width + x)
val offset = getOffset(x, y)
val lsb = tile.and(0xff).toByte()
val msb = tile.ushr(8).and(0xff).toByte()

View File

@@ -0,0 +1,80 @@
package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.getOffset
import net.torvald.terrarum.tryDispose
import net.torvald.unsafe.UnsafeHelper
/**
* Created by minjaesong on 2024-02-13.
*/
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) {
// create a copy of the tilemap
val tilemap = BlockLayerI16(CALC_WIDTH, CALC_WIDTH)
// fill in the tilemap copy
for (line in 0 until CALC_WIDTH) {
memcpyFromWorld(world, tx - CALC_RADIUS, ty - CALC_RADIUS, line, tilemap)
}
createExplosionWorker(tilemap, tx, ty, power, world).start()
}
private fun memcpyFromWorld(world: GameWorld, xStart: Int, yStart: Int, yOff: Int, out: BlockLayerI16) {
// if the bounding box must wrap around
if (xStart > world.width - CALC_RADIUS) {
val lenLeft = world.width - xStart
val lenRight = CALC_WIDTH - lenLeft
UnsafeHelper.memcpy(
world.layerTerrain.ptr,
world.layerTerrain.getOffset(xStart, yStart + yOff),
out.ptr,
out.getOffset(0, yOff),
world.layerTerrain.bytesPerBlock * lenLeft
)
UnsafeHelper.memcpy(
world.layerTerrain.ptr,
world.layerTerrain.getOffset(0, yStart + yOff),
out.ptr,
out.getOffset(lenLeft, yOff),
world.layerTerrain.bytesPerBlock * lenRight
)
}
else {
UnsafeHelper.memcpy(
world.layerTerrain.ptr,
world.layerTerrain.getOffset(xStart, yStart + yOff),
out.ptr,
out.getOffset(0, yOff),
world.layerTerrain.bytesPerBlock * CALC_WIDTH
)
}
}
/**
* @param tilemap a portion copy of the tilemap from the world, centred to the explosive
* @param tx tilewise centre-x of the explosive
* @param ty tilewise centre-y of the explosive
* @param outWorld world object to write the result to
*/
private fun createExplosionWorker(tilemap: BlockLayerI16, tx: Int, ty: Int, power: Float, outWorld: GameWorld): Thread {
return Thread {
// simulate explosion like lightmaprenderer
// write to the world
// dispose of the tilemap copy
tilemap.tryDispose()
}
}
}

View File

@@ -1116,7 +1116,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
Point2iMod(pcx - 2, pcy + 1), Point2iMod(pcx - 1, pcy + 1), Point2iMod(pcx, pcy + 1), Point2iMod(pcx + 1, pcy + 1), Point2iMod(pcx + 2, pcy + 1),
Point2iMod(pcx - 1, pcy + 2), Point2iMod(pcx, pcy + 2), Point2iMod(pcx + 1, pcy + 2),
).filter { it.y in 0 until world.height }.filter { (cx, cy) ->
world.chunkFlags[cy][cx].and(0x7F) == 0.toByte()
if (cy !in 0 until world.height / CHUNK_H) false
else (world.chunkFlags[cy][cx].and(0x7F) == 0.toByte())
}.forEach { (cx, cy) ->
Worldgen.generateChunkIngame(cx, cy) { cx, cy ->
listOf(0,1,2).forEach { layer ->

View File

@@ -37,6 +37,7 @@ internal object ExportMap : ConsoleCommand {
"ores@basegame:256" to Cvec(0xff00ffff.toInt()),
"ores@basegame:257" to Cvec(0xee77ffff.toInt()),
"ores@basegame:258" to Cvec(0xffffffff.toInt()),
"ores@basegame:259" to Cvec(0xdbd6a1ff.toInt()),
)
private val WALL_OVERLAY = Cvec(0.35f, 0.35f, 0.35f, 1f)

View File

@@ -0,0 +1,44 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.Second
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.modulebasegame.ExplosionManager
import org.dyn4j.geometry.Vector2
/**
* Created by minjaesong on 2024-02-13.
*/
open class ActorPrimedBomb : ActorWithBody {
protected constructor() {
renderOrder = RenderOrder.MIDTOP
}
private var explosionPower: Float = 1f
private var fuse: Second = 1f
constructor(
initialPos: Vector2,
initialVelo: Vector2,
power: Float,
fuse: Second
) {
renderOrder = RenderOrder.MIDTOP
this.explosionPower = power
this.fuse = fuse
}
override fun updateImpl(delta: Float) {
super.updateImpl(delta)
fuse -= delta
if (fuse <= 0f) {
physProp.usePhysics = false
ExplosionManager.goBoom(INGAME.world, intTilewiseHitbox.centeredX.toInt(), intTilewiseHitbox.centeredY.toInt(), explosionPower)
flagDespawn()
}
}
}

View File

@@ -169,6 +169,11 @@ class ItemRockSalt(originalID: ItemID) : OreItemBase(originalID, true) {
override val itemImage: TextureRegion
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(10,6)
}
class ItemNitre(originalID: ItemID) : OreItemBase(originalID, true) {
override var originalName = "ITEM_NITRE"
override val itemImage: TextureRegion
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(15,6)
}
class ItemCoalCoke(originalID: ItemID) : OreItemBase(originalID) {

View File

@@ -70,7 +70,12 @@ internal class UIInventoryCells(
// tooltip
if (Terrarum.mouseScreenX.toFloat() in encumbBarXPos..encumbBarXPos+UIInventoryCells.weightBarWidth && Terrarum.mouseScreenY.toFloat() in encumbBarYPos..encumbBarYPos+UIInventoryFull.controlHelpHeight - 6f) {
INGAME.setTooltipMessage("${actorInventory.capacity}/${actorInventory.maxCapacity}")
val capaStr = if (actorInventory.capacity >= 1125899906842624.0) /* 2^50 */
"${actorInventory.capacity}"
else
"${(actorInventory.capacity * 100L).toLong() / 100.0}"
INGAME.setTooltipMessage("$capaStr/${actorInventory.maxCapacity}.0")
tooltipShowing[10001] = true
}
else {

View File

@@ -58,7 +58,10 @@ class Oregen(world: GameWorld, isFinal: Boolean, private val caveAttenuateBiasSc
noiseValues.zip(oreTiles).firstNotNullOfOrNull { (n, tile) -> if (n > 0.5) tile else null }
val backingTile = world.getTileFromTerrain(x, y)
if (tileToPut != null && BlockCodex[backingTile].hasAllTagOf("ROCK", "OREBEARING")) {
val blockTagNonGrata = ores.firstOrNull { it.tile == tileToPut }?.blockTagNonGrata ?: hashSetOf()
val backingTileProp = BlockCodex[backingTile]
if (tileToPut != null && backingTileProp.hasAllTagOf("ROCK", "OREBEARING") && backingTileProp.hasNoTag(blockTagNonGrata)) {
// actually put the ore block
world.setTileOre(x, y, tileToPut, 0) // autotiling will be handled by the other worldgen process
}
@@ -153,4 +156,5 @@ data class OregenParams(
val scale: Double, // also adjust this if you've touched the bias value. Number can be greater than 1.0
val ratio: Double, // how stretched the ore veins are. >1.0 = stretched horizontally, <1.0 = stretched vertically
val tiling: String, // a16, a47, r16, r8
val blockTagNonGrata: HashSet<String>,
)

View File

@@ -343,9 +343,10 @@ internal object TerragenTest : NoiseMaker {
private val QUARTZ = 0//x55ff33ff.toInt()
private val AMETHYST = 0//xee77ffff.toInt()
private val ROCKSALT = 0xff00ffff.toInt()
private val NITRE = 0xdbd6a1ff.toInt()
private val oreCols = listOf(
COPPER_ORE, IRON_ORE, COAL_ORE, ZINC_ORE, TIN_ORE, GOLD_ORE, SILVER_ORE, LEAD_ORE, ROCKSALT, QUARTZ, AMETHYST
COPPER_ORE, IRON_ORE, COAL_ORE, ZINC_ORE, TIN_ORE, GOLD_ORE, SILVER_ORE, LEAD_ORE, ROCKSALT, QUARTZ, AMETHYST, NITRE
)
private val terragenYscaling = (NOISEBOX_HEIGHT / 2400.0).pow(0.75)
@@ -693,6 +694,7 @@ internal object TerragenTest : NoiseMaker {
Joise(generateOreVeinModule(caveAttenuateBiasScaledCache, seed shake "ores@basegame:256", 0.010, -0.366, 0.528, 2.4)),
Joise(generateOreVeinModule(caveAttenuateBiasScaledCache, seed shake "ores@basegame:257", 0.007, 0.100, 0.494, 1.0)),
Joise(generateOreVeinModule(caveAttenuateBiasScaledCache, seed shake "ores@basegame:258", 0.019, 0.015, 0.509, 1.0)),
Joise(generateOreVeinModule(caveAttenuateBiasScaledCache, seed shake "ores@basegame:259", 0.010, -0.166, 0.517, 1.4)),
Joise(generateRockLayer(groundScalingCached, seed, params, (0..7).map {
thicknesses[it] + marblerng.nextTriangularBal() * 0.006 to (2.6 * terragenYscaling) + it * 0.18 + marblerng.nextTriangularBal() * 0.09