diff --git a/assets/mods/basegame/gui/loadscr_layer04.png b/assets/mods/basegame/gui/loadscr_layer04.png new file mode 100644 index 000000000..849e0cc44 --- /dev/null +++ b/assets/mods/basegame/gui/loadscr_layer04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8285e46baa45d2eeeb7701803106e149b79b61b852fb7f0bae5fedbe5e4d4739 +size 4660 diff --git a/assets/mods/basegame/gui/loadscr_layer05.png b/assets/mods/basegame/gui/loadscr_layer05.png new file mode 100644 index 000000000..3016486fc --- /dev/null +++ b/assets/mods/basegame/gui/loadscr_layer05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87eb2b1c0811cdf8c36f963354fa87fa1c08e64081a3e50d18ebfc2c408f643d +size 9914 diff --git a/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt b/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt index 7a6cf450f..d253abfda 100644 --- a/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt @@ -26,6 +26,12 @@ open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private va CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer03") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer03.png")) } + CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer04") { + Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer04.png")) + } + CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer05") { + Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer05.png")) + } CommonResourcePool.loadAll() App.disposables.add(this) @@ -48,11 +54,11 @@ open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private va CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer01"), CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer02"), CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), + CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer04"), + CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), ) val drawWidth = Toolkit.drawWidth diff --git a/src/net/torvald/terrarum/modulebasegame/console/ExportMap2.kt b/src/net/torvald/terrarum/modulebasegame/console/ExportMap2.kt index 1b7a81631..89bdf493f 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ExportMap2.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ExportMap2.kt @@ -42,7 +42,7 @@ internal object ExportMap2 : ConsoleCommand { private fun Float.toDitherredByte(): Byte { val byteVal = this.times(255f).roundToInt() val error = this - byteVal - val errorInt = if (Math.random() < error.absoluteValue) 0 else (1 * error.sign).toInt() + val errorInt = if (Math.random() < (1 - error.absoluteValue)) 0 else (1 * error.sign).toInt() return (byteVal + errorInt).coerceIn(0..255).toByte() } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt index abb109b79..f958854d1 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt @@ -27,7 +27,11 @@ class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashM override fun getDone(loadscreen: LoadScreenBase) { - val SAND_RND = seed.ushr(7).xor(seed and 255L).and(255L).toInt() + loadscreen.stageValue += 1 + loadscreen.progress.set(0L) + + + val SAND_RND = (seed shake "SANDYCOLOURS").ushr(7).xor(seed and 255L).and(255L).toInt() val SAND_BASE = when (SAND_RND) { 255 -> 5 // green in 252..254 -> 4 // black @@ -58,6 +62,9 @@ class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashM draw(x, y, noise, world) } } + + + loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) } } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index 76b700e9f..730c70e89 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -1,9 +1,8 @@ package net.torvald.terrarum.modulebasegame.worldgenerator -import net.torvald.terrarum.App +import net.torvald.random.HQRNG +import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg -import net.torvald.terrarum.BlockCodex -import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.concurrent.sliceEvenly import net.torvald.terrarum.gameworld.BlockAddress @@ -11,6 +10,7 @@ import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_PLAINS import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_SPARSE_WOODS import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_WOODLANDS +import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.serialise.toUint import kotlin.math.absoluteValue @@ -18,7 +18,7 @@ import kotlin.math.absoluteValue /** * Created by minjaesong on 2023-11-10. */ -class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: HashMap) : Gen(world, seed, params) { +class Treegen(world: GameWorld, seed: Long, val terragenParams: TerragenParams, params: TreegenParams, val biomeMap: HashMap) : Gen(world, seed, params) { override fun getDone(loadscreen: LoadScreenBase) { loadscreen.stageValue += 1 @@ -27,7 +27,7 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: Worldgen.threadExecutor.renew() (0 until world.width).sliceEvenly(Worldgen.genSlices).rearrange().mapIndexed { i, xs -> Worldgen.threadExecutor.submit { - tryToPlant(xs, makeGrassMap(xs)) + tryToPlant(xs, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong())) loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) } } @@ -37,25 +37,16 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: App.printdbg(this, "Waking up Worldgen") } - - private val treegenProbabilityToBiome = hashMapOf( - 0.toByte() to 0.0, - BIOME_KEY_WOODLANDS to 1.0/params.woodlandsTreeDist, - BIOME_KEY_SPARSE_WOODS to 1.0/params.shrublandsTreeDist, - BIOME_KEY_PLAINS to 1.0/params.plainsTreeDist, - ) - - private fun makeGrassMap(xs: IntProgression): Array> { val r = Array>(xs.last - xs.first + 1) { emptyList() } - + val ymax = (world.height * YHEIGHT_DIVISOR + terragenParams.featureSize).ceilToInt() for (x in xs) { val ys = ArrayList() - var y = 1 + var y = (world.height * YHEIGHT_DIVISOR - terragenParams.featureSize).floorToInt().coerceAtLeast(1) var tileUp = world.getTileFromTerrain(x, y - 1) var tile = world.getTileFromTerrain(x, y) - while (y < 800) { + while (y < ymax) { if (tile == Block.GRASS && tileUp == Block.AIR) { ys.add(y) } @@ -72,14 +63,17 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: return r } - private val posTreeLarge = arrayOf(arrayOf(arrayOf(2), arrayOf(6))) - private val posTreeSmall = arrayOf(arrayOf(arrayOf(2, 3), arrayOf(6, 7)), arrayOf(arrayOf(2, 3, 6, 7), arrayOf(4, 5))) - private val treePlot1 = arrayOf(2, 3) private val treePlot2 = arrayOf(6, 7) private val treePlotM = arrayOf(4, 5) - private fun tryToPlant(xs: IntProgression, grassMap: Array>) { + private fun Double.toDitherredInt(rng: HQRNG): Int { + val ibase = this.floorToInt() + val thre = this - ibase + return if (rng.nextDouble() < (1.0 - thre)) ibase else ibase + 1 + } + + private fun tryToPlant(xs: IntProgression, grassMap: Array>, rng: HQRNG) { val treeSpecies = 0 @@ -89,52 +83,66 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: } // larger value = more likely to spawn large tree // range: [0, 3] - val woodsWgt = treePlantable.map { (x, y) -> (biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0).toUint().and(3) }.average() + 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 treeToSpawn = when (woodsWgt) { // . - none (0) o - shrub (1) ! - small tree (2) $ - large tree (3) // 0: . . // 1: o // 2: ! . / . ! - // 3: ! ! - // 4: $ ! / ! $ - // 5: $ $ - in 2.75..3.0 -> { - listOf(3, 3) + // 3: ! ! / $ + 3 -> { + val tree = if (rng.nextDouble() > 0.5) + listOf(3) + else + listOf(2, 2) + + if (rng.nextDouble() < (params as TreegenParams).deepForestTreeProb) tree else listOf() } - in 2.25..2.75 -> { - if (Math.random() < 0.5) listOf(2, 3) else listOf(3, 2) + 2 -> { + val tree = if (rng.nextDouble() > 0.5) + listOf(0, 2) + else + listOf(2, 0) + + if (rng.nextDouble() < (params as TreegenParams).sparseForestTreeProb) tree else listOf() } - in 1.75..2.25 -> { - listOf(2, 2) - } - in 1.25..1.75 -> { - if (Math.random() < 0.5) listOf(0, 2) else listOf(2, 0) - } - in 0.75..1.25 -> { - listOf(1) + 1 -> { + val tree = listOf(1) + + if (rng.nextDouble() < (params as TreegenParams).plainsShrubProb) tree else listOf() } else -> listOf() } +// printdbg(this, "Tree to spawn at [${xs.first}..${xs.last}]: $treeToSpawn (woodsWgt=$woodsWgt/$woodsWgtD)") + when (treeToSpawn.size) { 2 -> { val plot1 = if (treeToSpawn[0] < 3) treePlot1.random() else treePlot1[0] val plot2 = if (treeToSpawn[1] < 3) treePlot2.random() else treePlot2[0] // if there is no grass, grassMap[x] is an empty list - grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let { - plantTree(xs.first + plot1, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn + if (treeToSpawn[0] != 0) { + grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let { + plantTree(xs.first + plot1, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn + } } - grassMap[plot2].let { if (it.isEmpty()) null else it.random() }?.let { - plantTree(xs.first + plot2, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn + if (treeToSpawn[1] != 0) { + grassMap[plot2].let { if (it.isEmpty()) null else it.random() }?.let { + plantTree(xs.first + plot2, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn + } } } 1 -> { val plot1 = if (treeToSpawn[0] < 3) treePlotM.random() else treePlotM[0] // if there is no grass, grassMap[x] is an empty list - grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let { - plantTree(xs.first + plot1, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn + if (treeToSpawn[0] != 0) { + val treeSize = arrayOf(null, 0, 1, 2)[treeToSpawn[0]] + grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let { + plantTree(xs.first + plot1, it, treeSpecies, treeSize!!, rng) + } } } } @@ -151,7 +159,7 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: if ((grad1 * grad2).absoluteValue <= 1) { // printdbg(this, "Trying to plant tree at $x, $y") - val rnd = Math.random() + val rnd = rng.nextDouble() val biome = biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0 val prob = treegenProbabilityToBiome[biome] ?: 0.0 @@ -168,13 +176,53 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: /** * @param y where the grass/dirt tile is */ - private fun plantTree(x: Int, y: Int, type: Int, size: Int) { - val trunk = "basegame:" + ((if (size >= 1) 64 else 72) + type) + private fun plantTree(x: Int, y: Int, type: Int, size: Int, rng: HQRNG) { + val trunk = "basegame:" + ((if (size <= 1) 64 else 72) + type) val foliage = "basegame:" + (112 + type) var growCnt = 1 - if (size == 1) { - var heightSum = 5+3+2 + if (size == 0) { + val heightSum = 3 + + // check for minimum height + val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + + if (chkM1 || chk0 || chkP1) { + printdbg(this, "Ceiling not tall enough at $x, $y, aborting") + return + } + + val stem = 1 + val bulb1 = 3 + fudgeN(1, rng) + + // trunk + for (i in 0 until stem) { + for (xi in -1..+1) { + if (xi != 0) { + val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) + if (BlockCodex[tileHere].hasTag("TREETRUNK")) + world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) + } + else { + world.setTileTerrain(x + xi, y - growCnt, trunk, true) + } + } + growCnt += 1 + } + // bulb 1 + for (i in 0 until bulb1) { + for (x in x-1..x+1) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + } + } + else if (size == 1) { + val heightSum = 5+3+2+1 // check for minimum height val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } @@ -186,16 +234,10 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: } // roll for dice until we get a height that fits into the given terrain - var stem=0; var bulb1=0; var bulb2=0; var bulb3=0; -// do { - stem = 7 + fudgeN(2) - bulb1 = 4 + fudgeN(1) - bulb2 = 3 + fudgeN(1) - bulb3 = 2 + fudgeN(1) - heightSum = stem + bulb1 + bulb2 + bulb3 -// } -// while ((1..heightSum).none { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid }) - + val stem = 7 + fudgeN(2, rng) + val bulb1 = 4 + fudgeN(1, rng) + val bulb2 = 3 + fudgeN(1, rng) + val bulb3 = 2 + fudgeN(1, rng) printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3") // trunk @@ -248,6 +290,102 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: } } + else if (size == 2) { + val heightSum = 12+4+3+2+1 + // check for minimum height + val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP2 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + + if (chkM1 || chk0 || chkP1 || chkP2) { + printdbg(this, "Ceiling not tall enough at $x, $y, aborting") + return + } + + // 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) + + printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3") + + // soiling + for (i in 1..2) { + val tileLeft = world.getTileFromTerrain(x, y + i) + val wallLeft = world.getTileFromWall(x, y + i) + val tileRight = world.getTileFromTerrain(x + 1, y + i) + if (tileRight != tileLeft) { + world.setTileTerrain(x + 1, y + i, tileLeft, true) + world.setTileWall(x + 1, y + i, wallLeft, true) + } + } + // trunk + for (i in 0 until stem) { + for (xi in -1..+2) { + if (xi !in 0..1) { + val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) + if (BlockCodex[tileHere].hasTag("TREETRUNK")) + world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) + } + else { + world.setTileTerrain(x + xi, y - growCnt, trunk, true) + } + } + growCnt += 1 + } + // bulb base + for (x in x-2..x+3) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + for (x in x-3..x+4) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + // bulb 1 + for (i in 0 until bulb1) { + for (x in x-4..x+5) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + } + // bulb 2 + for (i in 0 until bulb2) { + for (x in x-3..x+4) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + } + // bulb 3 + for (i in 0 until bulb3) { + for (x in x-2..x+3) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + } + // bulb 4 + for (i in 0 until bulb4) { + for (x in x-1..x+2) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + } + } else throw IllegalArgumentException("Unknown tree size: $size") } @@ -275,12 +413,12 @@ class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: /** * @return normally distributed integer, for `maxvar=1`, `[-1, 0, 1]`; for `maxvar=2`, `[-2, -1, 0, 1, 2]`, etc. */ - private fun fudgeN(maxvar: Int) = (0 until maxvar).sumOf { (Math.random() * 3).toInt() - 1 } + private fun fudgeN(maxvar: Int, rng: HQRNG) = (0 until maxvar).sumOf { (rng.nextDouble() * 3).toInt() - 1 } } data class TreegenParams( - val woodlandsTreeDist: Int = 9, // distances are merely a suggestion tho - val shrublandsTreeDist: Int = 12, - val plainsTreeDist: Int = 16, + val deepForestTreeProb: Double = 0.8, + val sparseForestTreeProb: Double = 0.5, + val plainsShrubProb: Double = 0.25, ) \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt index 04c55447b..266ef9d78 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -66,7 +66,7 @@ object Worldgen { Work(Lang["MENU_IO_WORLDGEN_POSITIONING_ROCKS"], OregenAutotiling(world, params.seed, oreTilingModes), listOf("ORES")), Work(Lang["MENU_IO_WORLDGEN_CARVING_EARTH"], Cavegen(world, highlandLowlandSelectCache, params.seed, params.terragenParams), listOf("TERRAIN", "CAVE")), Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Biomegen(world, params.seed, params.biomegenParams, biomeMap), listOf("BIOME")), - Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Treegen(world, params.seed, params.treegenParams, biomeMap), listOf("TREES")), + Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Treegen(world, params.seed, params.terragenParams, params.treegenParams, biomeMap), listOf("TREES")), ).filter(tagFilter) }