From 9f46fade1d18a046beb4b60ff29a1d9383ffaf0a Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 19 Jan 2024 03:14:32 +0900 Subject: [PATCH] deterministically random treegen --- .../terrarum/modulebasegame/TerrarumIngame.kt | 2 +- .../modulebasegame/serialise/WriteSavegame.kt | 4 -- .../modulebasegame/worldgenerator/Treegen.kt | 59 +++++++++++-------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 674841b51..b3b4d3436 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -1071,7 +1071,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { } } - private fun Point2iMod(x: Int, y: Int) = Point2i(x fmod world.width, y) + private fun Point2iMod(x: Int, y: Int) = Point2i(x fmod (world.width / CHUNK_W), y) private fun updateWorldGenerator(actor: ActorWithBody) { val pcx = (actor.intTilewiseHitbox.canonicalX.toInt() fmod world.width) / CHUNK_W diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt index 6fefc07f2..91661d436 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt @@ -201,10 +201,6 @@ object LoadSavegame { newIngame.worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)?.let { chunkFile -> val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk.toInt()) - if (layer == 2) { - printdbg(this, "Loading ore layer chunk ($cx, $cy) size: ${chunkFile.getSizePure()}") - } - ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy) world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or CHUNK_LOADED } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index aaa74eac7..c364a0d0c 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -34,9 +34,10 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + val phi = (1.0 + Math.sqrt(5.0)) / 2.0 for (i in 0 until 10) { val xs = (xStart + 9*i) until (xStart + 9*i) + 9 - tryToPlant(xs, 986578287, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong())) + tryToPlant(xs, seed.shake(java.lang.Double.doubleToLongBits(phi * (i + 1))).toInt(), makeGrassMap(xs)) } } @@ -84,13 +85,20 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams return this[(r * this.size).toInt()] } - private fun Double.toDitherredInt(rng: HQRNG): Int { + private fun Double.toDitherredInt(x: Int, y: Int, h: Int): Int { val ibase = this.floorToInt() val thre = this - ibase - return if (rng.nextDouble() < 1.0 - thre) ibase else ibase + 1 + return if (nextDouble(x, y, h) < 1.0 - thre) ibase else ibase + 1 } - private fun tryToPlant(xs: IntProgression, ys: Int, grassMap: Array>, rng: HQRNG) { + private fun nextDouble(x: Int, y: Int, h: Int): Double { + return ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216.0 + } + private fun nextFloat(x: Int, y: Int, h: Int): Float { + return ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216f + } + + private fun tryToPlant(xs: IntProgression, ys: Int, grassMap: Array>) { val treeSpecies = 0 @@ -100,8 +108,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams } // larger value = more likely to spawn large tree // range: [0, 3] - val woodsWgtD = treePlantable.map { (x, y) -> (biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0).toUint().and(3) }.average() - val woodsWgt = treePlantable.map { (x, y) -> (biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0).toUint().and(3) }.average().toDitherredInt(rng) + val woodsWgt = treePlantable.map { (x, y) -> (biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0).toUint().and(3) }.average().toDitherredInt(xs.first, ys, 234571) val treeToSpawn = when (woodsWgt) { // . - none (0) o - shrub (1) ! - small tree (2) $ - large tree (3) // 0: . . @@ -109,25 +116,25 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams // 2: ! . / . ! // 3: ! ! / $ 3 -> { - val tree = if (rng.nextDouble() > 0.5) + val tree = if (nextDouble(xs.first, ys, 541035097) > 0.5) listOf(3) else listOf(2, 2) - if (rng.nextDouble() < (params as TreegenParams).deepForestTreeProb) tree else listOf() + if (nextDouble(xs.first, ys, 6431534) < (params as TreegenParams).deepForestTreeProb) tree else listOf() } 2 -> { - val tree = if (rng.nextDouble() > 0.5) + val tree = if (nextDouble(xs.first, ys, 51216464) > 0.5) listOf(0, 2) else listOf(2, 0) - if (rng.nextDouble() < (params as TreegenParams).sparseForestTreeProb) tree else listOf() + if (nextDouble(xs.first, ys, 125098) < (params as TreegenParams).sparseForestTreeProb) tree else listOf() } 1 -> { val tree = listOf(1) - if (rng.nextDouble() < (params as TreegenParams).plainsShrubProb) tree else listOf() + if (nextDouble(xs.first, ys, 150983) < (params as TreegenParams).plainsShrubProb) tree else listOf() } else -> listOf() } @@ -142,12 +149,12 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams // if there is no grass, grassMap[x] is an empty list if (treeToSpawn[0] != 0) { grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 1234) }?.let { - plantTree(xs.first + plot1, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn + plantTree(xs.first + plot1, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn } } if (treeToSpawn[1] != 0) { grassMap[plot2].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot2, ys, 2345) }?.let { - plantTree(xs.first + plot2, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn + plantTree(xs.first + plot2, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn } } } @@ -158,7 +165,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams if (treeToSpawn[0] != 0) { val treeSize = arrayOf(null, 0, 1, 2)[treeToSpawn[0]] grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 4567) }?.let { - plantTree(xs.first + plot1, it, treeSpecies, treeSize!!, rng) + plantTree(xs.first + plot1, it, treeSpecies, treeSize!!) } } } @@ -193,7 +200,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams /** * @param y where the grass/dirt tile is */ - private fun plantTree(x: Int, y: Int, type: Int, size: Int, rng: HQRNG) { + private fun plantTree(x: Int, y: Int, type: Int, size: Int) { val trunk = "basegame:" + ((if (size <= 1) 64 else 72) + type) val foliage = "basegame:" + (112 + type) @@ -212,7 +219,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams } val stem = 1 - val bulb1 = 3 + fudgeN(1, rng) + val bulb1 = 3 + fudgeN(x, y, 4095823) // trunk for (i in 0 until stem) { @@ -244,10 +251,10 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams } // roll for dice until we get a height that fits into the given terrain - val stem = 7 + fudgeN(2, rng) - val bulb1 = 4 + fudgeN(1, rng) - val bulb2 = 3 + fudgeN(1, rng) - val bulb3 = 2 + fudgeN(1, rng) + val stem = 7 + fudgeN(x, y, 7548291, 1530948) + val bulb1 = 4 + fudgeN(x, y, 345098) + val bulb2 = 3 + fudgeN(x, y, 6093481) + val bulb3 = 2 + fudgeN(x, y, 5413879) printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3") // trunk @@ -292,11 +299,11 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams } // roll for dice until we get a height that fits into the given terrain - val stem = 15 + fudgeN(3, rng) - val bulb1 = 5 + fudgeN(1, rng) - val bulb2 = 4 + fudgeN(1, rng) - val bulb3 = 3 + fudgeN(1, rng) - val bulb4 = 2 + fudgeN(1, rng) + val stem = 15 + fudgeN(x, y, 14509, 509348, 412098357) + val bulb1 = 5 + fudgeN(x, y, 1254) + val bulb2 = 4 + fudgeN(x, y, 98134) + val bulb3 = 3 + fudgeN(x, y, 123098) + val bulb4 = 2 + fudgeN(x, y, 8712) printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3") @@ -417,7 +424,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams /** * @return normally distributed integer, for `maxvar=1`, `[-1, 0, 1]`; for `maxvar=2`, `[-2, -1, 0, 1, 2]`, etc. */ - private fun fudgeN(maxvar: Int, rng: HQRNG) = (0 until maxvar).sumOf { (rng.nextDouble() * 3).toInt() - 1 } + private fun fudgeN(x: Int, y: Int, vararg hs: Int) = hs.sumOf { (nextDouble(x, y, it) * 3).toInt() - 1 } }