mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-10 05:41:51 +09:00
dynamic chunk generation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user