DEADSOFTWARE

Store block references intead of ids
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / world / GameWorldGenerator.kt
1 package ru.deadsoftware.cavedroid.game.world
3 import ru.deadsoftware.cavedroid.game.GameItemsHolder
4 import ru.deadsoftware.cavedroid.game.model.block.Block
5 import ru.deadsoftware.cavedroid.game.model.world.Biome
6 import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig
7 import kotlin.math.abs
8 import kotlin.math.max
9 import kotlin.random.Random
11 class GameWorldGenerator(
12 private val config: WorldGeneratorConfig,
13 private val gameItemsHolder: GameItemsHolder,
14 ) {
16 private val random = Random(config.seed)
18 private val foreMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
19 private val backMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
21 private val heights by lazy { generateHeights() }
22 private val biomesMap by lazy { generateBiomes() }
24 private fun generateHeights(): IntArray {
25 val surfaceHeightRange = config.minSurfaceHeight .. config.maxSurfaceHeight
26 val result = IntArray(config.width)
28 result[0] = (config.minSurfaceHeight + config.maxSurfaceHeight) / 2
30 for (x in 1 ..< config.width) {
31 val previous = result[x - 1]
32 var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 }
34 if (previous + d !in surfaceHeightRange) { d = -d }
36 if (result.lastIndex - x < abs(result[0] - previous) * 3) {
37 d = result[0].compareTo(previous).let { if (it != 0) it / abs(it) else 0 }
38 }
40 result[x] = result[x - 1] + d
41 }
43 return result
44 }
46 private fun generateBiomes(): Map<Int, Biome> {
47 val xSequence = sequence {
48 var lastX = 0
49 var count = 0
51 while (lastX < config.width - config.minBiomeSize - 1) {
52 yield(lastX)
54 lastX = random.nextInt(lastX + config.minBiomeSize, config.width)
55 count++
56 }
57 }
59 return xSequence.associateWith { config.biomes.random(random) }
60 }
62 private fun plainsBiome(x: Int) {
63 assert(x in 0 ..< config.width) { "x not in range of world width" }
65 val surfaceHeight = heights[x]
67 val grass = gameItemsHolder.getBlock("grass")
68 val bedrock = gameItemsHolder.getBlock("bedrock")
69 val dirt = gameItemsHolder.getBlock("dirt")
70 val stone = gameItemsHolder.getBlock("stone")
72 foreMap[x][surfaceHeight] = grass
73 foreMap[x][config.height - 1] = bedrock
74 backMap[x][surfaceHeight] = grass
75 backMap[x][config.height - 1] = bedrock
77 for (y in surfaceHeight + 1 ..< config.height - 1) {
78 foreMap[x][y] = when {
79 y < surfaceHeight + random.nextInt(5, 8) -> dirt
80 else -> stone
81 }
82 backMap[x][y] = foreMap[x][y]
83 }
84 }
86 private fun desertBiome(x: Int) {
87 assert(x in 0 ..< config.width) { "x not in range of world width" }
89 val surfaceHeight = heights[x]
91 val sand = gameItemsHolder.getBlock("sand")
92 val bedrock = gameItemsHolder.getBlock("bedrock")
93 val sandstone = gameItemsHolder.getBlock("sandstone")
94 val stone = gameItemsHolder.getBlock("stone")
97 foreMap[x][surfaceHeight] = sand
98 foreMap[x][config.height - 1] = bedrock
99 backMap[x][surfaceHeight] = sand
100 backMap[x][config.height - 1] = bedrock
102 for (y in surfaceHeight + 1 ..< config.height - 1) {
103 foreMap[x][y] = when {
104 y < surfaceHeight + random.nextInt(5, 8) -> sand
105 y < surfaceHeight + random.nextInt(0, 2) -> sandstone
106 else -> stone
108 backMap[x][y] = foreMap[x][y]
111 if (surfaceHeight < config.seaLevel && random.nextInt(100) < 5) {
112 generateCactus(x)
116 private fun fillWater() {
117 val water = gameItemsHolder.getBlock("water")
119 for (x in 0 ..< config.width) {
120 for (y in config.seaLevel ..< config.height) {
121 if (foreMap[x][y] != gameItemsHolder.fallbackBlock) {
122 break
125 foreMap[x][y] = water
130 private fun generateCactus(x: Int) {
131 val cactus = gameItemsHolder.getBlock("cactus")
132 val cactusHeight = random.nextInt(3)
133 val h = heights[x] - 1
135 for (y in h downTo max(0, h - cactusHeight)) {
136 foreMap[x][y] = cactus
140 /**
141 * Generate world
142 */
143 fun generate(): Pair<Array<Array<Block>>, Array<Array<Block>>> {
144 var biome = Biome.PLAINS
146 for (x in 0 until config.width) {
147 biome = biomesMap[x] ?: biome
149 when (biome) {
150 Biome.PLAINS -> plainsBiome(x)
151 Biome.DESERT -> desertBiome(x)
155 fillWater()
157 return Pair(foreMap, backMap)