DEADSOFTWARE

bf37f454c3ad36851a40062e5d74b2ffad7cdfde
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / world / GameWorldGenerator.kt
1 package ru.deadsoftware.cavedroid.game.world
3 import com.badlogic.gdx.utils.TimeUtils
4 import ru.deadsoftware.cavedroid.game.GameItems
5 import kotlin.math.abs
6 import kotlin.random.Random
8 object GameWorldGenerator {
10 private const val BIOME_MIN_SIZE = 64
12 private enum class Biome {
13 PLAINS,
14 DESERT
15 }
17 private fun generateHeights(width: Int, min: Int, max: Int, random: Random) = IntArray(width).apply {
18 set(0, (min + max) / 2)
19 for (x in 1 until width) {
20 val previous = get(x - 1)
21 var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 }
23 if (previous + d !in min..max) { d = -d }
24 if (lastIndex - x < abs(get(0) - previous) * 3) {
25 d = get(0).compareTo(previous).let { if (it != 0) it / abs(it) else 0 }
26 }
28 set(x, get(x - 1) + d)
29 }
30 }
32 private fun generateBiomes(width: Int, random: Random) = buildMap<Int, Biome> {
33 val xSequence = sequence {
34 var lastX = 0
35 var count = 0
37 while (lastX < width - BIOME_MIN_SIZE - 1) {
38 yield(lastX)
40 lastX = random.nextInt(lastX + BIOME_MIN_SIZE, width)
41 count++
42 }
43 }
45 return xSequence.associateWith { Biome.values()[random.nextInt(Biome.values().size)] }
46 }
48 private fun plainsBiome(
49 foreMap: Array<IntArray>,
50 backMap: Array<IntArray>,
51 width: Int,
52 height: Int,
53 x: Int,
54 xHeight: Int,
55 random: Random,
56 ) {
57 foreMap[x][xHeight] = GameItems.getBlockId("grass")
58 foreMap[x][height - 1] = GameItems.getBlockId("bedrock")
59 backMap[x][xHeight] = GameItems.getBlockId("grass")
60 backMap[x][height - 1] = GameItems.getBlockId("bedrock")
62 for (y in xHeight + 1 until height - 1) {
63 foreMap[x][y] = when {
64 y < xHeight + random.nextInt(5, 8) -> GameItems.getBlockId("dirt")
65 else -> GameItems.getBlockId("stone")
66 }
67 backMap[x][y] = foreMap[x][y]
68 }
69 }
71 private fun desertBiome(
72 foreMap: Array<IntArray>,
73 backMap: Array<IntArray>,
74 width: Int,
75 height: Int,
76 x: Int,
77 xHeight: Int,
78 random: Random,
79 ) {
80 foreMap[x][xHeight] = GameItems.getBlockId("sand")
81 foreMap[x][height - 1] = GameItems.getBlockId("bedrock")
82 backMap[x][xHeight] = GameItems.getBlockId("sand")
83 backMap[x][height - 1] = GameItems.getBlockId("bedrock")
85 for (y in xHeight + 1 until height - 1) {
86 foreMap[x][y] = when {
87 y < xHeight + random.nextInt(5, 8) -> GameItems.getBlockId("sand")
88 else -> GameItems.getBlockId("stone")
89 }
90 backMap[x][y] = foreMap[x][y]
91 }
92 }
94 private fun fillWater(foreMap: Array<IntArray>, width: Int, height: Int, waterLevel: Int) {
95 for (x in 0 until width) {
96 for (y in waterLevel until height) {
97 if (foreMap[x][y] != 0) {
98 break
99 }
101 foreMap[x][y] = GameItems.getBlockId("water")
106 /**
107 * Generates world of given width and height with given seed
108 * @param width world width
109 * @param height world height
110 * @param seed seed for random number generator
111 * @return pair of foreground and background layers
112 */
113 fun generate(width: Int, height: Int, seed: Long = TimeUtils.millis()): Pair<Array<IntArray>, Array<IntArray>> {
114 val random = Random(seed)
115 val foreMap = Array(width) { IntArray(height) }
116 val backMap = Array(width) { IntArray(width) }
117 val heightsMap = generateHeights(width, height / 4, height * 3 / 4, random)
118 val biomesMap = generateBiomes(width, random)
120 var biome = Biome.PLAINS
122 for (x in 0 until width) {
123 val xHeight = heightsMap[x]
124 biome = biomesMap[x] ?: biome
126 when (biome) {
127 Biome.PLAINS -> plainsBiome(foreMap, backMap, width, height, x, xHeight, random)
128 Biome.DESERT -> desertBiome(foreMap, backMap, width, height, x, xHeight, random)
132 fillWater(foreMap, width, height, height / 2)
134 return Pair(foreMap, backMap)