From df7089f4acdb14f941c75771f1a907ed160ec721 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 11 Nov 2023 12:17:45 +0900 Subject: [PATCH] treegen wip --- .../modulebasegame/worldgenerator/Biomegen.kt | 33 +++- .../modulebasegame/worldgenerator/Treegen.kt | 141 +++++++++++++++++- .../modulebasegame/worldgenerator/Worldgen.kt | 17 ++- 3 files changed, 178 insertions(+), 13 deletions(-) diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt index 070edc2e3..3ee0d69f7 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt @@ -7,14 +7,16 @@ import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.concurrent.sliceEvenly import net.torvald.terrarum.gameitems.ItemID +import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.realestate.LandUtil import kotlin.math.cos import kotlin.math.sin /** * Created by minjaesong on 2019-09-02. */ -class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, params) { +class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashMap) : Gen(world, seed, params) { private val YHEIGHT_MAGIC = 2800.0 / 3.0 private val YHEIGHT_DIVISOR = 2.0 / 7.0 @@ -84,33 +86,47 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par private const val BT = 5 private const val LF = 6 private const val RH = 7 + + const val BIOME_KEY_WOODLANDS = 1.toByte() + const val BIOME_KEY_SHRUBLANDS = 2.toByte() + const val BIOME_KEY_PLAINS = 3.toByte() + const val BIOME_KEY_ROCKY = (-1).toByte() + const val BIOME_KEY_SANDY = (-2).toByte() + const val BIOME_KEY_GRAVELS = (-3).toByte() + } private fun draw(x: Int, y: Int, noiseValue: List, world: GameWorld) { val control1 = noiseValue[0].coerceIn(0.0, 0.99999).times(slices).toInt().coerceAtMost(slices - 1) val control2 = noiseValue[1].coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1) + val ba = LandUtil.getBlockAddr(world, x, y) if (y > 0) { val tileThis = world.getTileFromTerrain(x, y) val wallThis = world.getTileFromWall(x, y) val nearbyTerr = nearbyArr.map { world.getTileFromTerrain(x + it.first, y + it.second) } val nearbyWall = nearbyArr.map { world.getTileFromWall(x + it.first, y + it.second) } + val exposedToAir = nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR } + val hasNoFloor = (nearbyTerr[BT] == Block.AIR) val grassRock = when (control1) { 0 -> { // woodlands - if (tileThis == Block.DIRT && nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR }) { + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_WOODLANDS Block.GRASS to null } else null to null } 1 -> { // shrublands - if (tileThis == Block.DIRT && nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR }) { + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_SHRUBLANDS Block.GRASS to null } else null to null } 2, 3 -> { // plains - if (tileThis == Block.DIRT && nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR }) { + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_PLAINS Block.GRASS to null } else null to null @@ -125,6 +141,7 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par }*/ 4 -> { // rockylands if (tileThis == Block.DIRT || tileThis == Block.STONE_QUARRIED) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_ROCKY Block.STONE to Block.STONE } else null to null @@ -133,19 +150,23 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par } val sablum = when (control2) { 0 -> { - if (tileThis == Block.DIRT && (nearbyTerr[BT] == Block.AIR)) { + if (tileThis == Block.DIRT && hasNoFloor) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS Block.STONE_QUARRIED to null } else if (tileThis == Block.DIRT) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS Block.GRAVEL to null } else null to null } 8 -> { - if (tileThis == Block.DIRT && (nearbyTerr[BT] == Block.AIR)) { + if (tileThis == Block.DIRT && hasNoFloor) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY THISWORLD_SANDSTONE to null } else if (tileThis == Block.DIRT) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY THISWORLD_SAND to null } else null to null diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index a8b167612..ea988c8a3 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -1,6 +1,143 @@ package net.torvald.terrarum.modulebasegame.worldgenerator +import net.torvald.terrarum.App +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 +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_SHRUBLANDS +import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_WOODLANDS +import net.torvald.terrarum.realestate.LandUtil +import kotlin.math.absoluteValue + /** * Created by minjaesong on 2023-11-10. - */class Treegen { -} \ No newline at end of file + */ +class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: HashMap) : Gen(world, seed, params) { + + override fun getDone(loadscreen: LoadScreenBase) { + loadscreen.stageValue += 1 + loadscreen.progress.set(0L) + + Worldgen.threadExecutor.renew() + (0 until world.width).sliceEvenly(Worldgen.genSlices).rearrange().mapIndexed { i, xs -> + Worldgen.threadExecutor.submit { + tryToPlant(xs, makeGrassMap(xs)) + loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) + } + } + + Worldgen.threadExecutor.join() + + 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_SHRUBLANDS to 1.0/params.shrublandsTreeDist, + BIOME_KEY_PLAINS to 1.0/params.plainsTreeDist, + ) + + + private fun makeGrassMap(xs: IntProgression): Array> { + val STRIDE = 4 + val r = Array>(xs.last - xs.first + 1) { emptyList() } + + + for (x in xs) { + val ys = ArrayList() + var y = 0 + while (y < 800) { + val tile = world.getTileFromTerrain(x, y) + val tileProp = BlockCodex[tile] + + if (tileProp.hasAnyTagOf("ROCK", "STONE")) break + + if (tile == Block.GRASS) { + ys.add(y) + } + // if dirt was hit, climb back up until a grass is seen + else if (tile == Block.DIRT) { + var yi = 1 + var tile0 = world.getTileFromTerrain(x, y - yi) + var found = false + while (tile0 == Block.DIRT || yi < STRIDE) { + tile0 = world.getTileFromTerrain(x, y - yi) + if (tile0 == Block.GRASS) found = true + yi += 1 + } + + // filter duplicates + if (found && ys.last() != y - yi) { + ys.add(y - yi) + } + } + + y += STRIDE + } + + r[x - xs.first] = ys + } + + return r + } + + private fun tryToPlant(xs: IntProgression, grassMap: Array>) { + for (x in xs.first+1..xs.last-1) { + grassMap[x - xs.first].forEachIndexed { index, y -> + val yLeft = grassMap[x - xs.first - 1].getOrNull(index) ?: -1 + val yRight = grassMap[x - xs.first + 1].getOrNull(index) ?: -1 + + val grad1 = y - yLeft + val grad2 = yRight - y + + if ((grad1 * grad2).absoluteValue <= 1) { + val rnd = Math.random() + val biome = biomeMap[LandUtil.getBlockAddr(world, x, y)] ?: 0 + val prob = treegenProbabilityToBiome[biome]!! + + // actually plant a tree + if (rnd < prob) { + + } + } + } + + + for (y in grassMap[x - xs.first]) { + } + } + } + + /** + * Rearranges the list such that: + * `1,2,3,4,5,6,7,8,9` + * is ordered as: + * `1,5,9,2,6,3,7,4,8` + */ + private fun List.rearrange(): List { + val r = ArrayList() + val stride = this.size / 2 + + for (i in 0 until stride) { + var b = i + while (b < this.size) { + r.add(this[b]) + b += stride + } + } + + return r + } +} + +data class TreegenParams( + val woodlandsTreeDist: Int = 9, // distances are merely a suggestion tho + val shrublandsTreeDist: Int = 14, + val plainsTreeDist: Int = 21, +) \ 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 7e35075b0..daee99173 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -7,6 +7,7 @@ import net.torvald.terrarum.App.* import net.torvald.terrarum.BlockCodex import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.gameitems.ItemID +import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.TerrarumIngame @@ -36,6 +37,8 @@ object Worldgen { internal lateinit var highlandLowlandSelectCache: ModuleCache internal lateinit var caveAttenuateBiasScaled: ModuleScaleDomain + internal lateinit var biomeMap: HashMap + /** * Other modules are free to add their own ores to the world generator. @@ -62,13 +65,16 @@ object Worldgen { Work(Lang["MENU_IO_WORLDGEN_GROWING_MINERALS"], Oregen(world, caveAttenuateBiasScaled, params.seed, oreRegistry), listOf("ORES")), 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), listOf("BIOME")), + 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")), ).filter(tagFilter) } fun generateMap(loadscreen: LoadScreenBase) { highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed) caveAttenuateBiasScaled = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams) + biomeMap = HashMap() + genSlices = max(threadExecutor.threadCount, world.width / 9) @@ -271,10 +277,11 @@ abstract class Gen(val world: GameWorld, val seed: Long, val params: Any? = null } data class WorldgenParams( - val seed: Long, - // optional parameters - val terragenParams: TerragenParams = TerragenParams(), - val biomegenParams: BiomegenParams = BiomegenParams() + val seed: Long, + // optional parameters + val terragenParams: TerragenParams = TerragenParams(), + val biomegenParams: BiomegenParams = BiomegenParams(), + val treegenParams: TreegenParams = TreegenParams(), ) infix fun Long.shake(other: Long): Long {