DEADSOFTWARE

CaveGame in kotlin
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / world / GameWorldGenerator.kt
1 package ru.deadsoftware.cavedroid.game.world
3 import com.google.common.primitives.Ints.min
4 import ru.deadsoftware.cavedroid.game.GameItemsHolder
5 import ru.deadsoftware.cavedroid.game.model.block.Block
6 import ru.deadsoftware.cavedroid.game.model.world.Biome
7 import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig
8 import kotlin.math.abs
9 import kotlin.math.max
10 import kotlin.random.Random
12 class GameWorldGenerator(
13 private val config: WorldGeneratorConfig,
14 private val gameItemsHolder: GameItemsHolder,
15 ) {
17 private val random = Random(config.seed)
19 private val foreMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
20 private val backMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
22 private val heights by lazy { generateHeights() }
23 private val biomesMap by lazy { generateBiomes() }
25 private val plainsPlants = listOf("dandelion", "rose", "tallgrass")
26 private val mushrooms = listOf("mushroom_brown", "mushroom_red",)
28 private fun generateHeights(): IntArray {
29 val surfaceHeightRange = config.minSurfaceHeight .. config.maxSurfaceHeight
30 val result = IntArray(config.width)
32 result[0] = surfaceHeightRange.random(random)
34 for (x in 1 ..< config.width) {
35 val previous = result[x - 1]
36 var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 }
38 if (previous + d !in surfaceHeightRange) { d = -d }
40 if (result.lastIndex - x < abs(result[0] - previous) * 3) {
41 d = result[0].compareTo(previous).let { if (it != 0) it / abs(it) else 0 }
42 }
44 result[x] = result[x - 1] + d
45 }
47 return result
48 }
50 private fun generateBiomes(): Map<Int, Biome> {
51 val xSequence = sequence {
52 var lastX = 0
53 var count = 0
55 while (lastX < config.width - config.minBiomeSize - 1) {
56 yield(lastX)
58 lastX = random.nextInt(lastX + config.minBiomeSize, config.width)
59 count++
60 }
61 }
63 return xSequence.associateWith { config.biomes.random(random) }
64 }
66 private fun winterBiome(x: Int) {
67 assert(x in 0 ..< config.width) { "x not in range of world width" }
69 val surfaceHeight = heights[x]
71 val grass = gameItemsHolder.getBlock("grass_snowed")
72 val bedrock = gameItemsHolder.getBlock("bedrock")
73 val dirt = gameItemsHolder.getBlock("dirt")
74 val stone = gameItemsHolder.getBlock("stone")
75 val snow = gameItemsHolder.getBlock("snow")
77 foreMap[x][surfaceHeight] = grass
78 foreMap[x][config.height - 1] = bedrock
79 backMap[x][surfaceHeight] = grass
80 backMap[x][config.height - 1] = bedrock
82 if (surfaceHeight - 1 < config.seaLevel) {
83 foreMap[x][surfaceHeight - 1] = snow
84 }
86 for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
87 if (y <= surfaceHeight) {
88 backMap[x][y] = dirt
89 continue
90 }
92 foreMap[x][y] = when {
93 y < surfaceHeight + random.nextInt(5, 8) -> dirt
94 else -> stone
95 }
96 backMap[x][y] = foreMap[x][y]
97 }
99 val plant = random.nextInt(100)
100 if (surfaceHeight < config.seaLevel) {
101 if (plant < 10) {
102 generateSpruce(x)
107 private fun plainsBiome(x: Int) {
108 assert(x in 0 ..< config.width) { "x not in range of world width" }
110 val surfaceHeight = heights[x]
112 val grass = gameItemsHolder.getBlock("grass")
113 val bedrock = gameItemsHolder.getBlock("bedrock")
114 val dirt = gameItemsHolder.getBlock("dirt")
115 val stone = gameItemsHolder.getBlock("stone")
117 foreMap[x][surfaceHeight] = grass
118 foreMap[x][config.height - 1] = bedrock
119 backMap[x][surfaceHeight] = grass
120 backMap[x][config.height - 1] = bedrock
122 for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
123 if (y <= surfaceHeight) {
124 backMap[x][y] = dirt
125 continue
128 foreMap[x][y] = when {
129 y < surfaceHeight + random.nextInt(5, 8) -> dirt
130 else -> stone
132 backMap[x][y] = foreMap[x][y]
135 val plant = random.nextInt(100)
136 if (surfaceHeight < config.seaLevel) {
137 if (plant < 10) {
138 generateOak(x)
139 } else if (plant < 40) {
140 generateTallGrass(x)
145 private fun desertBiome(x: Int) {
146 assert(x in 0 ..< config.width) { "x not in range of world width" }
148 val surfaceHeight = heights[x]
150 val sand = gameItemsHolder.getBlock("sand")
151 val bedrock = gameItemsHolder.getBlock("bedrock")
152 val sandstone = gameItemsHolder.getBlock("sandstone")
153 val stone = gameItemsHolder.getBlock("stone")
156 foreMap[x][surfaceHeight] = sand
157 foreMap[x][config.height - 1] = bedrock
158 backMap[x][surfaceHeight] = sand
159 backMap[x][config.height - 1] = bedrock
161 for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
162 if (y <= surfaceHeight) {
163 backMap[x][y] = sand
164 continue
167 foreMap[x][y] = when {
168 y < surfaceHeight + random.nextInt(5, 8) -> sand
169 y < surfaceHeight + random.nextInt(0, 2) -> sandstone
170 else -> stone
172 backMap[x][y] = foreMap[x][y]
175 val plant = random.nextInt(100)
176 if (surfaceHeight < config.seaLevel) {
177 if (plant < 5) {
178 generateCactus(x)
179 } else if (plant < 10) {
180 generateDeadBush(x)
185 private fun fillWater() {
186 val water = gameItemsHolder.getBlock("water")
188 for (x in 0 ..< config.width) {
189 for (y in config.seaLevel ..< config.height) {
190 if (foreMap[x][y] != gameItemsHolder.fallbackBlock) {
191 break
194 foreMap[x][y] = water
199 private fun generateCactus(x: Int) {
200 val cactus = gameItemsHolder.getBlock("cactus")
201 val cactusHeight = random.nextInt(3)
202 val h = heights[x] - 1
204 for (y in h downTo max(0, h - cactusHeight)) {
205 foreMap[x][y] = cactus
209 private fun generateOak(x: Int) {
210 val log = gameItemsHolder.getBlock("log_oak")
211 val leaves = gameItemsHolder.getBlock("leaves_oak")
212 val h = heights[x] - 1
213 val treeH = random.nextInt(5, 7)
214 val height = max(0, h - treeH)
216 val top = height - 1
217 if (top >= 0) {
218 foreMap[x][top] = leaves
219 backMap[x][top] = leaves
222 for (x1 in max(0, x - 1) .. min(config.width - 1, x + 1)) {
223 for (y in height .. height + treeH - 4) {
224 foreMap[x1][y] = leaves
225 backMap[x1][y] = leaves
227 if (random.nextInt(15) < 3) {
228 foreMap[x1][heights[x1] - 1] = gameItemsHolder.getBlock(mushrooms.random(random))
232 for (y in h downTo height) {
233 backMap[x][y] = log
237 private fun generateSpruce(x: Int) {
238 val log = gameItemsHolder.getBlock("log_spruce")
239 val leaves = gameItemsHolder.getBlock("leaves_spruce")
240 val h = heights[x] - 1
241 val treeH = random.nextInt(7, 9)
242 val height = max(0, h - treeH)
244 val top = height - 1
245 if (top >= 0) {
246 foreMap[x][top] = leaves
247 backMap[x][top] = leaves
250 for (x1 in max(0, x - 1) .. min(config.width - 1, x + 1)) {
251 val y = height
252 foreMap[x1][y] = leaves
253 backMap[x1][y] = leaves
256 for (y in 1..2) {
257 for (x1 in max(0, x - y) .. min(config.width - 1, x + y)) {
258 foreMap[x1][height + 1 + y] = leaves
259 backMap[x1][height + 1 + y] = leaves
263 for (y in h downTo height) {
264 backMap[x][y] = log
268 private fun generateTallGrass(x: Int) {
269 val tallGrass = gameItemsHolder.getBlock(plainsPlants.random(random))
270 val h = heights[x] - 1
271 if (h > 0) {
272 foreMap[x][h] = tallGrass
276 private fun generateDeadBush(x: Int) {
277 val bush = gameItemsHolder.getBlock("deadbush")
278 val h = heights[x] - 1
279 if (h > 0) {
280 foreMap[x][h] = bush
284 private fun generateOres(x : Int) {
285 val stone = gameItemsHolder.getBlock("stone")
286 val coal = gameItemsHolder.getBlock("coal_ore")
287 val iron = gameItemsHolder.getBlock("iron_ore")
288 val gold = gameItemsHolder.getBlock("gold_ore")
289 val diamond = gameItemsHolder.getBlock("diamond_ore")
290 val lapis = gameItemsHolder.getBlock("lapis_ore")
292 for (y in heights[x] ..< config.height) {
293 val res = random.nextInt(10000)
295 val h = config.height - y
296 val block = when {
297 res in 0..<25 && h < 16 -> diamond
298 res in 25 ..< 50 && h < 32 -> gold
299 res in 50 ..< 250 && h < 64 -> iron
300 res in 250 ..< 450 && h < 128 -> coal
301 res in 450 ..< (450 + (25 - (abs(h - 16) * (25 / 16)))) -> lapis
302 else -> null
305 if (block != null && foreMap[x][y] == stone) {
306 foreMap[x][y] = block
311 /**
312 * Generate world
313 */
314 fun generate(): Pair<Array<Array<Block>>, Array<Array<Block>>> {
315 var biome = Biome.PLAINS
317 for (x in 0 until config.width) {
318 biome = biomesMap[x] ?: biome
320 when (biome) {
321 Biome.PLAINS -> plainsBiome(x)
322 Biome.DESERT -> desertBiome(x)
323 Biome.WINTER -> winterBiome(x)
326 generateOres(x)
329 fillWater()
331 return Pair(foreMap, backMap)