diff --git a/assets/mods/basegame/blocks/blocks.csv b/assets/mods/basegame/blocks/blocks.csv index 1e9fcd88d..7b2f45c0f 100644 --- a/assets/mods/basegame/blocks/blocks.csv +++ b/assets/mods/basegame/blocks/blocks.csv @@ -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" diff --git a/assets/mods/basegame/items/itemid.csv b/assets/mods/basegame/items/itemid.csv index ab878a8b1..6b8406515 100644 --- a/assets/mods/basegame/items/itemid.csv +++ b/assets/mods/basegame/items/itemid.csv @@ -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 diff --git a/assets/mods/basegame/ores/1.tga b/assets/mods/basegame/ores/1.tga index 58cf4f382..91f4c3a7d 100644 --- a/assets/mods/basegame/ores/1.tga +++ b/assets/mods/basegame/ores/1.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09330d417565a116f9fd63582dd8e8382d50d1638ca97f06ab84478591ec7446 +oid sha256:fd78bf34024f8c81e13da781cb00708f3585b86e95236dc2043cafa9c362493a size 16402 diff --git a/assets/mods/basegame/ores/2.tga b/assets/mods/basegame/ores/2.tga new file mode 100644 index 000000000..492ef90e7 --- /dev/null +++ b/assets/mods/basegame/ores/2.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ad5a6ea6eb433d83ad24a87d398845000d5e07425dcb94dd42bf324fd67538c +size 16402 diff --git a/assets/mods/basegame/ores/ores.csv b/assets/mods/basegame/ores/ores.csv index e3b7e16bb..11fb133aa 100644 --- a/assets/mods/basegame/ores/ores.csv +++ b/assets/mods/basegame/ores/ores.csv @@ -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" diff --git a/assets/mods/basegame/ores/worldgen.csv b/assets/mods/basegame/ores/worldgen.csv new file mode 100644 index 000000000..56e1a2e01 --- /dev/null +++ b/assets/mods/basegame/ores/worldgen.csv @@ -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" diff --git a/src/net/torvald/terrarum/ModMgr.kt b/src/net/torvald/terrarum/ModMgr.kt index fdfbc498c..8244d3827 100644 --- a/src/net/torvald/terrarum/ModMgr.kt +++ b/src/net/torvald/terrarum/ModMgr.kt @@ -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() + } } } diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index fa3fd7e2b..b72dbdb82 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -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) diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index 7068614cb..6caa310e3 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -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) } diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/DroppedItem.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/DroppedItem.kt index 787c86c44..15b3df659 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/DroppedItem.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/DroppedItem.kt @@ -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() diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt index e603ef82e..e898c17d3 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt @@ -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) +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt index 3b0f3434b..67258b82f 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt @@ -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) : 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 { - TODO() + private fun getGenerator(seed: Long): List { + 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, 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 ) \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt new file mode 100644 index 000000000..8120dd4c1 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt @@ -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 { + 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 { + return getNearbyTilesPos8(x, y).map { world.getTileFromOre(it.x, it.y) } + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt index 91ca5c5a6..b4624dfba 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt @@ -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 { - 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 diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt index 51916579e..beabfdee9 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -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() + 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 diff --git a/src/net/torvald/terrarum/tests/WorldgenNoiseSandbox.kt b/src/net/torvald/terrarum/tests/WorldgenNoiseSandbox.kt index c6c01ca36..cf0c2a7ae 100644 --- a/src/net/torvald/terrarum/tests/WorldgenNoiseSandbox.kt +++ b/src/net/torvald/terrarum/tests/WorldgenNoiseSandbox.kt @@ -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, 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 { - 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 diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index daa7ea8ef..9da91ab0d 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -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))