mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 12:04:06 +09:00
treegen wip
This commit is contained in:
@@ -7,14 +7,16 @@ import net.torvald.terrarum.LoadScreenBase
|
|||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.concurrent.sliceEvenly
|
import net.torvald.terrarum.concurrent.sliceEvenly
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
|
import net.torvald.terrarum.gameworld.BlockAddress
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2019-09-02.
|
* 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<BlockAddress, Byte>) : Gen(world, seed, params) {
|
||||||
|
|
||||||
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
||||||
private val YHEIGHT_DIVISOR = 2.0 / 7.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 BT = 5
|
||||||
private const val LF = 6
|
private const val LF = 6
|
||||||
private const val RH = 7
|
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<Double>, world: GameWorld) {
|
private fun draw(x: Int, y: Int, noiseValue: List<Double>, world: GameWorld) {
|
||||||
val control1 = noiseValue[0].coerceIn(0.0, 0.99999).times(slices).toInt().coerceAtMost(slices - 1)
|
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 control2 = noiseValue[1].coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1)
|
||||||
|
val ba = LandUtil.getBlockAddr(world, x, y)
|
||||||
|
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
val tileThis = world.getTileFromTerrain(x, y)
|
val tileThis = world.getTileFromTerrain(x, y)
|
||||||
val wallThis = world.getTileFromWall(x, y)
|
val wallThis = world.getTileFromWall(x, y)
|
||||||
val nearbyTerr = nearbyArr.map { world.getTileFromTerrain(x + it.first, y + it.second) }
|
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 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) {
|
val grassRock = when (control1) {
|
||||||
0 -> { // woodlands
|
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
|
Block.GRASS to null
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
}
|
}
|
||||||
1 -> { // shrublands
|
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
|
Block.GRASS to null
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
}
|
}
|
||||||
2, 3 -> { // plains
|
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
|
Block.GRASS to null
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
@@ -125,6 +141,7 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
|||||||
}*/
|
}*/
|
||||||
4 -> { // rockylands
|
4 -> { // rockylands
|
||||||
if (tileThis == Block.DIRT || tileThis == Block.STONE_QUARRIED) {
|
if (tileThis == Block.DIRT || tileThis == Block.STONE_QUARRIED) {
|
||||||
|
if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_ROCKY
|
||||||
Block.STONE to Block.STONE
|
Block.STONE to Block.STONE
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
@@ -133,19 +150,23 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
|||||||
}
|
}
|
||||||
val sablum = when (control2) {
|
val sablum = when (control2) {
|
||||||
0 -> {
|
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
|
Block.STONE_QUARRIED to null
|
||||||
}
|
}
|
||||||
else if (tileThis == Block.DIRT) {
|
else if (tileThis == Block.DIRT) {
|
||||||
|
if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS
|
||||||
Block.GRAVEL to null
|
Block.GRAVEL to null
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
}
|
}
|
||||||
8 -> {
|
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
|
THISWORLD_SANDSTONE to null
|
||||||
}
|
}
|
||||||
else if (tileThis == Block.DIRT) {
|
else if (tileThis == Block.DIRT) {
|
||||||
|
if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY
|
||||||
THISWORLD_SAND to null
|
THISWORLD_SAND to null
|
||||||
}
|
}
|
||||||
else null to null
|
else null to null
|
||||||
|
|||||||
@@ -1,6 +1,143 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.worldgenerator
|
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.
|
* Created by minjaesong on 2023-11-10.
|
||||||
*/class Treegen {
|
*/
|
||||||
}
|
class Treegen(world: GameWorld, seed: Long, params: TreegenParams, val biomeMap: HashMap<BlockAddress, Byte>) : 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<List<Int>> {
|
||||||
|
val STRIDE = 4
|
||||||
|
val r = Array<List<Int>>(xs.last - xs.first + 1) { emptyList() }
|
||||||
|
|
||||||
|
|
||||||
|
for (x in xs) {
|
||||||
|
val ys = ArrayList<Int>()
|
||||||
|
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<List<Int>>) {
|
||||||
|
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<IntProgression>.rearrange(): List<IntProgression> {
|
||||||
|
val r = ArrayList<IntProgression>()
|
||||||
|
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,
|
||||||
|
)
|
||||||
@@ -7,6 +7,7 @@ import net.torvald.terrarum.App.*
|
|||||||
import net.torvald.terrarum.BlockCodex
|
import net.torvald.terrarum.BlockCodex
|
||||||
import net.torvald.terrarum.LoadScreenBase
|
import net.torvald.terrarum.LoadScreenBase
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
|
import net.torvald.terrarum.gameworld.BlockAddress
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
@@ -36,6 +37,8 @@ object Worldgen {
|
|||||||
|
|
||||||
internal lateinit var highlandLowlandSelectCache: ModuleCache
|
internal lateinit var highlandLowlandSelectCache: ModuleCache
|
||||||
internal lateinit var caveAttenuateBiasScaled: ModuleScaleDomain
|
internal lateinit var caveAttenuateBiasScaled: ModuleScaleDomain
|
||||||
|
internal lateinit var biomeMap: HashMap<BlockAddress, Byte>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Other modules are free to add their own ores to the world generator.
|
* 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_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_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_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)
|
).filter(tagFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMap(loadscreen: LoadScreenBase) {
|
fun generateMap(loadscreen: LoadScreenBase) {
|
||||||
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
|
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
|
||||||
caveAttenuateBiasScaled = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
|
caveAttenuateBiasScaled = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
|
||||||
|
biomeMap = HashMap()
|
||||||
|
|
||||||
genSlices = max(threadExecutor.threadCount, world.width / 9)
|
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(
|
data class WorldgenParams(
|
||||||
val seed: Long,
|
val seed: Long,
|
||||||
// optional parameters
|
// optional parameters
|
||||||
val terragenParams: TerragenParams = TerragenParams(),
|
val terragenParams: TerragenParams = TerragenParams(),
|
||||||
val biomegenParams: BiomegenParams = BiomegenParams()
|
val biomegenParams: BiomegenParams = BiomegenParams(),
|
||||||
|
val treegenParams: TreegenParams = TreegenParams(),
|
||||||
)
|
)
|
||||||
|
|
||||||
infix fun Long.shake(other: Long): Long {
|
infix fun Long.shake(other: Long): Long {
|
||||||
|
|||||||
Reference in New Issue
Block a user