dynamic chunk generation

This commit is contained in:
minjaesong
2024-01-18 22:41:30 +09:00
parent b2ea61aa4d
commit c8329b36c5
12 changed files with 176 additions and 87 deletions

View File

@@ -28,6 +28,7 @@ import net.torvald.terrarum.gameitems.mouseInInteractableRange
import net.torvald.terrarum.gameparticles.ParticleBase
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldSimulator
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver
@@ -43,6 +44,8 @@ import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
import net.torvald.terrarum.savegame.VDUtil
import net.torvald.terrarum.savegame.VirtualDisk
import net.torvald.terrarum.serialise.Common
@@ -62,6 +65,7 @@ import org.khelekore.prtree.PRTree
import java.io.File
import java.util.*
import java.util.logging.Level
import kotlin.experimental.and
import kotlin.math.absoluteValue
import kotlin.math.min
import kotlin.math.pow
@@ -363,6 +367,9 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
// Terrarum.itemCodex.loadFromSave(codices.item)
// Terrarum.apocryphas = HashMap(codices.apocryphas)
// feed info to the worldgen
Worldgen.attachMap(world, WorldgenParams(world.generatorSeed))
}
}
@@ -898,6 +905,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
App.audioMixer.convolveBusCave.volume = 0.0
}
actorNowPlaying?.let { if (WORLD_UPDATE_TIMER % 4 == 1) updateWorldGenerator(actorNowPlaying!!) }
WORLD_UPDATE_TIMER += 1
@@ -1062,6 +1071,24 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
}
private fun Point2iMod(x: Int, y: Int) = Point2i(x fmod world.width, y)
private fun updateWorldGenerator(actor: ActorWithBody) {
val pcx = (actor.intTilewiseHitbox.canonicalX.toInt() fmod world.width) / CHUNK_W
val pcy = (actor.intTilewiseHitbox.canonicalY.toInt() fmod world.width) / CHUNK_H
listOf(
Point2iMod(pcx - 1, pcy - 2), Point2iMod(pcx, pcy - 2), Point2iMod(pcx + 1, pcy - 2),
Point2iMod(pcx - 2, pcy - 1), Point2iMod(pcx - 1, pcy - 1), Point2iMod(pcx, pcy - 1), Point2iMod(pcx + 1, pcy - 1), Point2iMod(pcx + 2, pcy - 1),
Point2iMod(pcx - 2, pcy), Point2iMod(pcx - 1, pcy), Point2iMod(pcx + 1, pcy), Point2iMod(pcx + 2, pcy),
Point2iMod(pcx - 2, pcy + 1), Point2iMod(pcx - 1, pcy + 1), Point2iMod(pcx, pcy + 1), Point2iMod(pcx + 1, pcy + 1), Point2iMod(pcx + 2, pcy + 1),
Point2iMod(pcx - 1, pcy + 2), Point2iMod(pcx, pcy + 2), Point2iMod(pcx + 1, pcy + 2),
).filter { it.y in 0 until world.height }.filter { (cx, cy) ->
world.chunkFlags[cy][cx].and(0x7F) == 0.toByte()
}.forEach { (cx, cy) ->
Worldgen.generateChunkIngame(cx, cy) { cx, cy -> }
}
}
private var worldTransitionOngoing = false
private var worldTransitionPauseRequested = -1
private var saveRequested2 = false

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame.serialise
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
@@ -15,6 +16,7 @@ import net.torvald.terrarum.toInt
import net.torvald.terrarum.utils.PlayerLastStatus
import java.io.File
import java.util.zip.GZIPOutputStream
import kotlin.experimental.and
/**
* Created by minjaesong on 2021-09-29.
@@ -111,17 +113,20 @@ class QuickSingleplayerWorldSavingThread(
printdbg(this, "Writing chunks... $chunksWrote/$chunkCount (chunk# $chunkNumber at layer# $layerNum)")
val chunkXY = LandUtil.chunkNumToChunkXY(ingame.world, chunkNumber)
val (cx, cy) = LandUtil.chunkNumToChunkXY(ingame.world, chunkNumber)
val chunkFlag = ingame.world.chunkFlags[cy][cx]
// println("Chunk xy from number $chunkNumber -> (${chunkXY.x}, ${chunkXY.y})")
val chunkBytes = WriteWorld.encodeChunk(layer, chunkXY.x, chunkXY.y)
val entryID = 0x1_0000_0000L or layerNum.toLong().shl(24) or chunkNumber.toLong()
if (chunkFlag and 0x7F == GameWorld.CHUNK_LOADED) {
val chunkBytes = WriteWorld.encodeChunk(layer, cx, cy)
val entryID = 0x1_0000_0000L or layerNum.toLong().shl(24) or chunkNumber.toLong()
val entryContent = EntryFile(chunkBytes)
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
// "W1L0-92,15"
addFile(disk, entry); skimmer.appendEntry(entry)
val entryContent = EntryFile(chunkBytes)
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
// "W1L0-92,15"
addFile(disk, entry); skimmer.appendEntry(entry)
}
WriteSavegame.saveProgress += chunkProgressMultiplier
chunksWrote += 1

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.modulebasegame.serialise
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.*
import net.torvald.terrarum.gameworld.GameWorld.Companion.CHUNK_LOADED
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
@@ -12,6 +13,7 @@ import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import java.io.File
import java.util.zip.GZIPOutputStream
import kotlin.experimental.and
/**
* Created by minjaesong on 2021-09-14.
@@ -129,17 +131,20 @@ class WorldSavingThread(
for (layer in layers.indices) {
for (cx in 0 until cw) {
for (cy in 0 until ch) {
val chunkFlag = ingame.world.chunkFlags[cy][cx]
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
if (chunkFlag and 0x7F == CHUNK_LOADED) {
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
val entryContent = EntryFile(chunkBytes)
val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent)
// "W1L0-92,15"
addFile(disk, entry)
val entryContent = EntryFile(chunkBytes)
val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent)
// "W1L0-92,15"
addFile(disk, entry)
}
WriteSavegame.saveProgress += 1
}

View File

@@ -193,19 +193,21 @@ object LoadSavegame {
val cw = LandUtil.CHUNK_W
val ch = LandUtil.CHUNK_H
val chunkCount = world.width * world.height / (cw * ch)
val hasOreLayer = (newIngame.worldDisk.getFile(0x1_0000_0000L or 2L.shl(24)) != null)
val worldLayer = (if (hasOreLayer) intArrayOf(0,1,2) else intArrayOf(0,1)).map { world.getLayer(it) }
val layerCount = worldLayer.size
for (chunk in 0L until (world.width * world.height) / (cw * ch)) {
val worldLayer = intArrayOf(0,1,2).map { world.getLayer(it) }
for (chunk in 0L until chunkCount) {
for (layer in worldLayer.indices) {
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
val chunkFile = newIngame.worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)!!
val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk.toInt())
newIngame.worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)?.let { chunkFile ->
val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk.toInt())
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy)
if (layer == 2) {
printdbg(this, "Loading ore layer chunk ($cx, $cy) size: ${chunkFile.getSizePure()}")
}
world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or CHUNK_LOADED
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy)
world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or CHUNK_LOADED
}
}
loadscreen.progress.getAndAdd(1)
}

View File

@@ -5,7 +5,6 @@ import com.sudoplay.joise.module.*
import net.torvald.terrarum.App
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
@@ -28,9 +27,11 @@ class Biomegen(world: GameWorld, isFinal: Boolean, seed: Long, params: Any, val
private lateinit var THISWORLD_SANDSTONE: ItemID
override fun getDone(loadscreen: LoadScreenBase) {
loadscreen.stageValue += 1
loadscreen.progress.set(0L)
override fun getDone(loadscreen: LoadScreenBase?) {
loadscreen?.let {
it.stageValue += 1
it.progress.set(0L)
}
val SAND_RND = (seed shake "SANDYCOLOURS").ushr(7).xor(seed and 255L).and(255L).toInt()

View File

@@ -2,11 +2,9 @@ package net.torvald.terrarum.modulebasegame.worldgenerator
import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.*
import net.torvald.random.XXHash32
import net.torvald.terrarum.App
import net.torvald.terrarum.LoadScreenBase
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
@@ -23,9 +21,11 @@ class Cavegen(world: GameWorld, isFinal: Boolean, val highlandLowlandSelectCache
const val YHEIGHT_DIVISOR = 2.0 / 7.0
}
override fun getDone(loadscreen: LoadScreenBase) {
loadscreen.stageValue += 1
loadscreen.progress.set(0L)
override fun getDone(loadscreen: LoadScreenBase?) {
loadscreen?.let {
it.stageValue += 1
it.progress.set(0L)
}
Worldgen.threadExecutor.renew()
submitJob(loadscreen)

View File

@@ -3,11 +3,9 @@ package net.torvald.terrarum.modulebasegame.worldgenerator
import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.*
import net.torvald.terrarum.*
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_MAGIC
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
import kotlin.math.cos
@@ -18,9 +16,11 @@ import kotlin.math.sqrt
* Created by minjaesong on 2023-10-25.
*/
class Oregen(world: GameWorld, isFinal: Boolean, private val caveAttenuateBiasScaledCache: ModuleCache, seed: Long, private val ores: List<OregenParams>) : Gen(world, isFinal, seed) {
override fun getDone(loadscreen: LoadScreenBase) {
loadscreen.stageValue += 1
loadscreen.progress.set(0L)
override fun getDone(loadscreen: LoadScreenBase?) {
loadscreen?.let {
it.stageValue += 1
it.progress.set(0L)
}
Worldgen.threadExecutor.renew()
submitJob(loadscreen)

View File

@@ -4,12 +4,10 @@ import com.sudoplay.joise.Joise
import net.torvald.random.XXHash64
import net.torvald.terrarum.LoadScreenBase
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameitems.isOre
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
@@ -24,7 +22,7 @@ import kotlin.math.max
*/
class OregenAutotiling(world: GameWorld, isFinal: Boolean, seed: Long, val tilingModes: HashMap<ItemID, String>) : Gen(world, isFinal, seed) {
override fun getDone(loadscreen: LoadScreenBase) {
override fun getDone(loadscreen: LoadScreenBase?) {
Worldgen.threadExecutor.renew()
submitJob(loadscreen)
Worldgen.threadExecutor.join()

View File

@@ -29,9 +29,11 @@ class Terragen(world: GameWorld, isFinal: Boolean , val highlandLowlandSelectCac
private val dirtStoneDitherSize = 3 // actual dither size will be double of this value
private val stoneSlateDitherSize = 4
override fun getDone(loadscreen: LoadScreenBase) {
loadscreen.stageValue += 1
loadscreen.progress.set(0L)
override fun getDone(loadscreen: LoadScreenBase?) {
loadscreen?.let {
it.stageValue += 1
it.progress.set(0L)
}
Worldgen.threadExecutor.renew()
submitJob(loadscreen)
@@ -98,7 +100,7 @@ class Terragen(world: GameWorld, isFinal: Boolean , val highlandLowlandSelectCac
%
* - where the stone layer actually begins
*/
if (dirtStoneTransition > 0) {
/*if (dirtStoneTransition > 0) {
for (pos in 0 until dirtStoneDitherSize * 2) {
val y = pos + dirtStoneTransition - (dirtStoneDitherSize * 2) + 1
if (y >= world.height) break
@@ -116,14 +118,14 @@ class Terragen(world: GameWorld, isFinal: Boolean , val highlandLowlandSelectCac
world.setTileWall(x, y, newTile, true)
}
}
}
}*/
/*
#
# - stone-to-slate transition, height = stoneSlateDitherSize
#
*/
if (stoneSlateTransition > 0) {
/*if (stoneSlateTransition > 0) {
for (pos in 0 until stoneSlateDitherSize) {
val y = pos + stoneSlateTransition - stoneSlateDitherSize + 1
if (y >= world.height) break
@@ -138,7 +140,7 @@ class Terragen(world: GameWorld, isFinal: Boolean , val highlandLowlandSelectCac
world.setTileWall(x, y, newTile, true)
}
}
}
}*/
}
}

View File

@@ -2,32 +2,28 @@ package net.torvald.terrarum.modulebasegame.worldgenerator
import com.sudoplay.joise.Joise
import net.torvald.random.HQRNG
import net.torvald.random.XXHash32
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
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.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_PLAINS
import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_SPARSE_WOODS
import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_WOODLANDS
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
import net.torvald.terrarum.serialise.toUint
import kotlin.math.absoluteValue
/**
* Created by minjaesong on 2023-11-10.
*/
class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams: TerragenParams, params: TreegenParams, val biomeMap: HashMap<BlockAddress, Byte>) : Gen(world, isFinal, seed, params) {
override fun getDone(loadscreen: LoadScreenBase) {
loadscreen.stageValue += 1
loadscreen.progress.set(0L)
override fun getDone(loadscreen: LoadScreenBase?) {
loadscreen?.let {
it.stageValue += 1
it.progress.set(0L)
}
Worldgen.threadExecutor.renew()
submitJob(loadscreen)
@@ -40,7 +36,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams
override fun draw(xStart: Int, yStart: Int, noises: List<Joise>, soff: Double) {
for (i in 0 until 10) {
val xs = (xStart + 9*i) until (xStart + 9*i) + 9
tryToPlant(xs, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong()))
tryToPlant(xs, 986578287, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong()))
}
}
@@ -74,9 +70,19 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams
return r
}
private val treePlot1 = arrayOf(2, 3)
private val treePlot2 = arrayOf(6, 7)
private val treePlotM = arrayOf(4, 5)
private val treePlot1 = intArrayOf(2, 3)
private val treePlot2 = intArrayOf(6, 7)
private val treePlotM = intArrayOf(4, 5)
private fun IntArray.takeRand(x: Int, y: Int, h: Int): Int {
val r = ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216f
return this[(r * this.size).toInt()]
}
private fun List<Int>.takeRand(x: Int, y: Int, h: Int): Int {
val r = ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216f
return this[(r * this.size).toInt()]
}
private fun Double.toDitherredInt(rng: HQRNG): Int {
val ibase = this.floorToInt()
@@ -84,7 +90,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams
return if (rng.nextDouble() < 1.0 - thre) ibase else ibase + 1
}
private fun tryToPlant(xs: IntProgression, grassMap: Array<List<Int>>, rng: HQRNG) {
private fun tryToPlant(xs: IntProgression, ys: Int, grassMap: Array<List<Int>>, rng: HQRNG) {
val treeSpecies = 0
@@ -130,28 +136,28 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams
when (treeToSpawn.size) {
2 -> {
val plot1 = if (treeToSpawn[0] < 3) treePlot1.random() else treePlot1[0]
val plot2 = if (treeToSpawn[1] < 3) treePlot2.random() else treePlot2[0]
val plot1 = if (treeToSpawn[0] < 3) treePlot1.takeRand(xs.first, ys, 123) else treePlot1[0]
val plot2 = if (treeToSpawn[1] < 3) treePlot2.takeRand(xs.first, ys, 456) else treePlot2[0]
// if there is no grass, grassMap[x] is an empty list
if (treeToSpawn[0] != 0) {
grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let {
grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 1234) }?.let {
plantTree(xs.first + plot1, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn
}
}
if (treeToSpawn[1] != 0) {
grassMap[plot2].let { if (it.isEmpty()) null else it.random() }?.let {
grassMap[plot2].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot2, ys, 2345) }?.let {
plantTree(xs.first + plot2, it, treeSpecies, 1, rng) // TODO use treeSize from the treeToSpawn
}
}
}
1 -> {
val plot1 = if (treeToSpawn[0] < 3) treePlotM.random() else treePlotM[0]
val plot1 = if (treeToSpawn[0] < 3) treePlotM.takeRand(xs.first, ys, 3456) else treePlotM[0]
// if there is no grass, grassMap[x] is an empty list
if (treeToSpawn[0] != 0) {
val treeSize = arrayOf(null, 0, 1, 2)[treeToSpawn[0]]
grassMap[plot1].let { if (it.isEmpty()) null else it.random() }?.let {
grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 4567) }?.let {
plantTree(xs.first + plot1, it, treeSpecies, treeSize!!, rng)
}
}
@@ -367,7 +373,9 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams
val xEnd = xStart + width
var xStart2 = xStart
var xEnd2 = xEnd
Math.random().let {
val r = (XXHash32.hashGeoCoord(x, y) * width * height + growCnt).and(0xffffff) / 16777216f
r.let {
if (it < 0.25) xStart2 += 1
else if (it < 0.5) xEnd2 -= 1
}

View File

@@ -34,6 +34,10 @@ object Worldgen {
fun attachMap(world: GameWorld, genParams: WorldgenParams) {
this.world = world
params = genParams
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
caveAttenuateBiasScaledCache = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
biomeMap = HashMap()
}
internal lateinit var highlandLowlandSelectCache: ModuleCache
@@ -74,11 +78,6 @@ object Worldgen {
}
fun generateMap(loadscreen: LoadScreenBase) {
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
caveAttenuateBiasScaledCache = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
biomeMap = HashMap()
val jobs = getJobs()
@@ -101,6 +100,28 @@ object Worldgen {
}
/**
* Chunk flags will be set automatically
*/
fun generateChunkIngame(cx: Int, cy: Int, callback: (Int, Int) -> Unit) {
val jobs = getJobs()
printdbg(this, "Generating chunk on ($cx, $cy)")
Thread {
world.chunkFlags[cy][cx] = GameWorld.CHUNK_GENERATING
for (i in jobs.indices) {
val it = jobs[i]
it.theWork.getChunkDone(cx, cy)
}
world.chunkFlags[cy][cx] = GameWorld.CHUNK_LOADED
callback(cx, cy)
}.let {
it.priority = 2
it.start()
}
}
data class Work(val loadingScreenName: String, val theWork: Gen, val tags: List<String>)
fun getEstimationSec(width: Int, height: Int): Long {
@@ -122,6 +143,7 @@ object Worldgen {
val yInit = getChunkGenStrip(world).first
val tallies = ArrayList<Pair<Point2i, Vector2f>>() // xypos, score (0..1+)
var tries = 0
var found = false
while (tries < 99) {
val posX = (Math.random() * world.width).toInt()
var posY = yInit * CHUNK_H
@@ -166,12 +188,23 @@ object Worldgen {
printdbg(this, "...Survey says: $rocks/$rockScoreMin rocks, $trees/$treeScoreMin trees")
if (score.x >= 1f && score.y >= 1f) break
if (score.x >= 1f && score.y >= 1f) {
found = true
break
}
tries += 1
}
if (found)
return tallies.last().first
return tallies.toTypedArray().also {
it.map { it.second.let {
it.x = (it.x).coerceAtMost(1f)
it.y = (it.y).coerceAtMost(1f)
} }
it.shuffle()
it.sortByDescending { it.second.lengthSquared() }
@@ -342,30 +375,36 @@ object Worldgen {
abstract class Gen(val world: GameWorld, val isFinal: Boolean, val seed: Long, val params: Any? = null) {
open fun getDone(loadscreen: LoadScreenBase) { } // trying to use different name so that it won't be confused with Runnable or Callable
open fun getDone(loadscreen: LoadScreenBase?) { } // trying to use different name so that it won't be confused with Runnable or Callable
protected abstract fun getGenerator(seed: Long, params: Any?): List<Joise>
protected abstract fun draw(xStart: Int, yStart: Int, noises: List<Joise>, soff: Double)
protected open fun getChunksRange(): List<Int> {
private fun getChunksRange(): List<Int> {
val (yStart, yEnd) = Worldgen.getChunkGenStrip(world)
return (0 until world.width / CHUNK_W).flatMap { cx ->
(LandUtil.chunkXYtoChunkNum(world, cx, yStart)..LandUtil.chunkXYtoChunkNum(world, cx, yEnd)).toList()
}
}
open fun submitJob(loadscreen: LoadScreenBase) {
fun submitJob(loadscreen: LoadScreenBase?) {
getChunksRange().forEach { chunkNum ->
val (chunkX, chunkY) = LandUtil.chunkNumToChunkXY(world, chunkNum)
Worldgen.threadExecutor.submit {
val localJoise = getGenerator(seed, params)
val sampleOffset = world.width / 8.0
draw(chunkX * LandUtil.CHUNK_W, chunkY * CHUNK_H, localJoise, sampleOffset)
loadscreen.progress.addAndGet(1L)
loadscreen?.progress?.addAndGet(1L)
world.chunkFlags[chunkY][chunkX] = if (isFinal) GameWorld.CHUNK_LOADED else GameWorld.CHUNK_GENERATING
}
}
}
fun getChunkDone(chunkX: Int, chunkY: Int) {
val localJoise = getGenerator(seed, params)
val sampleOffset = world.width / 8.0
draw(chunkX * LandUtil.CHUNK_W, chunkY * CHUNK_H, localJoise, sampleOffset)
}
}
data class WorldgenParams(

View File

@@ -162,20 +162,22 @@ class BasicDebugInfoWindow : UICanvas() {
val xo = 224
val yo = 78
try {
world?.let { world ->
val ppos = ingame?.actorNowPlaying?.centrePosVector
val pcx = (ppos?.x?.div(TILE_SIZED)?.fmod(world.width.toDouble())?.div(CHUNK_W)?.toInt() ?: -999)
val pcy = (ppos?.y?.div(TILE_SIZED)?.fmod(world.height.toDouble())?.div(CHUNK_H)?.toInt() ?: -999)
world?.let { world ->
val ppos = ingame?.actorNowPlaying?.centrePosVector
val pcx = (ppos?.x?.div(TILE_SIZED)?.fmod(world.width.toDouble())?.div(CHUNK_W)?.toInt() ?: -999)
val pcy = (ppos?.y?.div(TILE_SIZED)?.fmod(world.height.toDouble())?.div(CHUNK_H)?.toInt() ?: -999)
for (y in 0 until world.height / CHUNK_H) {
for (x in 0 until world.width / CHUNK_W) {
val chunkStat = world.chunkFlags[y][x].toUint()
batch.color = if (pcx == x && pcy == y) chunkStatCurrentChunk else chunkStatColours[chunkStat]
Toolkit.fillArea(batch, xo + 3*x, yo + 3*y, 2, 2)
for (y in 0 until world.height / CHUNK_H) {
for (x in 0 until world.width / CHUNK_W) {
val chunkStat = world.chunkFlags[y][x].toUint()
batch.color = if (pcx == x && pcy == y) chunkStatCurrentChunk else chunkStatColours[chunkStat]
Toolkit.fillArea(batch, xo + 3 * x, yo + 3 * y, 2, 2)
}
}
}
}
catch (_: UninitializedPropertyAccessException) {}
}