mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
worldgen: actually generating ores
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INCONSEQUENTIAL,AIR"
|
||||
"1";"0";"0";"BLOCK_UPDATE";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INTERNAL"
|
||||
"16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
|
||||
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK"
|
||||
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
|
||||
"18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE"
|
||||
"19";"19";"19";"BLOCK_STONE_BRICKS";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"STONE"
|
||||
"20";"20";"20";"BLOCK_STONE_DEEP";"0.1252";"0.1252";"0.1252";"0.1252";"80";"24600";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
|
||||
|
||||
|
@@ -17,6 +17,7 @@ id;classname
|
||||
16;net.torvald.terrarum.modulebasegame.gameitems.ItemWorkbench
|
||||
|
||||
128;net.torvald.terrarum.modulebasegame.gameitems.OreCopper
|
||||
129;net.torvald.terrarum.modulebasegame.gameitems.OreIron
|
||||
|
||||
256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
|
||||
257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
|
||||
|
||||
|
BIN
assets/mods/basegame/ores/1.tga
LFS
BIN
assets/mods/basegame/ores/1.tga
LFS
Binary file not shown.
BIN
assets/mods/basegame/ores/2.tga
LFS
Normal file
BIN
assets/mods/basegame/ores/2.tga
LFS
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
"id";"item";"tags"
|
||||
"1";"item@basegame:128";"COPPER,MALACHITE"
|
||||
# "2";"item@basegame:129";"IRON,HEMATITE"
|
||||
"2";"item@basegame:129";"IRON,HEMATITE"
|
||||
# "3";"item@basegame:130";"GOLD,NATURAL_GOLD"
|
||||
# "4";"item@basegame:131";"COAL,CARBON"
|
||||
# "5";"item@basegame:132";"ZINC,SPHALERITE"
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3 and column 3.
|
4
assets/mods/basegame/ores/worldgen.csv
Normal file
4
assets/mods/basegame/ores/worldgen.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
"id";"freq";"power";"scale";"comment"
|
||||
"1";"0.026";"0.01";"0.505";"copper (malachite)"
|
||||
"2";"0.040";"0.01";"0.505";"iron (haematite)"
|
||||
#"3";"0.040";"0.08";"0.501";"coal"
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 2.
|
@@ -15,6 +15,8 @@ import net.torvald.terrarum.gameitems.ItemID
|
||||
import net.torvald.terrarum.itemproperties.ItemCodex
|
||||
import net.torvald.terrarum.itemproperties.MaterialCodex
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.OregenParams
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
|
||||
import net.torvald.terrarum.serialise.Common
|
||||
import net.torvald.terrarum.utils.CSVFetcher
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
@@ -508,7 +510,23 @@ object ModMgr {
|
||||
}
|
||||
|
||||
@JvmStatic operator fun invoke(module: String) {
|
||||
// register ore codex
|
||||
Terrarum.oreCodex.fromModule(module, "ores/ores.csv")
|
||||
|
||||
// register to worldgen
|
||||
try {
|
||||
CSVFetcher.readFromModule(module, "ores/worldgen.csv").forEach { rec ->
|
||||
val tile = "ores@$module:${rec.get("id")}"
|
||||
val freq = rec.get("freq").toDouble()
|
||||
val power = rec.get("power").toDouble()
|
||||
val scale = rec.get("scale").toDouble()
|
||||
|
||||
Worldgen.registerOre(OregenParams(tile, freq, power, scale))
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -582,6 +582,9 @@ open class GameWorld(
|
||||
throw IllegalArgumentException("illegal mode input: $mode")
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return (Block.AIR, 0) if there is no ore
|
||||
*/
|
||||
fun getTileFromOre(rawX: Int, rawY: Int): OrePlacement {
|
||||
val (x, y) = coerceXY(rawX, rawY)
|
||||
val (tileNum, placement) = layerOres.unsafeGetTile1(x, y)
|
||||
|
||||
@@ -569,7 +569,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement ->
|
||||
acc or (placement.item == palSelection).toInt(index)
|
||||
}
|
||||
val placement = BlocksDrawer.connectLut47[autotiled]
|
||||
val placement = BlocksDrawer.connectLut16[autotiled]
|
||||
|
||||
world.setTileOre(x, y, palSelection, placement)
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ open class DroppedItem : ActorWithBody {
|
||||
visualItemID = BlockCodex.getOrNull(itemID)?.world ?: itemID
|
||||
}
|
||||
if (textureRegion == null) {
|
||||
textureRegion = ItemCodex.getItemImage(visualItemID)!!
|
||||
textureRegion = ItemCodex.getItemImage(visualItemID) ?: throw NullPointerException("No Item image for ${visualItemID}")
|
||||
}
|
||||
|
||||
// copy-pasted from ActorWithBody.drawSpriteInGoodPosition()
|
||||
|
||||
@@ -35,3 +35,8 @@ class OreCopper(originalID: ItemID) : OreItemBase(originalID) {
|
||||
override val itemImage: TextureRegion
|
||||
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(2,6)
|
||||
}
|
||||
class OreIron(originalID: ItemID) : OreItemBase(originalID) {
|
||||
override var originalName = "ITEM_ORE_HAEMATITE"
|
||||
override val itemImage: TextureRegion
|
||||
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(3,6)
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
package net.torvald.terrarum.modulebasegame.worldgenerator
|
||||
|
||||
import com.sudoplay.joise.Joise
|
||||
import com.sudoplay.joise.module.*
|
||||
import net.torvald.terrarum.BlockCodex
|
||||
import net.torvald.terrarum.Point2i
|
||||
import net.torvald.terrarum.blockproperties.Block
|
||||
import net.torvald.terrarum.concurrent.sliceEvenly
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_MAGIC
|
||||
import net.torvald.terrarum.toInt
|
||||
import net.torvald.terrarum.utils.OrePlacement
|
||||
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sin
|
||||
@@ -14,7 +20,7 @@ import kotlin.math.sin
|
||||
/**
|
||||
* Created by minjaesong on 2023-10-25.
|
||||
*/
|
||||
class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, params) {
|
||||
class Oregen(world: GameWorld, private val caveAttenuateBiasScaled: ModuleScaleDomain, seed: Long, private val ores: List<OregenParams>) : Gen(world, seed) {
|
||||
|
||||
private val threadExecutor = TerrarumIngame.worldgenThreadExecutor
|
||||
private val genSlices = max(threadExecutor.threadCount, world.width / 8)
|
||||
@@ -23,7 +29,7 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
|
||||
threadExecutor.renew()
|
||||
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
|
||||
threadExecutor.submit {
|
||||
val localJoise = getGenerator(seed, params as OregenParams)
|
||||
val localJoise = getGenerator(seed)
|
||||
for (x in xs) {
|
||||
val sampleTheta = (x.toDouble() / world.width) * TWO_PI
|
||||
val sampleOffset = world.width / 8.0
|
||||
@@ -38,10 +44,15 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
|
||||
/**
|
||||
* @return List of noise instances, each instance refers to one of the spawnable ores
|
||||
*/
|
||||
private fun getGenerator(seed: Long, params: OregenParams): List<Joise> {
|
||||
TODO()
|
||||
private fun getGenerator(seed: Long): List<Joise> {
|
||||
return ores.map {
|
||||
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake it.tile, it.freq, it.power, it.scale))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indices of `noises` has one-to-one mapping to the `ores`
|
||||
*/
|
||||
private fun draw(x: Int, noises: List<Joise>, st: Double, soff: Double) {
|
||||
for (y in 0 until world.height) {
|
||||
val sx = sin(st) * soff + soff // plus sampleOffset to make only
|
||||
@@ -50,15 +61,90 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
|
||||
// DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant
|
||||
|
||||
// get the actual noise values
|
||||
// the size of the two lists are guaranteed to be identical as they all derive from the same `ores`
|
||||
val noiseValues = noises.map { it.get(sx, sy, sz) }
|
||||
val oreTiles = ores.map { it.tile }
|
||||
|
||||
TODO()
|
||||
val tileToPut = noiseValues.zip(oreTiles).firstNotNullOfOrNull { (n, tile) -> if (n > 0.5) tile else null }
|
||||
val backingTile = world.getTileFromTerrain(x, y)
|
||||
|
||||
if (tileToPut != null && BlockCodex[backingTile].hasTag("ROCK")) {
|
||||
// actually put the ore block
|
||||
world.setTileOre(x, y, tileToPut, 0) // autotiling will be handled by the other worldgen process
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun applyPowMult(joiseModule: Module, pow: Double, mult: Double): Module {
|
||||
return ModuleScaleOffset().also {
|
||||
it.setSource(ModulePow().also {
|
||||
it.setSource(joiseModule)
|
||||
it.setPower(pow)
|
||||
})
|
||||
it.setScale(mult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateOreVeinModule(caveAttenuateBiasScaled: ModuleScaleDomain, seed: Long, freq: Double, pow: Double, scale: Double): Module {
|
||||
val oreShape = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(2)
|
||||
it.setFrequency(freq) // adjust the "density" of the caves
|
||||
it.seed = seed
|
||||
}
|
||||
|
||||
val oreShape2 = ModuleScaleOffset().also {
|
||||
it.setSource(oreShape)
|
||||
it.setScale(1.0)
|
||||
it.setOffset(-0.5)
|
||||
}
|
||||
|
||||
val caveAttenuateBias3 = applyPowMult(caveAttenuateBiasScaled, pow, scale)
|
||||
|
||||
val oreShapeAttenuate = ModuleCombiner().also {
|
||||
it.setType(ModuleCombiner.CombinerType.MULT)
|
||||
it.setSource(0, oreShape2)
|
||||
it.setSource(1, caveAttenuateBias3)
|
||||
}
|
||||
|
||||
val orePerturbFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(6)
|
||||
it.setFrequency(freq * 3.0 / 4.0)
|
||||
it.seed = seed shake 0x5721CE_76E_EA276L // strike the earth
|
||||
}
|
||||
|
||||
val orePerturbScale = ModuleScaleOffset().also {
|
||||
it.setSource(orePerturbFractal)
|
||||
it.setScale(20.0)
|
||||
it.setOffset(0.0)
|
||||
}
|
||||
|
||||
val orePerturb = ModuleTranslateDomain().also {
|
||||
it.setSource(oreShapeAttenuate)
|
||||
it.setAxisXSource(orePerturbScale)
|
||||
}
|
||||
|
||||
val oreSelect = ModuleSelect().also {
|
||||
it.setLowSource(0.0)
|
||||
it.setHighSource(1.0)
|
||||
it.setControlSource(orePerturb)
|
||||
it.setThreshold(0.5)
|
||||
it.setFalloff(0.0)
|
||||
}
|
||||
|
||||
return oreSelect
|
||||
}
|
||||
}
|
||||
|
||||
data class OregenParams(
|
||||
val oreShapeFreq: Double = 0.04, //adjust the "density" of the caves
|
||||
val oreAttenuateBias: Double = 0.90, // adjust the "concentration" of the cave gen. Lower = larger voids
|
||||
val oreSelectThre: Double = 0.918, // also adjust this if you've touched the bias value. Number can be greater than 1.0
|
||||
val tile: String,
|
||||
val freq: Double, //adjust the "density" of the caves
|
||||
val power: Double, // adjust the "concentration" of the cave gen. Lower = larger voids
|
||||
val scale: Double, // also adjust this if you've touched the bias value. Number can be greater than 1.0
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
package net.torvald.terrarum.modulebasegame.worldgenerator
|
||||
|
||||
import net.torvald.terrarum.Point2i
|
||||
import net.torvald.terrarum.concurrent.sliceEvenly
|
||||
import net.torvald.terrarum.gameitems.isOre
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.toInt
|
||||
import net.torvald.terrarum.utils.OrePlacement
|
||||
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-10-26.
|
||||
*/
|
||||
class OregenAutotiling(world: GameWorld, seed: Long) : Gen(world, seed) {
|
||||
|
||||
private val threadExecutor = TerrarumIngame.worldgenThreadExecutor
|
||||
private val genSlices = max(threadExecutor.threadCount, world.width / 8)
|
||||
|
||||
override fun getDone() {
|
||||
threadExecutor.renew()
|
||||
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
|
||||
threadExecutor.submit {
|
||||
for (x in xs) {
|
||||
draw(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
threadExecutor.join()
|
||||
}
|
||||
|
||||
|
||||
private fun draw(x: Int) {
|
||||
for (y in 0 until world.height) {
|
||||
val (ore, _) = world.getTileFromOre(x, y)
|
||||
|
||||
if (ore.isOre()) {
|
||||
// get placement (tile connection) info
|
||||
val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement ->
|
||||
acc or (placement.item == ore).toInt(index)
|
||||
}
|
||||
val placement = BlocksDrawer.connectLut16[autotiled]
|
||||
|
||||
// actually put the ore block
|
||||
world.setTileOre(x, y, ore, placement) // autotiling will be handled by the other worldgen process
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun getNearbyTilesPos8(x: Int, y: Int): Array<Point2i> {
|
||||
return arrayOf(
|
||||
Point2i(x + 1, y),
|
||||
Point2i(x + 1, y + 1),
|
||||
Point2i(x, y + 1),
|
||||
Point2i(x - 1, y + 1),
|
||||
Point2i(x - 1, y),
|
||||
Point2i(x - 1, y - 1),
|
||||
Point2i(x, y - 1),
|
||||
Point2i(x + 1, y - 1)
|
||||
)
|
||||
}
|
||||
private fun getNearbyOres8(x: Int, y: Int): List<OrePlacement> {
|
||||
return getNearbyTilesPos8(x, y).map { world.getTileFromOre(it.x, it.y) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import kotlin.math.sin
|
||||
/**
|
||||
* Created by minjaesong on 2019-07-23.
|
||||
*/
|
||||
class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, params) {
|
||||
class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, seed, params) {
|
||||
|
||||
companion object {
|
||||
const val YHEIGHT_MAGIC = 2800.0 / 3.0
|
||||
@@ -137,160 +137,10 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
||||
|
||||
|
||||
private fun getGenerator(seed: Long, params: TerragenParams): List<Joise> {
|
||||
val lowlandMagic: Long = 0x41A21A114DBE56 // Maria Lindberg
|
||||
val highlandMagic: Long = 0x0114E091 // Olive Oyl
|
||||
val mountainMagic: Long = 0x115AA4DE2504 // Lisa Anderson
|
||||
val selectionMagic: Long = 0x41E10D9B100 // Melody Blue
|
||||
|
||||
val caveMagic: Long = 0x00215741CDF // Urist McDF
|
||||
val cavePerturbMagic: Long = 0xA2410C // Armok
|
||||
val caveBlockageMagic: Long = 0xD15A57E5 // Disaster
|
||||
|
||||
|
||||
val groundGradient = ModuleGradient().also {
|
||||
it.setGradient(0.0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
|
||||
/* lowlands */
|
||||
|
||||
val lowlandShapeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.BILLOW)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(2)
|
||||
it.setFrequency(0.25)
|
||||
it.seed = seed shake lowlandMagic
|
||||
}
|
||||
|
||||
val lowlandScale = ModuleScaleOffset().also {
|
||||
it.setSource(lowlandShapeFractal)
|
||||
it.setScale(0.22)
|
||||
it.setOffset(params.lowlandScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val lowlandYScale = ModuleScaleDomain().also {
|
||||
it.setSource(lowlandScale)
|
||||
it.setScaleY(0.02) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val lowlandTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(lowlandYScale)
|
||||
}
|
||||
|
||||
/* highlands */
|
||||
|
||||
val highlandShapeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(4)
|
||||
it.setFrequency(2.0)
|
||||
it.seed = seed shake highlandMagic
|
||||
}
|
||||
|
||||
val highlandScale = ModuleScaleOffset().also {
|
||||
it.setSource(highlandShapeFractal)
|
||||
it.setScale(0.5)
|
||||
it.setOffset(params.highlandScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val highlandYScale = ModuleScaleDomain().also {
|
||||
it.setSource(highlandScale)
|
||||
it.setScaleY(0.14) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val highlandTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(highlandYScale)
|
||||
}
|
||||
|
||||
/* mountains */
|
||||
|
||||
val mountainShapeFractal = ModuleFractal().also {
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(8)
|
||||
it.setFrequency(1.0)
|
||||
it.seed = seed shake mountainMagic
|
||||
}
|
||||
|
||||
val mountainScale = ModuleScaleOffset().also {
|
||||
it.setSource(mountainShapeFractal)
|
||||
it.setScale(1.0)
|
||||
it.setOffset(params.mountainScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val mountainYScale = ModuleScaleDomain().also {
|
||||
it.setSource(mountainScale)
|
||||
it.setScaleY(params.mountainDisturbance) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val mountainTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(mountainYScale)
|
||||
}
|
||||
|
||||
/* selection */
|
||||
|
||||
val terrainTypeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(3)
|
||||
it.setFrequency(0.125)
|
||||
it.seed = seed shake selectionMagic
|
||||
}
|
||||
|
||||
val terrainScaleOffset = ModuleScaleOffset().also {
|
||||
it.setSource(terrainTypeFractal)
|
||||
it.setOffset(0.5)
|
||||
it.setScale(0.666666) // greater = more dynamic terrain
|
||||
}
|
||||
|
||||
val terrainTypeYScale = ModuleScaleDomain().also {
|
||||
it.setSource(terrainScaleOffset)
|
||||
it.setScaleY(0.0)
|
||||
}
|
||||
|
||||
val terrainTypeCache = ModuleCache().also {
|
||||
it.setSource(terrainTypeYScale)
|
||||
}
|
||||
|
||||
val highlandMountainSelect = ModuleSelect().also {
|
||||
it.setLowSource(highlandTerrain)
|
||||
it.setHighSource(mountainTerrain)
|
||||
it.setControlSource(terrainTypeCache)
|
||||
it.setThreshold(0.55)
|
||||
it.setFalloff(0.2)
|
||||
}
|
||||
|
||||
val highlandLowlandSelect = ModuleSelect().also {
|
||||
it.setLowSource(lowlandTerrain)
|
||||
it.setHighSource(highlandMountainSelect)
|
||||
it.setControlSource(terrainTypeCache)
|
||||
it.setThreshold(0.25)
|
||||
it.setFalloff(0.15)
|
||||
}
|
||||
|
||||
val highlandLowlandSelectCache = ModuleCache().also {
|
||||
it.setSource(highlandLowlandSelect)
|
||||
}
|
||||
|
||||
val groundSelect = ModuleSelect().also {
|
||||
it.setLowSource(0.0)
|
||||
it.setHighSource(1.0)
|
||||
it.setThreshold(0.5)
|
||||
it.setControlSource(highlandLowlandSelectCache)
|
||||
}
|
||||
|
||||
val groundSelect2 = ModuleSelect().also {
|
||||
it.setLowSource(0.0)
|
||||
it.setHighSource(1.0)
|
||||
it.setThreshold(0.8)
|
||||
it.setControlSource(highlandLowlandSelectCache)
|
||||
}
|
||||
|
||||
/* caves */
|
||||
|
||||
val caveShape = ModuleFractal().also {
|
||||
@@ -401,6 +251,7 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
||||
it.setSource(caveClamp)
|
||||
}
|
||||
|
||||
|
||||
//return Joise(caveInMix)
|
||||
return listOf(
|
||||
Joise(groundScaling),
|
||||
@@ -422,13 +273,13 @@ data class TerragenParams(
|
||||
val caveBlockageFractalFreq: Double = 8.88,
|
||||
val caveBlockageSelectThre: Double = 1.40, // adjust cave cloing-up strength. Larger = more closing
|
||||
|
||||
val oreCopperFreq: Double = 0.024, // adjust the "density" of the ore veins
|
||||
val oreCopperPower: Double = 0.01, // super-low value almost negates the depth element
|
||||
val oreCopperScale: Double = 0.505,
|
||||
// val oreCopperFreq: Double = 0.024, // adjust the "density" of the ore veins
|
||||
// val oreCopperPower: Double = 0.01, // super-low value almost negates the depth element
|
||||
// val oreCopperScale: Double = 0.505,
|
||||
|
||||
val oreIronFreq: Double = 0.04, // adjust the "density" of the ore veins
|
||||
val oreIronPower: Double = 0.01, // super-low value almost negates the depth element
|
||||
val oreIronScale: Double = 0.505,
|
||||
// val oreIronFreq: Double = 0.04, // adjust the "density" of the ore veins
|
||||
// val oreIronPower: Double = 0.01, // super-low value almost negates the depth element
|
||||
// val oreIronScale: Double = 0.505,
|
||||
|
||||
|
||||
// 0.01 - 0.505
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.torvald.terrarum.modulebasegame.worldgenerator
|
||||
|
||||
import com.sudoplay.joise.module.*
|
||||
import net.torvald.random.XXHash64
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.App.*
|
||||
import net.torvald.terrarum.BlockCodex
|
||||
@@ -24,10 +26,27 @@ object Worldgen {
|
||||
params = genParams
|
||||
}
|
||||
|
||||
internal lateinit var highlandLowlandSelectCache: ModuleCache
|
||||
internal lateinit var caveAttenuateBiasScaled: ModuleScaleDomain
|
||||
|
||||
/**
|
||||
* Other modules are free to add their own ores to the world generator.
|
||||
*/
|
||||
fun registerOre(oreInfo: OregenParams) {
|
||||
oreRegistry.add(oreInfo)
|
||||
}
|
||||
|
||||
private val oreRegistry = ArrayList<OregenParams>()
|
||||
|
||||
fun generateMap() {
|
||||
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
|
||||
caveAttenuateBiasScaled = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
|
||||
|
||||
val jobs = listOf(
|
||||
Work("Reticulating Splines", Terragen(world, params.seed, params.terragenParams)),
|
||||
Work("Adding Vegetations", Biomegen(world, params.seed, params.biomegenParams))
|
||||
Work("Reticulating Splines", Terragen(world, highlandLowlandSelectCache, params.seed, params.terragenParams)),
|
||||
Work("Adding Rocks", Oregen(world, caveAttenuateBiasScaled, params.seed, oreRegistry)),
|
||||
Work("Positioning Rocks", OregenAutotiling(world, params.seed)),
|
||||
Work("Adding Vegetations", Biomegen(world, params.seed, params.biomegenParams)),
|
||||
)
|
||||
|
||||
|
||||
@@ -66,9 +85,163 @@ object Worldgen {
|
||||
fun getEstimationSec(width: Int, height: Int): Long {
|
||||
return (23.05 * 1.25 * (48600000.0 / bogoflops) * ((width * height) / 40095000.0) * (32.0 / THREAD_COUNT)).roundToLong()
|
||||
}
|
||||
|
||||
private fun getHighlandLowlandSelectCache(params: TerragenParams, seed: Long): ModuleCache {
|
||||
val lowlandMagic: Long = 0x41A21A114DBE56 // Maria Lindberg
|
||||
val highlandMagic: Long = 0x0114E091 // Olive Oyl
|
||||
val mountainMagic: Long = 0x115AA4DE2504 // Lisa Anderson
|
||||
val selectionMagic: Long = 0x41E10D9B100 // Melody Blue
|
||||
|
||||
val groundGradient = ModuleGradient().also {
|
||||
it.setGradient(0.0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
|
||||
/* lowlands */
|
||||
|
||||
val lowlandShapeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.BILLOW)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(2)
|
||||
it.setFrequency(0.25)
|
||||
it.seed = seed shake lowlandMagic
|
||||
}
|
||||
|
||||
val lowlandScale = ModuleScaleOffset().also {
|
||||
it.setSource(lowlandShapeFractal)
|
||||
it.setScale(0.22)
|
||||
it.setOffset(params.lowlandScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val lowlandYScale = ModuleScaleDomain().also {
|
||||
it.setSource(lowlandScale)
|
||||
it.setScaleY(0.02) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val lowlandTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(lowlandYScale)
|
||||
}
|
||||
|
||||
/* highlands */
|
||||
|
||||
val highlandShapeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(4)
|
||||
it.setFrequency(2.0)
|
||||
it.seed = seed shake highlandMagic
|
||||
}
|
||||
|
||||
val highlandScale = ModuleScaleOffset().also {
|
||||
it.setSource(highlandShapeFractal)
|
||||
it.setScale(0.5)
|
||||
it.setOffset(params.highlandScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val highlandYScale = ModuleScaleDomain().also {
|
||||
it.setSource(highlandScale)
|
||||
it.setScaleY(0.14) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val highlandTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(highlandYScale)
|
||||
}
|
||||
|
||||
/* mountains */
|
||||
|
||||
val mountainShapeFractal = ModuleFractal().also {
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(8)
|
||||
it.setFrequency(1.0)
|
||||
it.seed = seed shake mountainMagic
|
||||
}
|
||||
|
||||
val mountainScale = ModuleScaleOffset().also {
|
||||
it.setSource(mountainShapeFractal)
|
||||
it.setScale(1.0)
|
||||
it.setOffset(params.mountainScaleOffset) // linearly alters the height
|
||||
}
|
||||
|
||||
val mountainYScale = ModuleScaleDomain().also {
|
||||
it.setSource(mountainScale)
|
||||
it.setScaleY(params.mountainDisturbance) // greater = more distortion, overhangs
|
||||
}
|
||||
|
||||
val mountainTerrain = ModuleTranslateDomain().also {
|
||||
it.setSource(groundGradient)
|
||||
it.setAxisYSource(mountainYScale)
|
||||
}
|
||||
|
||||
/* selection */
|
||||
|
||||
val terrainTypeFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(3)
|
||||
it.setFrequency(0.125)
|
||||
it.seed = seed shake selectionMagic
|
||||
}
|
||||
|
||||
val terrainScaleOffset = ModuleScaleOffset().also {
|
||||
it.setSource(terrainTypeFractal)
|
||||
it.setOffset(0.5)
|
||||
it.setScale(0.666666) // greater = more dynamic terrain
|
||||
}
|
||||
|
||||
val terrainTypeYScale = ModuleScaleDomain().also {
|
||||
it.setSource(terrainScaleOffset)
|
||||
it.setScaleY(0.0)
|
||||
}
|
||||
|
||||
val terrainTypeCache = ModuleCache().also {
|
||||
it.setSource(terrainTypeYScale)
|
||||
}
|
||||
|
||||
val highlandMountainSelect = ModuleSelect().also {
|
||||
it.setLowSource(highlandTerrain)
|
||||
it.setHighSource(mountainTerrain)
|
||||
it.setControlSource(terrainTypeCache)
|
||||
it.setThreshold(0.55)
|
||||
it.setFalloff(0.2)
|
||||
}
|
||||
|
||||
val highlandLowlandSelect = ModuleSelect().also {
|
||||
it.setLowSource(lowlandTerrain)
|
||||
it.setHighSource(highlandMountainSelect)
|
||||
it.setControlSource(terrainTypeCache)
|
||||
it.setThreshold(0.25)
|
||||
it.setFalloff(0.15)
|
||||
}
|
||||
|
||||
val highlandLowlandSelectCache = ModuleCache().also {
|
||||
it.setSource(highlandLowlandSelect)
|
||||
}
|
||||
|
||||
return highlandLowlandSelectCache
|
||||
}
|
||||
|
||||
private fun getCaveAttenuateBiasScaled(highlandLowlandSelectCache: ModuleCache, params: TerragenParams): ModuleScaleDomain {
|
||||
val caveAttenuateBias = ModuleBias().also {
|
||||
it.setSource(highlandLowlandSelectCache)
|
||||
it.setBias(params.caveAttenuateBias) // (0.5+) adjust the "concentration" of the cave gen. Lower = larger voids
|
||||
}
|
||||
|
||||
return ModuleScaleDomain().also {
|
||||
it.setScaleX(1.0 / params.featureSize) // adjust this value to change features size
|
||||
it.setScaleY(1.0 / params.featureSize)
|
||||
it.setScaleZ(1.0 / params.featureSize)
|
||||
it.setSource(caveAttenuateBias)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any) {
|
||||
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any? = null) {
|
||||
open fun getDone() { } // trying to use different name so that it won't be confused with Runnable or Callable
|
||||
}
|
||||
|
||||
@@ -90,5 +263,9 @@ infix fun Long.shake(other: Long): Long {
|
||||
return s0 + s1
|
||||
}
|
||||
|
||||
infix fun Long.shake(other: String): Long {
|
||||
return this shake XXHash64.hash(other.toByteArray(), this + 31)
|
||||
}
|
||||
|
||||
val TWO_PI = Math.PI * 2.0
|
||||
val HALF_PI = Math.PI / 2.0
|
||||
|
||||
@@ -81,7 +81,6 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
||||
|
||||
private val NM_TERR = TerragenTest to TerragenParams()
|
||||
private val NM_BIOME = BiomeMaker to BiomegenParams()
|
||||
private val NM_ORES = Oregen to OregenParams()
|
||||
|
||||
private val NOISEMAKER = NM_TERR
|
||||
|
||||
@@ -687,8 +686,8 @@ internal object TerragenTest : NoiseMaker {
|
||||
return listOf(
|
||||
Joise(groundScaling),
|
||||
Joise(caveScaling),
|
||||
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake 0xC08204, params.oreCopperFreq, params.oreCopperPower, params.oreCopperScale)),
|
||||
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake 0xFE2204, params.oreIronFreq, params.oreIronPower, params.oreIronScale)),
|
||||
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake "ores@basegame:1", 0.024, 0.01, 0.505)),
|
||||
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake "ores@basegame:2", 0.04, 0.01, 0.505)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -759,84 +758,6 @@ internal object TerragenTest : NoiseMaker {
|
||||
}
|
||||
|
||||
|
||||
internal object Oregen : NoiseMaker {
|
||||
override fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap) {
|
||||
var n = noiseValue[0]
|
||||
|
||||
// if (n in 0.0..1.0) n = 1.0 - n
|
||||
|
||||
val cout = if (n >= 0.0)
|
||||
Color(n.toFloat(), n.toFloat(), n.toFloat(), 1f)
|
||||
else
|
||||
Color(-n.toFloat(), 0f, 1f, 1f)
|
||||
|
||||
outTex.drawPixel(x, y, cout.toRGBA())
|
||||
}
|
||||
|
||||
override fun getGenerator(seed: Long, params: Any): List<Joise> {
|
||||
val params = params as OregenParams
|
||||
|
||||
val oreMagic = 0x023L
|
||||
val orePerturbMagic = 12345L
|
||||
|
||||
val oreShape = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(2)
|
||||
it.setFrequency(params.oreShapeFreq) // adjust the "density" of the caves
|
||||
it.seed = seed shake oreMagic
|
||||
}
|
||||
|
||||
val oreShape2 = ModuleScaleOffset().also {
|
||||
it.setSource(oreShape)
|
||||
it.setScale(1.0)
|
||||
it.setOffset(-0.5)
|
||||
}
|
||||
|
||||
val orePerturbFractal = ModuleFractal().also {
|
||||
it.setType(ModuleFractal.FractalType.FBM)
|
||||
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||
it.setNumOctaves(6)
|
||||
it.setFrequency(params.oreShapeFreq * 3.0 / 4.0)
|
||||
it.seed = seed shake orePerturbMagic
|
||||
}
|
||||
|
||||
val orePerturbScale = ModuleScaleOffset().also {
|
||||
it.setSource(orePerturbFractal)
|
||||
it.setScale(20.0)
|
||||
it.setOffset(0.0)
|
||||
}
|
||||
|
||||
val orePerturb = ModuleTranslateDomain().also {
|
||||
it.setSource(oreShape2)
|
||||
it.setAxisXSource(orePerturbScale)
|
||||
}
|
||||
|
||||
val oreSelectAttenuate = ModulePow().also {
|
||||
it.setSource(ModuleGradient().also {
|
||||
it.setGradient(0.0, 0.0, NOISEBOX_HEIGHT.toDouble() * 5, 100.0)
|
||||
})
|
||||
it.setPower(1.0 / 4.0)
|
||||
}
|
||||
|
||||
val oreSelect = ModuleSelect().also {
|
||||
it.setLowSource(0.0)
|
||||
it.setHighSource(1.0)
|
||||
it.setControlSource(orePerturb)
|
||||
it.setThreshold(oreSelectAttenuate)
|
||||
it.setFalloff(0.0)
|
||||
}
|
||||
|
||||
return listOf(
|
||||
Joise(oreSelect)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*infix fun Long.shake(other: Long): Long {
|
||||
var s0 = this
|
||||
var s1 = other
|
||||
|
||||
@@ -248,7 +248,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
|
||||
val wallNum = it.getTileFromWall(mouseTileX, mouseTileY)
|
||||
val tileNum = it.getTileFromTerrain(mouseTileX, mouseTileY)
|
||||
val oreNum = it.getTileFromOre(mouseTileX, mouseTileY).item
|
||||
val (oreNum, orePlacement) = it.getTileFromOre(mouseTileX, mouseTileY)
|
||||
val wires = it.getAllWiresFrom(mouseTileX, mouseTileY)
|
||||
val fluid = it.getFluid(mouseTileX, mouseTileY)
|
||||
val wireCount = wires.first?.size?.toString() ?: "no"
|
||||
@@ -256,7 +256,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
App.fontSmallNumbers.draw(batch, "$ccO$TERRAIN$ccG$tileNum", gap + 7f*(tileCursX + 3), line(tileCursY))
|
||||
App.fontSmallNumbers.draw(batch, "$ccO$WALL$ccG$wallNum", gap + 7f*(tileCursX + 3), line(tileCursY + 1))
|
||||
// App.fontSmallNumbers.draw(batch, "$ccO$LIQUID$ccG${fluid.type.padEnd(3)}$ccO$BEAKER$ccG${fluid.amount.toIntAndFrac(2)}", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
|
||||
App.fontSmallNumbers.draw(batch, "$ccO$ROCK$ccG$oreNum", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
|
||||
App.fontSmallNumbers.draw(batch, "$ccO$ROCK$ccG$oreNum.$orePlacement", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
|
||||
App.fontSmallNumbers.draw(batch, "$ccO$WIRE$ccG$wireCount ${ccY}X$ccO$mouseTileX ${ccY}Y$ccO$mouseTileY", gap + 7f*(tileCursX + 3), line(tileCursY + 3))
|
||||
App.fontSmallNumbers.draw(batch, "$ccR$rawR $ccG$rawG $ccB$rawB $ccW$rawA", gap + 7f*(tileCursX + 3), line(tileCursY + 4))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user