From c8329b36c502a054129e48079c63a80d37c2a3cf Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 18 Jan 2024 22:41:30 +0900 Subject: [PATCH] dynamic chunk generation --- .../terrarum/modulebasegame/TerrarumIngame.kt | 27 +++++++++ .../serialise/QuickSaveThread.kt | 19 +++--- .../serialise/WorldSavingThread.kt | 17 ++++-- .../modulebasegame/serialise/WriteSavegame.kt | 18 +++--- .../modulebasegame/worldgenerator/Biomegen.kt | 9 +-- .../modulebasegame/worldgenerator/Cavegen.kt | 10 ++-- .../modulebasegame/worldgenerator/Oregen.kt | 10 ++-- .../worldgenerator/OregenAutotiling.kt | 4 +- .../modulebasegame/worldgenerator/Terragen.kt | 16 ++--- .../modulebasegame/worldgenerator/Treegen.kt | 52 +++++++++------- .../modulebasegame/worldgenerator/Worldgen.kt | 59 +++++++++++++++---- .../terrarum/ui/BasicDebugInfoWindow.kt | 22 +++---- 12 files changed, 176 insertions(+), 87 deletions(-) diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index ae87692ee..674841b51 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt index 6ae019aa7..623eecba2 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt index 2e251abba..456451b42 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt @@ -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 } diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt index 7849f5cda..6fefc07f2 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt @@ -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) } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt index 114cae6af..467c7959f 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt @@ -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() diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt index 8e5e03b89..91c474230 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt @@ -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) diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt index c8d465995..584739ed6 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt @@ -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) : 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) diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt index ce70ce68f..5b025b95e 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt @@ -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) : Gen(world, isFinal, seed) { - override fun getDone(loadscreen: LoadScreenBase) { + override fun getDone(loadscreen: LoadScreenBase?) { Worldgen.threadExecutor.renew() submitJob(loadscreen) Worldgen.threadExecutor.join() diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt index 6c26ab409..53c992a9b 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt @@ -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) } } - } + }*/ } } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index 18a73dfba..aaa74eac7 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -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) : 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, 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.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>, rng: HQRNG) { + private fun tryToPlant(xs: IntProgression, ys: Int, grassMap: Array>, 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 } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt index 48a4da417..317387677 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -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) fun getEstimationSec(width: Int, height: Int): Long { @@ -122,6 +143,7 @@ object Worldgen { val yInit = getChunkGenStrip(world).first val tallies = ArrayList>() // 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 protected abstract fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) - protected open fun getChunksRange(): List { + private fun getChunksRange(): List { 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( diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index cacb53876..7b0536195 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -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) {} }